summaryrefslogtreecommitdiffhomepage
path: root/nGenEx
diff options
context:
space:
mode:
authorFWoltermann@gmail.com <FWoltermann@gmail.com@076cb2c4-205e-83fd-5cf3-1be9aa105544>2011-12-08 14:53:40 +0000
committerFWoltermann@gmail.com <FWoltermann@gmail.com@076cb2c4-205e-83fd-5cf3-1be9aa105544>2011-12-08 14:53:40 +0000
commite33e19d0587146859d48a134ec9fd94e7b7ba5cd (patch)
tree69d048c8801858d2756ab3a487090a7a1b74bf14 /nGenEx
downloadstarshatter-e33e19d0587146859d48a134ec9fd94e7b7ba5cd.zip
starshatter-e33e19d0587146859d48a134ec9fd94e7b7ba5cd.tar.gz
starshatter-e33e19d0587146859d48a134ec9fd94e7b7ba5cd.tar.bz2
Initial upload
Diffstat (limited to 'nGenEx')
-rw-r--r--nGenEx/ActiveWindow.cpp1001
-rw-r--r--nGenEx/ActiveWindow.h325
-rw-r--r--nGenEx/Archive.cpp587
-rw-r--r--nGenEx/Archive.h90
-rw-r--r--nGenEx/AviFile.cpp178
-rw-r--r--nGenEx/AviFile.h63
-rw-r--r--nGenEx/Bitmap.cpp1618
-rw-r--r--nGenEx/Bitmap.h124
-rw-r--r--nGenEx/Bmp.cpp251
-rw-r--r--nGenEx/Bmp.h76
-rw-r--r--nGenEx/Bolt.cpp176
-rw-r--r--nGenEx/Bolt.h65
-rw-r--r--nGenEx/Button.cpp687
-rw-r--r--nGenEx/Button.h121
-rw-r--r--nGenEx/Camera.cpp212
-rw-r--r--nGenEx/Camera.h61
-rw-r--r--nGenEx/CameraView.cpp805
-rw-r--r--nGenEx/CameraView.h114
-rw-r--r--nGenEx/Color.cpp682
-rw-r--r--nGenEx/Color.h295
-rw-r--r--nGenEx/ComboBox.cpp434
-rw-r--r--nGenEx/ComboBox.h105
-rw-r--r--nGenEx/ComboList.cpp434
-rw-r--r--nGenEx/ComboList.h92
-rw-r--r--nGenEx/ContentBundle.cpp137
-rw-r--r--nGenEx/ContentBundle.h48
-rw-r--r--nGenEx/D3DXImage.cpp222
-rw-r--r--nGenEx/D3DXImage.h43
-rw-r--r--nGenEx/DataLoader.cpp1013
-rw-r--r--nGenEx/DataLoader.h86
-rw-r--r--nGenEx/Director.h44
-rw-r--r--nGenEx/EditBox.cpp452
-rw-r--r--nGenEx/EditBox.h92
-rw-r--r--nGenEx/Encrypt.cpp171
-rw-r--r--nGenEx/Encrypt.h38
-rw-r--r--nGenEx/EventDispatch.cpp275
-rw-r--r--nGenEx/EventDispatch.h62
-rw-r--r--nGenEx/EventTarget.h59
-rw-r--r--nGenEx/FadeView.cpp145
-rw-r--r--nGenEx/FadeView.h56
-rw-r--r--nGenEx/Fix.cpp24
-rw-r--r--nGenEx/Fix.h180
-rw-r--r--nGenEx/Font.cpp1232
-rw-r--r--nGenEx/Font.h143
-rw-r--r--nGenEx/FontMgr.cpp58
-rw-r--r--nGenEx/FontMgr.h50
-rw-r--r--nGenEx/FormDef.cpp1269
-rw-r--r--nGenEx/FormDef.h332
-rw-r--r--nGenEx/FormWindow.cpp809
-rw-r--r--nGenEx/FormWindow.h77
-rw-r--r--nGenEx/FormatUtil.cpp337
-rw-r--r--nGenEx/FormatUtil.h47
-rw-r--r--nGenEx/Game.cpp1574
-rw-r--r--nGenEx/Game.h220
-rw-r--r--nGenEx/Geometry.cpp698
-rw-r--r--nGenEx/Geometry.h302
-rw-r--r--nGenEx/Graphic.cpp170
-rw-r--r--nGenEx/Graphic.h139
-rw-r--r--nGenEx/IA3D.H128
-rw-r--r--nGenEx/ImageBox.cpp298
-rw-r--r--nGenEx/ImageBox.h71
-rw-r--r--nGenEx/ImgView.cpp79
-rw-r--r--nGenEx/ImgView.h51
-rw-r--r--nGenEx/Joystick.cpp914
-rw-r--r--nGenEx/Joystick.h82
-rw-r--r--nGenEx/Keyboard.cpp217
-rw-r--r--nGenEx/Keyboard.h74
-rw-r--r--nGenEx/Layout.cpp246
-rw-r--r--nGenEx/Layout.h66
-rw-r--r--nGenEx/Light.cpp72
-rw-r--r--nGenEx/Light.h96
-rw-r--r--nGenEx/ListBox.cpp1333
-rw-r--r--nGenEx/ListBox.h170
-rw-r--r--nGenEx/Locale.cpp237
-rw-r--r--nGenEx/Locale.h53
-rw-r--r--nGenEx/MCIWave.cpp155
-rw-r--r--nGenEx/MCIWave.h25
-rw-r--r--nGenEx/MachineInfo.cpp795
-rw-r--r--nGenEx/MachineInfo.h42
-rw-r--r--nGenEx/Menu.cpp143
-rw-r--r--nGenEx/Menu.h124
-rw-r--r--nGenEx/MotionController.h231
-rw-r--r--nGenEx/Mouse.cpp192
-rw-r--r--nGenEx/Mouse.h75
-rw-r--r--nGenEx/MouseController.cpp247
-rw-r--r--nGenEx/MouseController.h70
-rw-r--r--nGenEx/MultiController.cpp130
-rw-r--r--nGenEx/MultiController.h68
-rw-r--r--nGenEx/PCX.CPP516
-rw-r--r--nGenEx/ParseUtil.cpp481
-rw-r--r--nGenEx/ParseUtil.h52
-rw-r--r--nGenEx/Particles.cpp264
-rw-r--r--nGenEx/Particles.h86
-rw-r--r--nGenEx/Pcx.h68
-rw-r--r--nGenEx/Physical.cpp775
-rw-r--r--nGenEx/Physical.h205
-rw-r--r--nGenEx/PngImage.cpp246
-rw-r--r--nGenEx/PngImage.h44
-rw-r--r--nGenEx/Polygon.cpp745
-rw-r--r--nGenEx/Polygon.h159
-rw-r--r--nGenEx/Projector.cpp470
-rw-r--r--nGenEx/Projector.h106
-rw-r--r--nGenEx/Random.cpp148
-rw-r--r--nGenEx/Random.h35
-rw-r--r--nGenEx/Res.cpp28
-rw-r--r--nGenEx/Res.h37
-rw-r--r--nGenEx/Resource.h21
-rw-r--r--nGenEx/RichTextBox.cpp454
-rw-r--r--nGenEx/RichTextBox.h65
-rw-r--r--nGenEx/Scene.cpp261
-rw-r--r--nGenEx/Scene.h76
-rw-r--r--nGenEx/Screen.cpp161
-rw-r--r--nGenEx/Screen.h65
-rw-r--r--nGenEx/ScrollWindow.cpp632
-rw-r--r--nGenEx/ScrollWindow.h132
-rw-r--r--nGenEx/Sha1.cpp589
-rw-r--r--nGenEx/Sha1.h89
-rw-r--r--nGenEx/Shadow.cpp176
-rw-r--r--nGenEx/Shadow.h70
-rw-r--r--nGenEx/Skin.cpp175
-rw-r--r--nGenEx/Skin.h92
-rw-r--r--nGenEx/Slider.cpp557
-rw-r--r--nGenEx/Slider.h107
-rw-r--r--nGenEx/Solid.cpp2481
-rw-r--r--nGenEx/Solid.h313
-rw-r--r--nGenEx/Sound.cpp284
-rw-r--r--nGenEx/Sound.h150
-rw-r--r--nGenEx/SoundCard.cpp103
-rw-r--r--nGenEx/SoundCard.h76
-rw-r--r--nGenEx/SoundD3D.cpp1295
-rw-r--r--nGenEx/SoundD3D.h162
-rw-r--r--nGenEx/Sprite.cpp380
-rw-r--r--nGenEx/Sprite.h90
-rw-r--r--nGenEx/TexCubeDX9.cpp120
-rw-r--r--nGenEx/TexCubeDX9.h49
-rw-r--r--nGenEx/TexDX9.cpp418
-rw-r--r--nGenEx/TexDX9.h79
-rw-r--r--nGenEx/TimeSnap.h26
-rw-r--r--nGenEx/Types.h66
-rw-r--r--nGenEx/Universe.h32
-rw-r--r--nGenEx/Video.cpp65
-rw-r--r--nGenEx/Video.h242
-rw-r--r--nGenEx/VideoDX9.cpp3675
-rw-r--r--nGenEx/VideoDX9.h195
-rw-r--r--nGenEx/VideoDX9Enum.cpp1057
-rw-r--r--nGenEx/VideoDX9Enum.h185
-rw-r--r--nGenEx/VideoDX9VertexBuffer.cpp281
-rw-r--r--nGenEx/VideoDX9VertexBuffer.h79
-rw-r--r--nGenEx/VideoFactory.cpp71
-rw-r--r--nGenEx/VideoFactory.h42
-rw-r--r--nGenEx/VideoSettings.cpp268
-rw-r--r--nGenEx/VideoSettings.h131
-rw-r--r--nGenEx/View.h52
-rw-r--r--nGenEx/Water.cpp295
-rw-r--r--nGenEx/Water.h54
-rw-r--r--nGenEx/Wave.h52
-rw-r--r--nGenEx/WebBrowser.cpp133
-rw-r--r--nGenEx/WebBrowser.h46
-rw-r--r--nGenEx/Window.cpp936
-rw-r--r--nGenEx/Window.h104
-rw-r--r--nGenEx/nGenEx.dsp800
-rw-r--r--nGenEx/nGenEx.dsw29
-rw-r--r--nGenEx/nGenEx.vcxproj310
-rw-r--r--nGenEx/nGenEx.vcxproj.filters550
-rw-r--r--nGenEx/nGenEx.vcxproj.user3
165 files changed, 49650 insertions, 0 deletions
diff --git a/nGenEx/ActiveWindow.cpp b/nGenEx/ActiveWindow.cpp
new file mode 100644
index 0000000..4eebfa6
--- /dev/null
+++ b/nGenEx/ActiveWindow.cpp
@@ -0,0 +1,1001 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: ActiveWindow.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Window class
+*/
+
+#include "MemDebug.h"
+#include "ActiveWindow.h"
+#include "EventDispatch.h"
+#include "Color.h"
+#include "Bitmap.h"
+#include "Font.h"
+#include "FontMgr.h"
+#include "Layout.h"
+#include "Polygon.h"
+#include "Screen.h"
+#include "View.h"
+#include "Video.h"
+
+// +--------------------------------------------------------------------+
+
+Font* ActiveWindow::sys_font = 0;
+Color ActiveWindow::sys_back_color = Color(128,128,128);
+Color ActiveWindow::sys_fore_color = Color( 0, 0, 0);
+
+void ActiveWindow::SetSystemFont(Font* f) { sys_font = f; }
+void ActiveWindow::SetSystemBackColor(Color c) { sys_back_color = c; }
+void ActiveWindow::SetSystemForeColor(Color c) { sys_fore_color = c; }
+
+// +--------------------------------------------------------------------+
+
+ActiveWindow::ActiveWindow(Screen* screen, int ax, int ay, int aw, int ah,
+ DWORD aid, DWORD s, ActiveWindow* pParent)
+ : Window(screen, ax, ay, aw, ah), id(aid), style(s), focus(false), enabled(true),
+ text_align(DT_CENTER), single_line(false), alpha(1),
+ texture(0), back_color(sys_back_color), fore_color(sys_fore_color),
+ parent(pParent), form(0), transparent(false), topmost(true),
+ layout(0), rows(1), cols(1), polys(0), vset(0), mtl(0),
+ fixed_width(0), fixed_height(0), hide_partial(true)
+{
+ ZeroMemory(tab, sizeof(tab));
+
+ font = sys_font;
+
+ if (parent) {
+ parent->AddChild(this);
+ }
+ else {
+ screen->AddWindow(this);
+ }
+
+ shown = false;
+ Show();
+
+ char buf[32];
+ sprintf(buf, "ActiveWindow %d", id);
+ desc = buf;
+}
+
+// +--------------------------------------------------------------------+
+
+ActiveWindow::~ActiveWindow()
+{
+ if (layout) delete layout;
+
+ screen->DelWindow(this);
+ Hide();
+ clients.destroy();
+ children.destroy();
+
+ if (polys) delete [] polys;
+ if (vset) delete vset;
+ if (mtl) delete mtl;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ActiveWindow::Show()
+{
+ EventDispatch* dispatch = EventDispatch::GetInstance();
+ if (dispatch)
+ dispatch->Register(this);
+
+ ListIter<View> v_iter = view_list;
+ while (++v_iter) {
+ View* view = v_iter.value();
+ view->OnShow();
+ }
+
+ ListIter<ActiveWindow> c_iter = children;
+ while (++c_iter) {
+ ActiveWindow* child = c_iter.value();
+ child->Show();
+ }
+
+ shown = true;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ActiveWindow::Hide()
+{
+ EventDispatch* dispatch = EventDispatch::GetInstance();
+ if (dispatch) {
+ dispatch->Unregister(this);
+ focus = false;
+ }
+
+ ListIter<View> v_iter = view_list;
+ while (++v_iter) {
+ View* view = v_iter.value();
+ view->OnHide();
+ }
+
+ ListIter<ActiveWindow> c_iter = children;
+ while (++c_iter) {
+ ActiveWindow* child = c_iter.value();
+ child->Hide();
+ }
+
+ shown = false;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ActiveWindow::MoveTo(const Rect& r)
+{
+ if (rect.x == r.x &&
+ rect.y == r.y &&
+ rect.w == r.w &&
+ rect.h == r.h)
+ return;
+
+ rect = r;
+ CalcGrid();
+
+ ListIter<View> v = view_list;
+ while (++v)
+ v->OnWindowMove();
+
+ if (layout)
+ layout->DoLayout(this);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ActiveWindow::AddChild(ActiveWindow* child)
+{
+ if (child)
+ children.append(child);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ActiveWindow::DoLayout()
+{
+ if (layout)
+ layout->DoLayout(this);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ActiveWindow::UseLayout(const ArrayList& min_x,
+ const ArrayList& min_y,
+ const FloatList& weight_x,
+ const FloatList& weight_y)
+{
+ if (!layout)
+ layout = new(__FILE__,__LINE__) Layout;
+
+ if (layout)
+ layout->SetConstraints(min_x, min_y, weight_x, weight_y);
+}
+
+void
+ActiveWindow::UseLayout(const FloatList& min_x,
+ const FloatList& min_y,
+ const FloatList& weight_x,
+ const FloatList& weight_y)
+{
+ if (!layout)
+ layout = new(__FILE__,__LINE__) Layout;
+
+ if (layout)
+ layout->SetConstraints(min_x, min_y, weight_x, weight_y);
+}
+
+void
+ActiveWindow::UseLayout(int nrows,
+ int ncols,
+ int* min_x,
+ int* min_y,
+ float* weight_x,
+ float* weight_y)
+{
+ if (!layout)
+ layout = new(__FILE__,__LINE__) Layout;
+
+ if (layout)
+ layout->SetConstraints(nrows, ncols, min_x, min_y, weight_x, weight_y);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ActiveWindow::Paint()
+{
+ Draw();
+}
+
+// +--------------------------------------------------------------------+
+
+Color
+ActiveWindow::ShadeColor(Color c, double shade)
+{
+ int ishade = (int) (shade * Color::SHADE_LEVELS);
+ return c.ShadeColor(ishade);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ActiveWindow::Draw()
+{
+ int w = rect.w;
+ int h = rect.h;
+
+ if (w < 1 || h < 1 || !shown)
+ return;
+
+ float old_alpha = alpha;
+
+ if (!enabled)
+ SetAlpha(0.5);
+
+ if (!transparent) {
+ if (texture && texture->Width()) {
+ DrawTextureGrid();
+ }
+ else {
+ FillRect(0, 0, w, h, ShadeColor(back_color, 1.0));
+ }
+ }
+
+ if (enabled && view_list.size()) {
+ ListIter<View> v = view_list;
+ while (++v)
+ v->Refresh();
+ }
+
+ if (!transparent) {
+ DrawStyleRect(0, 0, w, h, style);
+ }
+
+ // draw text here:
+ DrawTabbedText();
+
+ if (!enabled)
+ SetAlpha(old_alpha);
+
+ // update children windows:
+ ListIter<ActiveWindow> iter = children;
+ while (++iter) {
+ ActiveWindow* child = iter.value();
+ child->Draw();
+ }
+}
+
+void
+ActiveWindow::CalcGrid()
+{
+ if (polys) delete [] polys;
+ if (vset) delete vset;
+ if (mtl) delete mtl;
+
+ rows = 1;
+ cols = 1;
+
+ if (!texture || texture->Width() < 1)
+ return;
+
+ if (margins.left > 0) cols++;
+ if (margins.right > 0) cols++;
+ if (margins.top > 0) rows++;
+ if (margins.bottom > 0) rows++;
+
+ int npolys = rows*cols;
+ int nverts = (rows+1) * (cols+1);
+
+ if (style & WIN_FRAME_ONLY && npolys == 9)
+ npolys = 8; // skip the center poly
+
+ if (npolys > 0) {
+ int i, j;
+ int x_offsets[4];
+ int y_offsets[4];
+ float u_offsets[4];
+ float v_offsets[4];
+
+ x_offsets[0] = 0;
+ x_offsets[1] = margins.left ? margins.left : rect.w - margins.right;
+ x_offsets[2] = cols==2 ? rect.w : rect.w - margins.right;
+ x_offsets[3] = rect.w;
+
+ y_offsets[0] = 0;
+ y_offsets[1] = margins.top ? margins.top : rect.h - margins.bottom;
+ y_offsets[2] = rows==2 ? rect.h : rect.h - margins.bottom;
+ y_offsets[3] = rect.h;
+
+ float tex_w = (float) texture->Width();
+ float tex_h = (float) texture->Height();
+
+ if (tex_w > rect.w) tex_w = (float) rect.w;
+ if (tex_h > rect.h) tex_h = (float) rect.h;
+
+ u_offsets[0] = 0.0f;
+ u_offsets[1] = margins.left ? (float) margins.left : tex_w - (float) margins.right;
+ u_offsets[2] = cols==2 ? tex_w : tex_w - (float) margins.right;
+ u_offsets[3] = tex_w;
+
+ v_offsets[0] = 0.0f;
+ v_offsets[1] = margins.top ? (float) margins.top : tex_h - (float) margins.bottom;
+ v_offsets[2] = rows==2 ? tex_h : tex_h - (float) margins.bottom;
+ v_offsets[3] = tex_h;
+
+ tex_w = (float) texture->Width();
+ tex_h = (float) texture->Height();
+
+ vset = new(__FILE__,__LINE__) VertexSet(nverts);
+
+ int v = 0;
+
+ Color c = Color::White;
+ c.SetAlpha((BYTE) (alpha*255));
+
+ vset->space = VertexSet::SCREEN_SPACE;
+
+ for (i = 0; i <= rows; i++) {
+ for (j = 0; j <= cols; j++) {
+ vset->diffuse[v] = c.Value();
+
+ vset->s_loc[v].x = (float) (rect.x + x_offsets[j]) - 0.5f;
+ vset->s_loc[v].y = (float) (rect.y + y_offsets[i]) - 0.5f;
+ vset->s_loc[v].z = 0.0f;
+ vset->rw[v] = 1.0f;
+
+ vset->tu[v] = u_offsets[j] / tex_w;
+ vset->tv[v] = v_offsets[i] / tex_h;
+
+ v++;
+ }
+ }
+
+ mtl = new(__FILE__,__LINE__) Material;
+ mtl->tex_diffuse = texture;
+
+ polys = new(__FILE__,__LINE__) Poly[npolys];
+
+ Poly* p = polys;
+
+ ZeroMemory(polys, npolys*sizeof(Poly));
+
+ for (i = 0; i < rows; i++) {
+ for (j = 0; j < cols; j++) {
+ if (style & WIN_FRAME_ONLY) {
+ if (i == 1 && j == 1)
+ continue;
+ }
+
+ p->nverts = 4;
+ p->vertex_set = vset;
+ p->material = mtl;
+
+ p->verts[0] = (i+0)*(cols+1) + j;
+ p->verts[1] = (i+0)*(cols+1) + j + 1;
+ p->verts[2] = (i+1)*(cols+1) + j + 1;
+ p->verts[3] = (i+1)*(cols+1) + j;
+
+ p++;
+ }
+ }
+ }
+}
+
+void
+ActiveWindow::DrawTextureGrid()
+{
+ int npolys = rows*cols;
+
+ if (style & WIN_FRAME_ONLY && npolys == 9)
+ npolys = 8; // skip the center poly
+
+ if (mtl) {
+ mtl->tex_diffuse = texture;
+ }
+
+ int blend = Video::BLEND_SOLID;
+
+ if (alpha < 1)
+ blend = Video::BLEND_ALPHA;
+
+ Video* video = screen->GetVideo();
+ video->SetRenderState(Video::TEXTURE_WRAP, 0);
+ video->DrawScreenPolys(npolys, polys, blend);
+ video->SetRenderState(Video::TEXTURE_WRAP, 1);
+}
+
+void
+ActiveWindow::DrawStyleRect(const Rect& r, int style)
+{
+ DrawStyleRect(r.x, r.y, r.x+r.w, r.y+r.h, style);
+}
+
+void
+ActiveWindow::DrawStyleRect(int x1, int y1, int x2, int y2, int style)
+{
+ if (style & WIN_THIN_FRAME) {
+ DrawRect(x1,y1,x2-1,y2-1, ShadeColor(fore_color, 1.0));
+ }
+ else if (style & WIN_THICK_FRAME) {
+ DrawRect(x1+0,y1+0,x2-1,y2-1, ShadeColor(fore_color, 1.0));
+ DrawRect(x1+1,y1+1,x2-2,y2-2, ShadeColor(fore_color, 1.0));
+ DrawRect(x1+2,y1+2,x2-3,y2-3, ShadeColor(fore_color, 1.0));
+ }
+ else {
+ // draw bevel:
+ if ((style & WIN_RAISED_FRAME) && (style & WIN_SUNK_FRAME)) {
+ Color c = ShadeColor(back_color, 1.6); // full highlight
+ DrawLine(x1, y1, x2-1, y1, c);
+ DrawLine(x1, y1, x1, y2-1, c);
+
+ c = ShadeColor(back_color, 1.3); // soft highlight
+ DrawLine(x1+1,y1+1, x2-2, y1+1, c);
+ DrawLine(x1+1,y1+1, x1+1, y2-2, c);
+
+ c = ShadeColor(back_color, 0.6); // soft shadow
+ DrawLine(x2-2,y1+1, x2-2,y2-1, c);
+ DrawLine(x1+1,y2-2, x2-1,y2-2, c);
+
+ c = ShadeColor(back_color, 0.3); // full shadow
+ DrawLine(x2-1,y1, x2-1,y2, c);
+ DrawLine(x1 ,y2-1, x2,y2-1, c);
+
+ DrawRect(x1+4,y1+4, x2-5,y2-5, ShadeColor(back_color, 0.6)); // soft shadow
+ DrawRect(x1+5,y1+5, x2-6,y2-6, ShadeColor(back_color, 0.3)); // full shadow
+ DrawLine(x1+5,y2-6, x2-5,y2-6, ShadeColor(back_color, 1.3)); // soft highlight (bottom)
+ DrawLine(x2-6,y1+5, x2-6,y2-6, ShadeColor(back_color, 1.3)); // soft highlight (side)
+ DrawLine(x1+4,y2-5, x2-4,y2-5, ShadeColor(back_color, 1.6)); // soft highlight (bottom)
+ DrawLine(x2-5,y1+4, x2-5,y2-5, ShadeColor(back_color, 1.6)); // soft highlight (side)
+ }
+
+ else if (style & WIN_RAISED_FRAME) {
+ Color c = ShadeColor(back_color, 1.6); // full highlight
+ DrawLine(x1, y1, x2-1, y1, c);
+ DrawLine(x1, y1, x1, y2-1, c);
+
+ c = ShadeColor(back_color, 1.3); // soft highlight
+ DrawLine(x1+1,y1+1, x2-2, y1+1, c);
+ DrawLine(x1+1,y1+1, x1+1, y2-2, c);
+
+ c = ShadeColor(back_color, 0.6); // soft shadow
+ DrawLine(x2-2,y1+1, x2-2,y2-1, c);
+ DrawLine(x1+1,y2-2, x2-1,y2-2, c);
+
+ c = ShadeColor(back_color, 0.3); // full shadow
+ DrawLine(x2-1,y1, x2-1,y2, c);
+ DrawLine(x1 ,y2-1, x2,y2-1, c);
+ }
+
+ else if (style & WIN_SUNK_FRAME) {
+ Color c = ShadeColor(back_color, 0.3); // full shadow
+ DrawLine(x1+1,y1+1, x1+1, y2, c);
+ DrawLine(x1+1,y1+1, x2, y1+1, c);
+
+ c = ShadeColor(back_color, 0.6); // soft shadow
+ DrawLine(x1, y1, x1, y2, c);
+ DrawLine(x1, y1, x2, y1, c);
+
+ c = ShadeColor(back_color, 1.3); // soft highlight
+ DrawLine(x2-2,y1+1, x2-2,y2-1, c);
+ DrawLine(x1+1,y2-2, x2-1,y2-2, c);
+
+ c = ShadeColor(back_color, 1.6); // full highlight
+ DrawLine(x2-1,y1+1, x2-1,y2, c);
+ DrawLine(x1 ,y2-1, x2,y2-1, c);
+ }
+
+ // draw frame:
+ if (style & WIN_BLACK_FRAME)
+ DrawRect(x1,y1,x2-1,y2-1, ShadeColor(Color::Black, 1.0));
+ else if (style & WIN_WHITE_FRAME)
+ DrawRect(x1,y1,x2-1,y2-1, ShadeColor(Color::White, 1.0));
+ }
+}
+
+void
+ActiveWindow::DrawTabbedText()
+{
+ if (shown && font && text.length()) {
+ Rect label_rect;
+
+ if (text_insets.left) {
+ label_rect.w = rect.w;
+ label_rect.h = rect.h;
+
+ label_rect.Inset(text_insets.left,
+ text_insets.right,
+ text_insets.top,
+ text_insets.bottom);
+ }
+ else {
+ int border_size = 4;
+
+ if (style & WIN_RAISED_FRAME && style & WIN_SUNK_FRAME)
+ border_size = 8;
+
+ label_rect.x = border_size;
+ label_rect.y = border_size;
+ label_rect.w = rect.w - border_size * 2;
+ label_rect.h = rect.h - border_size * 2;
+ }
+
+ font->SetAlpha(alpha);
+
+ // no tabs set:
+ if (tab[0] == 0) {
+ DWORD text_flags = DT_WORDBREAK | text_align;
+
+ if (single_line)
+ text_flags = text_flags | DT_SINGLELINE;
+
+ if (style & WIN_TEXT_SHADOW) {
+ label_rect.x++;
+ label_rect.y++;
+
+ if (transparent) {
+ font->SetColor(back_color);
+ DrawText(text.data(), 0, label_rect, text_flags);
+ }
+
+ else {
+ Color shadow = ShadeColor(back_color, 1.6);
+ font->SetColor(shadow);
+ DrawText(text.data(), 0, label_rect, text_flags);
+ }
+
+ label_rect.x--;
+ label_rect.y--;
+ }
+
+ Color fore = ShadeColor(fore_color, 1);
+ font->SetColor(fore);
+ DrawText(text.data(), 0, label_rect, text_flags);
+ }
+
+ // use tabs:
+ else {
+ }
+
+ font->SetAlpha(1);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ActiveWindow::SetTabStop(int n, int x)
+{
+ if (n >= 0 && n < 10)
+ tab[n] = x;
+}
+
+int
+ActiveWindow::GetTabStop(int n) const
+{
+ if (n >= 0 && n < 10)
+ return tab[n];
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ActiveWindow::SetText(const char* t)
+{
+ if (t && text != t) {
+ int len = strlen(t);
+
+ if (len > 0) {
+ char* buf = new(__FILE__,__LINE__) char[2*len];
+
+ if (buf != 0) {
+ const char* src = t;
+ char* dst = buf;
+
+ while (*src) {
+ if (*src != '\\') {
+ *dst++ = *src++;
+ }
+ else {
+ src++;
+
+ switch (*src) {
+ case 'n': *dst++ = '\n'; break;
+ case 't': *dst++ = '\t'; break;
+ default: *dst++ = *src; break;
+ }
+
+ src++;
+ }
+ }
+
+ *dst = 0;
+
+ if (text != buf) {
+ text = buf;
+ }
+
+ delete [] buf;
+ }
+ }
+ else {
+ text = t;
+ }
+ }
+}
+
+void
+ActiveWindow::SetText(const Text& t)
+{
+ if (t && text != t) {
+ int len = t.length();
+
+ if (len > 0 && t.contains('\\')) {
+ char* buf = new(__FILE__,__LINE__) char[2*len];
+
+ if (buf != 0) {
+ const char* src = t;
+ char* dst = buf;
+
+ while (*src) {
+ if (*src != '\\') {
+ *dst++ = *src++;
+ }
+ else {
+ src++;
+
+ switch (*src) {
+ case 'n': *dst++ = '\n'; break;
+ case 't': *dst++ = '\t'; break;
+ default: *dst++ = *src; break;
+ }
+
+ src++;
+ }
+ }
+
+ *dst = 0;
+
+ if (text != buf) {
+ text = buf;
+ }
+
+ delete [] buf;
+ }
+ }
+ else {
+ text = t;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ActiveWindow::AddText(const char* t)
+{
+ if (t) {
+ text += t;
+ }
+}
+
+void
+ActiveWindow::AddText(const Text& t)
+{
+ if (t) {
+ text += t;
+ }
+}
+
+void
+ActiveWindow::SetTextAlign(DWORD a)
+{
+ if (a == DT_LEFT || a == DT_RIGHT || a == DT_CENTER)
+ text_align = a;
+}
+
+void
+ActiveWindow::SetMargins(const Insets& m)
+{
+ margins = m;
+ CalcGrid();
+}
+
+void
+ActiveWindow::SetTextInsets(const Insets& t)
+{
+ text_insets = t;
+}
+
+void
+ActiveWindow::SetCellInsets(const Insets& c)
+{
+ cell_insets = c;
+}
+
+void
+ActiveWindow::SetCells(int cx, int cy, int cw, int ch)
+{
+ cells.x = cx;
+ cells.y = cy;
+ cells.w = cw;
+ cells.h = ch;
+
+ if (cells.w < 1)
+ cells.w = 1;
+
+ if (cells.h < 1)
+ cells.h = 1;
+}
+
+void
+ActiveWindow::SetAlpha(double a)
+{
+ if (alpha != a) {
+ alpha = (float) a;
+
+ Color c = Color::White;
+ c.SetAlpha((BYTE) (alpha*255));
+
+ if (vset && vset->nverts) {
+ for (int i = 0; i < vset->nverts; i++) {
+ vset->diffuse[i] = c.Value();
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ActiveWindow::DrawText(const char* txt, int count, Rect& txt_rect, DWORD flags)
+{
+ if (font) {
+ font->SetAlpha(alpha);
+ Window::DrawText(txt, count, txt_rect, flags);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ActiveWindow::RegisterClient(int eid, ActiveWindow* client, PFVAWE callback)
+{
+ AWMap* map = new(__FILE__,__LINE__) AWMap(eid, client, callback);
+
+ if (map != 0)
+ clients.append(map);
+}
+
+void
+ActiveWindow::UnregisterClient(int eid, ActiveWindow* client)
+{
+ AWMap test(eid, client, 0);
+ int index = clients.index(&test);
+
+ if (index >= 0)
+ delete clients.removeIndex(index);
+}
+
+void
+ActiveWindow::ClientEvent(int eid, int x, int y)
+{
+ event.window = this;
+ event.eid = eid;
+ event.x = x;
+ event.y = y;
+
+ ListIter<AWMap> map = clients;
+ while (++map) {
+ if (map->eid == eid)
+ map->func(map->client, &event);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+int ActiveWindow::OnMouseEnter(int x, int y)
+{
+ ClientEvent(EID_MOUSE_ENTER, x, y);
+ return 0;
+}
+
+int ActiveWindow::OnMouseExit(int x, int y)
+{
+ ClientEvent(EID_MOUSE_EXIT, x, y);
+ return 0;
+}
+
+int ActiveWindow::OnMouseMove(int x, int y)
+{
+ ClientEvent(EID_MOUSE_MOVE, x, y);
+ return 0;
+}
+
+int ActiveWindow::OnMouseWheel(int wheel)
+{
+ ClientEvent(EID_MOUSE_WHEEL, wheel, 0);
+ return 0;
+}
+
+int ActiveWindow::OnLButtonDown(int x, int y)
+{
+ ClientEvent(EID_LBUTTON_DOWN, x, y);
+ return 0;
+}
+
+int ActiveWindow::OnLButtonUp(int x, int y)
+{
+ ClientEvent(EID_LBUTTON_UP, x, y);
+ return 0;
+}
+
+int ActiveWindow::OnClick()
+{
+ ClientEvent(EID_CLICK);
+ return 0;
+}
+
+int ActiveWindow::OnSelect()
+{
+ ClientEvent(EID_SELECT);
+ return 0;
+}
+
+int ActiveWindow::OnRButtonDown(int x, int y)
+{
+ ClientEvent(EID_RBUTTON_DOWN, x, y);
+ return 0;
+}
+
+int ActiveWindow::OnRButtonUp(int x, int y)
+{
+ ClientEvent(EID_RBUTTON_UP, x, y);
+ return 0;
+}
+
+int ActiveWindow::OnKeyDown(int vk, int flags)
+{
+ ClientEvent(EID_KEY_DOWN, vk, flags);
+ return 0;
+}
+
+int ActiveWindow::OnDragStart(int x, int y)
+{
+ ClientEvent(EID_DRAG_START, x, y);
+ return 0;
+}
+
+int ActiveWindow::OnDragDrop(int x, int y, ActiveWindow* source)
+{
+ ClientEvent(EID_DRAG_DROP, x, y);
+ return 0;
+}
+
+Rect ActiveWindow::TargetRect() const
+{
+ return rect;
+}
+
+// +--------------------------------------------------------------------+
+
+void ActiveWindow::SetFocus()
+{
+ EventDispatch* dispatch = EventDispatch::GetInstance();
+ if (dispatch)
+ dispatch->SetFocus(this);
+
+ focus = true;
+ ClientEvent(EID_SET_FOCUS);
+}
+
+void ActiveWindow::KillFocus()
+{
+ EventDispatch* dispatch = EventDispatch::GetInstance();
+ if (dispatch)
+ dispatch->KillFocus(this);
+
+ focus = false;
+ ClientEvent(EID_KILL_FOCUS);
+}
+
+// +--------------------------------------------------------------------+
+
+ActiveWindow*
+ActiveWindow::GetCapture()
+{
+ EventDispatch* dispatch = EventDispatch::GetInstance();
+ if (dispatch)
+ return (ActiveWindow*) dispatch->GetCapture();
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+ActiveWindow::SetCapture()
+{
+ EventDispatch* dispatch = EventDispatch::GetInstance();
+ if (dispatch)
+ return dispatch->CaptureMouse(this);
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+ActiveWindow::ReleaseCapture()
+{
+ EventDispatch* dispatch = EventDispatch::GetInstance();
+ if (dispatch)
+ return dispatch->ReleaseMouse(this);
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+ActiveWindow::IsFormActive() const
+{
+ if (form)
+ return form->IsTopMost();
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+ActiveWindow*
+ActiveWindow::FindChild(DWORD id)
+{
+ ListIter<ActiveWindow> iter = children;
+ while (++iter) {
+ ActiveWindow* w = iter.value();
+ if (w->GetID() == id)
+ return w;
+
+ ActiveWindow* w2 = w->FindChild(id);
+
+ if (w2)
+ return w2;
+ }
+
+ return 0;
+}
+
+
+// +--------------------------------------------------------------------+
+
+ActiveWindow*
+ActiveWindow::FindChild(int x, int y)
+{
+ ActiveWindow* mouse_tgt = 0;
+
+ ListIter<ActiveWindow> iter = children;
+ while (++iter) {
+ ActiveWindow* test = iter.value();
+ if (test->TargetRect().Contains(x,y))
+ mouse_tgt = test;
+ }
+
+ return mouse_tgt;
+}
diff --git a/nGenEx/ActiveWindow.h b/nGenEx/ActiveWindow.h
new file mode 100644
index 0000000..50707fb
--- /dev/null
+++ b/nGenEx/ActiveWindow.h
@@ -0,0 +1,325 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: ActiveWindow.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Active Window class (a window that knows how to draw itself)
+*/
+
+#ifndef ActiveWindow_h
+#define ActiveWindow_h
+
+#include "Types.h"
+#include "Color.h"
+#include "Geometry.h"
+#include "Bitmap.h"
+#include "Window.h"
+#include "EventTarget.h"
+#include "ArrayList.h"
+#include "List.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+struct Poly;
+struct Material;
+struct VertexSet;
+class Layout;
+
+// +--------------------------------------------------------------------+
+
+enum {
+ WIN_NO_FRAME = 0x0000,
+ WIN_BLACK_FRAME = 0x0001,
+ WIN_WHITE_FRAME = 0x0002,
+ WIN_THIN_FRAME = 0x0004,
+ WIN_THICK_FRAME = 0x0008,
+ WIN_RAISED_FRAME = 0x0010,
+ WIN_SUNK_FRAME = 0x0020,
+ WIN_TEXT_SHADOW = 0x0040,
+ WIN_FRAME_ONLY = 0x0080
+};
+
+enum {
+ EID_CREATE,
+ EID_DESTROY,
+ EID_MOUSE_MOVE,
+ EID_CLICK,
+ EID_SELECT,
+ EID_LBUTTON_DOWN,
+ EID_LBUTTON_UP,
+ EID_RBUTTON_DOWN,
+ EID_RBUTTON_UP,
+ EID_KEY_DOWN,
+ EID_SET_FOCUS,
+ EID_KILL_FOCUS,
+ EID_MOUSE_ENTER,
+ EID_MOUSE_EXIT,
+ EID_MOUSE_WHEEL,
+ EID_DRAG_START,
+ EID_DRAG_DROP,
+
+ EID_USER_1,
+ EID_USER_2,
+ EID_USER_3,
+ EID_USER_4,
+
+ EID_NUM_EVENTS
+};
+
+// +--------------------------------------------------------------------+
+
+class ActiveWindow;
+
+struct AWEvent
+{
+ static const char* TYPENAME() { return "AWEvent"; }
+
+ AWEvent() : window(0), eid(0), x(0), y(0) { }
+ AWEvent(ActiveWindow* w, int e, int ax=0, int ay=0) : window(w), eid(e), x(ax), y(ay) { }
+
+ int operator == (const AWEvent& e) const { return (window == e.window) &&
+ (eid == e.eid) &&
+ (x == e.x) &&
+ (y == e.y); }
+
+ ActiveWindow* window;
+ int eid;
+ int x;
+ int y;
+};
+
+typedef void (*PFVAWE)(ActiveWindow*, AWEvent*);
+
+struct AWMap
+{
+ static const char* TYPENAME() { return "AWMap"; }
+
+ AWMap() : eid(0), client(0), func(0) { }
+ AWMap(int e, ActiveWindow* w, PFVAWE f) : eid(e), client(w), func(f) { }
+
+ int operator == (const AWMap& m) const { return (eid == m.eid) &&
+ (client == m.client); }
+
+ int eid;
+ ActiveWindow* client;
+ PFVAWE func;
+};
+
+// +--------------------------------------------------------------------+
+
+class ActiveWindow : public Window,
+ public EventTarget
+{
+public:
+ static const char* TYPENAME() { return "ActiveWindow"; }
+
+ ActiveWindow(Screen* s, int ax, int ay, int aw, int ah,
+ DWORD id=0, DWORD style=0, ActiveWindow* parent=0);
+ virtual ~ActiveWindow();
+
+ int operator == (const ActiveWindow& w) const { return id == w.id; }
+
+ // Operations:
+ virtual void Paint(); // blt to screen
+ virtual void Draw(); // refresh backing store
+ virtual void Show();
+ virtual void Hide();
+ virtual void MoveTo(const Rect& r);
+ virtual void UseLayout(const ArrayList& min_x,
+ const ArrayList& min_y,
+ const FloatList& weight_x,
+ const FloatList& weight_y);
+ virtual void UseLayout(const FloatList& min_x,
+ const FloatList& min_y,
+ const FloatList& weight_x,
+ const FloatList& weight_y);
+ virtual void UseLayout(int ncols,
+ int nrows,
+ int* min_x,
+ int* min_y,
+ float* weight_x,
+ float* weight_y);
+ virtual void DoLayout();
+
+ // Event Target Interface:
+ virtual int OnMouseMove(int x, int y);
+ virtual int OnLButtonDown(int x, int y);
+ virtual int OnLButtonUp(int x, int y);
+ virtual int OnClick();
+ virtual int OnSelect();
+ virtual int OnRButtonDown(int x, int y);
+ virtual int OnRButtonUp(int x, int y);
+ virtual int OnMouseEnter(int x, int y);
+ virtual int OnMouseExit(int x, int y);
+ virtual int OnMouseWheel(int wheel);
+
+ virtual int OnKeyDown(int vk, int flags);
+
+ virtual const char* GetDescription() const { return desc; }
+
+ // pseudo-events:
+ virtual int OnDragStart(int x, int y);
+ virtual int OnDragDrop(int x, int y, ActiveWindow* source);
+
+ virtual ActiveWindow* FindControl(int x, int y) { return 0; }
+ virtual Rect TargetRect() const;
+
+ virtual ActiveWindow* GetCapture();
+ virtual int SetCapture();
+ virtual int ReleaseCapture();
+
+ // Property accessors:
+ virtual void SetFocus();
+ virtual void KillFocus();
+ virtual bool HasFocus() const { return focus; }
+
+ void SetEnabled(bool e=true) { enabled = e; }
+ bool IsEnabled() const { return enabled; }
+ bool IsVisible() const { return shown; }
+
+ DWORD GetID() const { return id; }
+ void SetStyle(DWORD s) { style = s; }
+ DWORD GetStyle() const { return style; }
+
+ void SetText(const char* t);
+ void SetText(const Text& t);
+ void AddText(const char* t);
+ void AddText(const Text& t);
+ const Text& GetText() const { return text; }
+
+ void SetAltText(const char* t) { alt_text = t; }
+ void SetAltText(const Text& t) { alt_text = t; }
+ const Text& GetAltText() const { return alt_text; }
+
+ void SetTexture(Bitmap* bmp) { texture = bmp; }
+ Bitmap* GetTexture() { return texture; }
+ void SetMargins(const Insets& m);
+ Insets& GetMargins() { return margins; }
+ void SetTextInsets(const Insets& t);
+ Insets& GetTextInsets() { return text_insets; }
+
+ List<ActiveWindow>& GetChildren() { return children; }
+ void SetCellInsets(const Insets& c);
+ Insets& GetCellInsets() { return cell_insets; }
+ void SetCells(int cx, int cy, int cw=1, int ch=1);
+ void SetCells(const Rect& r) { cells = r; }
+ Rect& GetCells() { return cells; }
+ void SetFixedWidth(int w) { fixed_width = w; }
+ int GetFixedWidth() const { return fixed_width; }
+ void SetFixedHeight(int h) { fixed_height = h; }
+ int GetFixedHeight() const { return fixed_height;}
+
+ void SetAlpha(double a);
+ double GetAlpha() const { return alpha; }
+ void SetBackColor(Color c) { back_color = c; }
+ Color GetBackColor() const { return back_color; }
+ void SetBaseColor(Color c) { base_color = c; }
+ Color GetBaseColor() const { return base_color; }
+ void SetForeColor(Color c) { fore_color = c; }
+ Color GetForeColor() const { return fore_color; }
+ void SetSingleLine(bool a) { single_line = a; }
+ bool GetSingleLine() const { return single_line; }
+ void SetTextAlign(DWORD a);
+ DWORD GetTextAlign() const { return text_align; }
+ void SetTransparent(bool t) { transparent = t; }
+ bool GetTransparent() const { return transparent; }
+ void SetHidePartial(bool a) { hide_partial = a; }
+ bool GetHidePartial() const { return hide_partial;}
+
+ void SetTabStop(int n, int x);
+ int GetTabStop(int n) const;
+
+ void DrawText(const char* txt, int count, Rect& txt_rect, DWORD flags);
+
+ // class properties:
+ static void SetSystemFont(Font* f);
+ static void SetSystemBackColor(Color c);
+ static void SetSystemForeColor(Color c);
+
+ // callback function registration:
+ virtual void RegisterClient(int EID, ActiveWindow* client, PFVAWE callback);
+ virtual void UnregisterClient(int EID, ActiveWindow* client);
+ virtual void ClientEvent(int EID, int x=0, int y=0);
+
+ // form context:
+ virtual ActiveWindow* GetForm() { return form; }
+ virtual void SetForm(ActiveWindow* f) { form = f; }
+ virtual bool IsFormActive() const;
+ virtual bool IsTopMost() const { return topmost; }
+ virtual void SetTopMost(bool t) { topmost = t; }
+
+ virtual ActiveWindow* FindChild(DWORD id);
+ virtual ActiveWindow* FindChild(int x, int y);
+
+protected:
+ virtual Color ShadeColor(Color c, double shade);
+ virtual void AddChild(ActiveWindow* child);
+ virtual void DrawStyleRect(const Rect& r, int style);
+ virtual void DrawStyleRect(int x1, int y1, int x2, int y2, int style);
+ virtual void DrawTabbedText();
+ virtual void DrawTextureGrid();
+ virtual void CalcGrid();
+
+ DWORD id;
+ DWORD style;
+ DWORD text_align;
+ bool single_line;
+ bool focus;
+ bool enabled;
+ bool hide_partial;
+ float alpha;
+ Color back_color;
+ Color base_color;
+ Color fore_color;
+ Text text;
+ Text alt_text;
+ Text desc;
+ Bitmap* texture;
+ Insets margins;
+ Insets text_insets;
+ Insets cell_insets;
+ Rect cells;
+ int fixed_width;
+ int fixed_height;
+ int tab[10];
+
+ ActiveWindow* parent;
+ ActiveWindow* form;
+ bool transparent;
+ bool topmost;
+
+ Layout* layout;
+ List<ActiveWindow> children;
+ List<AWMap> clients;
+ AWEvent event;
+
+ int rows;
+ int cols;
+ Poly* polys;
+ VertexSet* vset;
+ Material* mtl;
+
+ static Font* sys_font;
+ static Color sys_back_color;
+ static Color sys_fore_color;
+};
+
+#define DEF_MAP_CLIENT(cname, fname)\
+ void Map##cname##fname(ActiveWindow* client, AWEvent* event) \
+ { cname* c = (cname*) client; c->fname(event); }
+
+#define REGISTER_CLIENT(eid, ctrl, cname, fname)\
+ if (ctrl) ctrl->RegisterClient(eid, this, Map##cname##fname);
+
+#define UNREGISTER_CLIENT(eid, ctrl, cname)\
+ if (ctrl) ctrl->UnregisterClient(eid, this);
+
+#endif ActiveWindow_h
+
diff --git a/nGenEx/Archive.cpp b/nGenEx/Archive.cpp
new file mode 100644
index 0000000..dfec332
--- /dev/null
+++ b/nGenEx/Archive.cpp
@@ -0,0 +1,587 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Archive.cpp
+ AUTHOR: John DiCamillo
+
+*/
+
+#include "MemDebug.h"
+#include "Types.h"
+#include "Archive.h"
+
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <io.h>
+#include <stdio.h>
+#include <time.h>
+
+#include "zlib.h"
+
+// +--------------------------------------------------------------------+
+
+void Print(const char* fmt, ...);
+
+int verbose = 1;
+int err;
+
+#define CHECK_ERR(err, msg) { \
+ if (err != Z_OK) { \
+ fprintf(stderr, "%s error: %d\n", msg, err); \
+ exit(1); \
+ } \
+}
+
+// +--------------------------------------------------------------------+
+
+DataArchive::DataArchive(const char* name)
+{
+ ZeroMemory(this, sizeof(DataArchive));
+
+ if (name)
+ LoadDatafile(name);
+}
+
+DataArchive::~DataArchive()
+{
+ delete [] block_map;
+ delete [] directory;
+}
+
+// +--------------------------------------------------------------------+
+
+void DataArchive::WriteEntry(int index, BYTE* buf)
+{
+ int f = _open(datafile, _O_RDWR|_O_CREAT|_O_BINARY, _S_IREAD|_S_IWRITE);
+
+ if (f != -1) {
+ header.dir_size_comp = DirBlocks() * BLOCK_SIZE;
+ dirbuf = new(__FILE__,__LINE__) BYTE[header.dir_size_comp];
+
+ if (!dirbuf) {
+ err = Z_MEM_ERROR;
+ }
+
+ else {
+ err = compress(dirbuf, &header.dir_size_comp,
+ (BYTE*) directory, header.nfiles * sizeof(DataEntry));
+ CHECK_ERR(err, "compress");
+
+ header.dir_blocks = Blocks(header.dir_size_comp) * BLOCK_SIZE;
+
+ _lseek(f, 0, SEEK_SET);
+ _write(f, &header, sizeof(DataHeader));
+ _lseek(f, sizeof(DataHeader) + header.dir_offset, SEEK_SET);
+ _write(f, dirbuf, header.dir_blocks);
+
+ delete [] dirbuf;
+ dirbuf = 0;
+ }
+
+ if (buf && directory && directory[index].size_comp) {
+ _lseek(f, sizeof(DataHeader) + directory[index].offset, SEEK_SET);
+ _write(f, buf, directory[index].size_comp);
+ }
+ _close(f);
+ }
+ else
+ perror("WriteEntry");
+}
+
+// +--------------------------------------------------------------------+
+
+DWORD DataArchive::Blocks(DWORD raw_size)
+{
+ int full_blocks = raw_size / BLOCK_SIZE;
+ int part_blocks = (raw_size % BLOCK_SIZE) > 0;
+
+ return full_blocks + part_blocks;
+}
+
+DWORD DataArchive::DirBlocks()
+{
+ DWORD result = Blocks(header.nfiles * sizeof(DataEntry));
+ if (result == 0) result = 1;
+ return result;
+}
+
+DWORD DataArchive::FileBlocks(int index)
+{
+ if (index >= 0 && index < (int) header.nfiles && directory)
+ return Blocks(directory[index].size_comp);
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void DataArchive::CreateBlockMap()
+{
+ delete [] block_map;
+ block_map = 0;
+
+ if (header.nfiles == 0) return;
+
+ DWORD i,j;
+ DWORD dir_usage = header.dir_offset + DirBlocks() * BLOCK_SIZE;
+ DWORD max_usage = dir_usage;
+
+ for (i = 0; i < header.nfiles; i++) {
+ DWORD last_block = directory[i].offset + FileBlocks(i) * BLOCK_SIZE;
+ if (last_block > max_usage)
+ max_usage = last_block;
+ }
+
+ nblocks = max_usage/BLOCK_SIZE;
+ block_map = new(__FILE__,__LINE__) DWORD[nblocks];
+
+ if (!block_map) {
+ nblocks = 0;
+ }
+
+ else {
+ ZeroMemory(block_map, nblocks*sizeof(DWORD));
+
+ DWORD first_block = header.dir_offset/BLOCK_SIZE +
+ (header.dir_offset%BLOCK_SIZE > 0);
+
+ for (j = 0; j < DirBlocks(); j++)
+ block_map[first_block+j] = 1;
+
+ for (i = 0; i < header.nfiles; i++) {
+ DWORD first_block = directory[i].offset/BLOCK_SIZE +
+ (directory[i].offset%BLOCK_SIZE > 0);
+
+ for (j = 0; j < FileBlocks(i); j++)
+ block_map[first_block+j] = i+2;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+int DataArchive::FindDataBlocks(int need)
+{
+ if ((int) (nblocks)-need > 0) {
+ DWORD start;
+ int i;
+
+ for (start = 0; start < nblocks-need; start++) {
+ for (i = 0; block_map[start+i] == 0 && i < need; i++);
+
+ if (i == need) return start*BLOCK_SIZE;
+
+ start += i;
+ }
+ }
+
+ return nblocks*BLOCK_SIZE;
+}
+
+// +--------------------------------------------------------------------+
+
+void DataArchive::LoadDatafile(const char* name)
+{
+ if (!name) return;
+
+ delete [] directory;
+ delete [] block_map;
+
+ ZeroMemory(this, sizeof(DataArchive));
+ strncpy(datafile, name, NAMELEN-1);
+
+ FILE* f = fopen(datafile, "rb");
+ if (f) {
+ fread(&header, sizeof(DataHeader), 1, f);
+
+ if (header.version != VERSION) {
+ Print("ERROR: datafile '%s' invalid version '%d'\n",
+ datafile, header.version);
+ fclose(f);
+ ZeroMemory(&header, sizeof(header));
+ return;
+ }
+
+ DWORD len = DirBlocks() * BLOCK_SIZE;
+ DWORD dirsize = header.nfiles + 64;
+
+ dirbuf = new(__FILE__,__LINE__) BYTE[len];
+ directory = new(__FILE__,__LINE__) DataEntry[dirsize];
+
+ if (!dirbuf || !directory) {
+ err = Z_MEM_ERROR;
+ }
+
+ else {
+ ZeroMemory(directory, sizeof(DataEntry) * dirsize);
+
+ fseek(f, sizeof(DataHeader) + header.dir_offset, SEEK_SET);
+ fread(dirbuf, header.dir_size_comp, 1, f);
+
+ int err = uncompress((BYTE*) directory, &len,
+ dirbuf, header.dir_size_comp);
+ if (err != Z_OK)
+ ZeroMemory(directory, sizeof(DataEntry) * dirsize);
+
+ delete [] dirbuf;
+ dirbuf = 0;
+
+ CreateBlockMap();
+ }
+ }
+ else {
+ Print("Creating Archive '%s'...\n", datafile);
+
+ header.version = VERSION;
+ header.nfiles = 0;
+ header.dir_blocks = 0;
+ header.dir_size_comp = 0;
+ header.dir_offset = 0;
+
+ nblocks = DirBlocks();
+
+ delete [] block_map;
+ block_map = 0;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+int DataArchive::FindEntry(const char* req_name)
+{
+ int entry = -1;
+
+ if (req_name && *req_name && directory) {
+ char path[256];
+ int len = strlen(req_name);
+
+ ZeroMemory(path, sizeof(path));
+
+ for (int c = 0; c < len; c++) {
+ if (req_name[c] == '\\')
+ path[c] = '/';
+ else
+ path[c] = req_name[c];
+ }
+
+ for (DWORD i = 0; i < header.nfiles; i++) {
+ if (!stricmp(directory[i].name, path))
+ return i;
+ }
+ }
+
+ return entry;
+}
+
+// +--------------------------------------------------------------------+
+
+BYTE* DataArchive::CompressEntry(int i)
+{
+ if (directory && i >= 0 && i < (int) header.nfiles) {
+ char* name = directory[i].name;
+
+ FILE* f = fopen(name, "rb");
+
+ if (f) {
+ fseek(f, 0, SEEK_END);
+ DWORD len = ftell(f);
+ fseek(f, 0, SEEK_SET);
+
+ BYTE* buf = new(__FILE__,__LINE__) BYTE[len];
+
+ if (!buf) {
+ err = Z_MEM_ERROR;
+ }
+
+ else {
+ fread(buf, len, 1, f);
+ fclose(f);
+
+ directory[i].size_orig = len;
+
+ directory[i].size_comp = (int) (len * 1.1);
+ BYTE* cbuf = new(__FILE__,__LINE__) BYTE[directory[i].size_comp];
+
+ if (!cbuf) {
+ err = Z_MEM_ERROR;
+ }
+ else {
+ err = compress(cbuf, &directory[i].size_comp, buf, len);
+ CHECK_ERR(err, "compress");
+ }
+
+ delete [] buf;
+ return cbuf;
+ }
+ }
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+int DataArchive::ExpandEntry(int i, BYTE*& buf, bool null_terminate)
+{
+ DWORD len = 0;
+
+ if (directory && i >= 0 && i < (int) header.nfiles) {
+ FILE* f = fopen(datafile, "rb");
+
+ if (f) {
+ DWORD clen = directory[i].size_comp;
+ BYTE* cbuf = new(__FILE__,__LINE__) BYTE[clen];
+
+ if (!cbuf) {
+ err = Z_MEM_ERROR;
+ }
+
+ else {
+ fseek(f, sizeof(DataHeader) + directory[i].offset, SEEK_SET);
+ fread(cbuf, clen, 1, f);
+
+ len = directory[i].size_orig;
+
+ if (null_terminate) {
+ buf = new(__FILE__,__LINE__) BYTE[len+1];
+ if (buf) buf[len] = 0;
+ }
+
+ else {
+ buf = new(__FILE__,__LINE__) BYTE[len];
+ }
+
+ if (!buf) {
+ err = Z_MEM_ERROR;
+ }
+
+ else {
+ err = uncompress(buf, &len, cbuf, clen);
+ if (err != Z_OK) {
+ delete [] buf;
+ buf = 0;
+ }
+ }
+
+ delete [] cbuf;
+ fclose(f);
+ }
+ }
+ }
+
+ return len;
+}
+
+// +--------------------------------------------------------------------+
+
+int DataArchive::InsertEntry(const char* name)
+{
+ if (name && *name) {
+ char path[256];
+ DWORD len = strlen(name);
+
+ ZeroMemory(path, sizeof(path));
+
+ for (DWORD c = 0; c < len; c++) {
+ if (name[c] == '\\')
+ path[c] = '/';
+ else
+ path[c] = name[c];
+ }
+
+ int dirsize = header.nfiles + 64;
+
+ if (directory && dirsize) {
+ for (int i = 0; i < dirsize; i++) {
+ if (directory[i].size_orig == 0) {
+ ZeroMemory(directory[i].name, NAMELEN);
+ strncpy(directory[i].name, path, NAMELEN);
+ directory[i].name[NAMELEN-1] = '\0';
+ directory[i].size_orig = 1;
+
+ return i;
+ }
+ }
+ }
+
+ DataEntry* dir = new(__FILE__,__LINE__) DataEntry[dirsize + 64];
+
+ if (directory && dirsize) {
+ ZeroMemory(dir, (dirsize + 64) * sizeof(DataEntry));
+ CopyMemory(dir, directory, dirsize * sizeof(DataEntry));
+ }
+
+ delete [] directory;
+
+ header.nfiles = dirsize + 64;
+ directory = dir;
+
+ ZeroMemory(directory[dirsize].name, NAMELEN);
+ strncpy(directory[dirsize].name, path, NAMELEN);
+ directory[dirsize].name[NAMELEN-1] = '\0';
+ directory[dirsize].size_orig = 1;
+
+ return dirsize;
+ }
+
+ return -1;
+}
+
+// +--------------------------------------------------------------------+
+
+void DataArchive::RemoveEntry(int index)
+{
+ if (directory && index >= 0 && index < (int) header.nfiles)
+ ZeroMemory(&directory[index], sizeof(DataEntry));
+}
+
+// +--------------------------------------------------------------------+
+
+void DataArchive::Insert(const char* name)
+{
+ DWORD old_blocks = 0, old_offset = 0, new_blocks = 0;
+ DWORD old_dir_blocks = 0, old_dir_offset = 0, new_dir_blocks = 0;
+ int added = 0;
+
+ int index = FindEntry(name);
+
+ if (index < 0) {
+ old_dir_blocks = DirBlocks();
+ old_dir_offset = header.dir_offset;
+
+ index = InsertEntry(name);
+
+ if (index >= (int) header.nfiles) {
+ header.nfiles = index+1;
+ added = 1;
+ }
+
+ new_dir_blocks = DirBlocks();
+
+ if (new_dir_blocks > old_dir_blocks) {
+ header.dir_offset = FindDataBlocks(new_dir_blocks);
+ CreateBlockMap();
+ }
+ }
+ else {
+ old_blocks = FileBlocks(index);
+ old_offset = directory[index].offset;
+ }
+
+ if (index >= 0) {
+ DataEntry& e = directory[index];
+
+ if (verbose) Print(" Inserting: %-16s ", e.name);
+
+ BYTE* buf = CompressEntry(index);
+
+ if (!buf) {
+ // this is (almost) unrecoverable,
+ // so we quit before screwing things up:
+ Print("ERROR: Could not compress %d:%s\n", index, directory[index].name);
+ exit(1);
+ }
+
+ new_blocks = FileBlocks(index);
+
+ // the file is new, or got bigger,
+ // need to find room for the data:
+ if (new_blocks > old_blocks) {
+ directory[index].offset = FindDataBlocks(new_blocks);
+ CreateBlockMap();
+ }
+
+ WriteEntry(index, buf);
+ delete [] buf;
+
+ if (verbose) {
+ int ratio = (int) (100.0 * (double) e.size_comp / (double) e.size_orig);
+ Print("%9d => %9d (%2d%%)\n", e.size_orig, e.size_comp, ratio);
+ }
+ }
+ else if (added)
+ header.nfiles--;
+}
+
+// +--------------------------------------------------------------------+
+
+void DataArchive::Extract(const char* name)
+{
+ int index = FindEntry(name);
+
+ if (!directory || index < 0 || index >= (int) header.nfiles) {
+ Print("Could not extract '%s', not found\n", name);
+ return;
+ }
+
+ BYTE* buf;
+ ExpandEntry(index, buf);
+
+ FILE* f = fopen(directory[index].name, "wb");
+ if (f) {
+ fwrite(buf, directory[index].size_orig, 1, f);
+ fclose(f);
+ }
+ else
+ Print("Could not extract '%s', could not open file for writing\n", name);
+
+ delete [] buf;
+
+ if (verbose) Print(" Extracted: %s\n", name);
+}
+
+// +--------------------------------------------------------------------+
+
+void DataArchive::Remove(const char* name)
+{
+ int index = FindEntry(name);
+
+ if (!directory || index < 0 || index >= (int) header.nfiles) {
+ Print("Could not remove '%s', not found\n", name);
+ return;
+ }
+
+ RemoveEntry(index);
+ WriteEntry(index, 0);
+
+ if (verbose) Print(" Removed: %s\n", name);
+}
+
+// +--------------------------------------------------------------------+
+
+void DataArchive::List()
+{
+ int total_orig = 0;
+ int total_comp = 0;
+
+ printf("DATAFILE: %s\n", datafile);
+ printf("Files: %d\n", header.nfiles);
+ printf("\n");
+
+ if (directory && header.nfiles) {
+ printf("Index Orig Size Comp Size Ratio Name\n");
+ printf("----- --------- --------- ----- ----------------\n");
+
+ for (DWORD i = 0; i < header.nfiles; i++) {
+ DataEntry& e = directory[i];
+ int ratio = (int) (100.0 * (double) e.size_comp / (double) e.size_orig);
+
+ printf("%5d %9d %9d %2d%% %s\n", i+1, e.size_orig, e.size_comp, ratio, e.name);
+
+ total_orig += e.size_orig;
+ total_comp += e.size_comp;
+ }
+
+ int total_ratio = (int) (100.0 * (double) total_comp / (double) total_orig);
+
+ printf("----- --------- --------- -----\n");
+ printf("TOTAL %9d %9d %2d%%\n\n", total_orig, total_comp, total_ratio);
+ }
+}
+
+
+// +--------------------------------------------------------------------+
+
diff --git a/nGenEx/Archive.h b/nGenEx/Archive.h
new file mode 100644
index 0000000..ad4bd5e
--- /dev/null
+++ b/nGenEx/Archive.h
@@ -0,0 +1,90 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Archive.h
+ AUTHOR: John DiCamillo
+
+*/
+
+#ifndef Archive_h
+#define Archive_h
+
+// +------------------------------------------------------------------+
+
+#define VERSION 0x0010
+#define BLOCK_SIZE 1024
+#define FILE_BLOCK 1024
+#define NAMELEN 64
+
+// +------------------------------------------------------------------+
+
+struct DataHeader
+{
+ static const char* TYPENAME() { return "DataHeader"; }
+
+ DWORD version;
+ DWORD nfiles;
+ DWORD dir_blocks;
+ DWORD dir_size_comp;
+ DWORD dir_offset;
+};
+
+struct DataEntry
+{
+ static const char* TYPENAME() { return "DataEntry"; }
+
+ char name[NAMELEN];
+ DWORD size_orig;
+ DWORD size_comp;
+ DWORD offset;
+};
+
+class DataArchive
+{
+public:
+ static const char* TYPENAME() { return "DataArchive"; }
+
+ // ctor:
+ DataArchive(const char* name = 0);
+ ~DataArchive();
+
+ // operations:
+ void LoadDatafile(const char* name);
+ void Insert(const char* name);
+ void Extract(const char* name);
+ void Remove(const char* name);
+ void List();
+
+ void WriteEntry(int index, BYTE* buf);
+ int FindEntry(const char* req_name);
+ int ExpandEntry(int index, BYTE*& buf, bool null_terminate=false);
+ BYTE* CompressEntry(int index);
+ int InsertEntry(const char* name);
+ void RemoveEntry(int index);
+ DWORD Blocks(DWORD raw_size);
+ DWORD DirBlocks();
+ DWORD FileBlocks(int index);
+ int FindDataBlocks(int blocks_needed);
+ void CreateBlockMap();
+
+ DWORD NumFiles() { return header.nfiles; }
+ DataEntry* GetFile(int i) { if (i>=0 && i<(int)header.nfiles) return &directory[i]; return 0; }
+
+ const char* Name() const { return datafile; }
+
+private:
+ // persistent data members:
+ DataHeader header;
+ DataEntry* directory;
+ BYTE* dirbuf;
+
+ // transient members:
+ char datafile[NAMELEN];
+
+ DWORD* block_map;
+ DWORD nblocks;
+};
+
+#endif Archive_h
diff --git a/nGenEx/AviFile.cpp b/nGenEx/AviFile.cpp
new file mode 100644
index 0000000..983de0c
--- /dev/null
+++ b/nGenEx/AviFile.cpp
@@ -0,0 +1,178 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: AviFile.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ AviFile reader / writer
+*/
+
+
+#include "MemDebug.h"
+#include "AviFile.h"
+
+#include <vfw.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+// +--------------------------------------------------------------------+
+
+void Print(const char* fmt, ...);
+
+// +--------------------------------------------------------------------+
+
+AviFile::AviFile(const char* fname)
+ : filename(fname), fps(0), play(true), pfile(0), ps(0), ps_comp(0),
+ nframe(0), nsamp(0), iserr(false), frame_size(0)
+{
+ AVIFileInit();
+}
+
+AviFile::AviFile(const char* fname, const Rect& r, int frame_rate)
+ : filename(fname), rect(r), fps(frame_rate), play(false), pfile(0),
+ ps(0), ps_comp(0), nframe(0), nsamp(0), iserr(false)
+{
+ Print("\n\nAviFile(%s, w=%d, h=%d, f=%d FPS)\n", fname, r.w, r.h, fps);
+ frame_size = r.w * r.h * 3;
+
+ AVIFileInit();
+ HRESULT hr = AVIFileOpen(&pfile, fname, OF_WRITE|OF_CREATE, 0);
+
+ if (hr != AVIERR_OK) {
+ Print("AviFile - open failed. %08x\n", hr);
+ iserr = true;
+ return;
+ }
+
+ Print("AviFile - open successful\n");
+
+ AVISTREAMINFO strhdr;
+ ZeroMemory(&strhdr,sizeof(strhdr));
+
+ strhdr.fccType = streamtypeVIDEO;
+ strhdr.fccHandler = 0;
+ strhdr.dwScale = 1000 / fps;
+ strhdr.dwRate = 1000;
+ strhdr.dwSuggestedBufferSize = frame_size;
+
+ SetRect(&strhdr.rcFrame, 0, 0, r.w, r.h);
+
+ hr = AVIFileCreateStream(pfile, &ps, &strhdr);
+
+ if (hr != AVIERR_OK) {
+ Print("AviFile - create stream failed. %08x\n", hr);
+ iserr = true;
+ return;
+ }
+
+ Print("AviFile - create stream successful\n");
+
+ AVICOMPRESSOPTIONS opts;
+ ZeroMemory(&opts,sizeof(opts));
+ opts.fccType = streamtypeVIDEO;
+ //opts.fccHandler = mmioFOURCC('W','M','V','3');
+ opts.fccHandler = mmioFOURCC('D','I','B',' '); // (full frames)
+ opts.dwFlags = 8;
+
+ hr = AVIMakeCompressedStream(&ps_comp, ps, &opts, 0);
+ if (hr != AVIERR_OK) {
+ Print("AviFile - compressed stream failed. %08x\n", hr);
+ iserr=true;
+ return;
+ }
+
+ Print("AviFile - make compressed stream successful\n");
+
+ BITMAPINFOHEADER bmih;
+ ZeroMemory(&bmih, sizeof(BITMAPINFOHEADER));
+
+ bmih.biSize = sizeof(BITMAPINFOHEADER);
+ bmih.biBitCount = 24;
+ bmih.biCompression = BI_RGB;
+ bmih.biWidth = rect.w;
+ bmih.biHeight = rect.h;
+ bmih.biPlanes = 1;
+ bmih.biSizeImage = frame_size;
+
+ hr = AVIStreamSetFormat(ps_comp, 0, &bmih, sizeof(BITMAPINFOHEADER));
+
+ if (hr != AVIERR_OK) {
+ Print("AviFile - stream format failed. %08x\n", hr);
+ iserr=true;
+ return;
+ }
+
+ Print("AviFile - stream format successful\n");
+}
+
+AviFile::~AviFile()
+{
+ if (!play) {
+ Print("*** Closing AVI file '%s' with %d frames\n", (const char*) filename, nframe);
+ }
+
+ if (ps_comp) AVIStreamRelease(ps_comp);
+ if (ps) AVIStreamRelease(ps);
+ if (pfile) AVIFileRelease(pfile);
+
+ AVIFileExit();
+}
+
+// +--------------------------------------------------------------------+
+//
+// Note that AVI frames use DIB format - Y is inverted.
+// So we need to flip the native bmp before sending to the
+// file.
+
+HRESULT
+AviFile::AddFrame(const Bitmap& bmp)
+{
+ HRESULT hr = E_FAIL;
+
+ if (!iserr && !play && bmp.IsHighColor() && bmp.Width() == rect.w && bmp.Height() == rect.h) {
+ int w = bmp.Width();
+ int h = bmp.Height();
+ BYTE* buffer = new(__FILE__,__LINE__) BYTE[frame_size];
+ BYTE* dst = buffer;
+
+ for (int y = 0; y < bmp.Height(); y++) {
+ Color* src = bmp.HiPixels() + (h - 1 - y) * w;
+
+ for (int x = 0; x < bmp.Width(); x++) {
+ *dst++ = (BYTE) src->Blue();
+ *dst++ = (BYTE) src->Green();
+ *dst++ = (BYTE) src->Red();
+ src++;
+ }
+ }
+
+ hr = AVIStreamWrite(ps_comp, nframe, 1, buffer, frame_size, AVIIF_KEYFRAME, 0, 0);
+
+ if (SUCCEEDED(hr)) {
+ nframe++;
+ }
+ else {
+ Print("AVIStreamWriteFile failed. %08x\n", hr);
+ iserr = true;
+ }
+
+ delete [] buffer;
+ }
+
+ return hr;
+}
+
+// +--------------------------------------------------------------------+
+
+HRESULT
+AviFile::GetFrame(double seconds, Bitmap& bmp)
+{
+ HRESULT hr = E_FAIL;
+ return hr;
+}
+
diff --git a/nGenEx/AviFile.h b/nGenEx/AviFile.h
new file mode 100644
index 0000000..495693a
--- /dev/null
+++ b/nGenEx/AviFile.h
@@ -0,0 +1,63 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: AviFile.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ PCX image file loader
+*/
+
+#ifndef AviFile_h
+#define AviFile_h
+
+#include "Text.h"
+#include "Color.h"
+#include "Bitmap.h"
+#include "Geometry.h"
+
+// +--------------------------------------------------------------------+
+
+struct IAVIFile;
+struct IAVIStream;
+
+// +--------------------------------------------------------------------+
+
+class AviFile
+{
+public:
+ static const char* TYPENAME() { return "AviFile"; }
+
+ // open for reading:
+ AviFile(const char* fname);
+
+ // create for writing
+ AviFile(const char* fname, const Rect& rect, int frame_rate=30);
+ ~AviFile();
+
+ HRESULT AddFrame(const Bitmap& bmp);
+ HRESULT GetFrame(double seconds, Bitmap& bmp);
+
+private:
+ Text filename;
+ Rect rect;
+ int fps;
+ bool play;
+
+ IAVIFile* pfile; // created by CreateAvi
+ IAVIStream* ps;
+ IAVIStream* ps_comp; // video stream, when first created
+ DWORD frame_size; // total bytes per frame of video
+ DWORD nframe; // which frame will be added next
+ DWORD nsamp; // which sample will be added next
+ bool iserr; // if true, then no function will do anything
+};
+
+// +--------------------------------------------------------------------+
+
+
+#endif AviFile_h
diff --git a/nGenEx/Bitmap.cpp b/nGenEx/Bitmap.cpp
new file mode 100644
index 0000000..54cd458
--- /dev/null
+++ b/nGenEx/Bitmap.cpp
@@ -0,0 +1,1618 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Bitmap.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Bitmap Resource class
+*/
+
+#include "MemDebug.h"
+#include "Bitmap.h"
+#include "Video.h"
+#include "Color.h"
+#include "Game.h"
+
+// +--------------------------------------------------------------------+
+
+DWORD GetRealTime();
+
+static inline void swap(int& a, int& b) { int tmp=a; a=b; b=tmp; }
+static inline void sort(int& a, int& b) { if (a>b) swap(a,b); }
+static inline void swap(double& a, double& b) { double tmp=a; a=b; b=tmp; }
+static inline void sort(double& a, double& b) { if (a>b) swap(a,b); }
+static void draw_strip(BYTE* dst, int pitch, int pixsize, int x, int y, int len, Color color);
+static void draw_vline(BYTE* dst, int pitch, int pixsize, int x, int y, int len, Color color);
+
+class WinPlot
+{
+public:
+ WinPlot(Bitmap* bmp);
+ void plot(int x, int y, DWORD val, int exch=0);
+
+private:
+ BYTE* s;
+ int pitch, pixsize;
+};
+
+WinPlot::WinPlot(Bitmap* bmp)
+{
+ s = bmp->GetSurface();
+ pitch = bmp->Pitch();
+ pixsize = bmp->PixSize();
+}
+
+void WinPlot::plot(int x, int y, DWORD val, int exch)
+{
+ if (exch) swap(x,y);
+ BYTE* dst = s + y*pitch + x*pixsize;
+
+ switch (pixsize) {
+ case 1: *dst = (BYTE) val; break;
+ case 2: { LPWORD dst2 = (LPWORD) dst; *dst2 = (WORD) val; } break;
+ case 4: { LPDWORD dst4 = (LPDWORD) dst; *dst4 = (DWORD) val; } break;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Bitmap::Bitmap()
+ : type(BMP_SOLID), width(0), height(0),
+ ownpix(false), alpha_loaded(false), texture(false),
+ pix(0), hipix(0), mapsize(0),
+ last_modified(0)
+{
+ strcpy(filename, "Bitmap()");
+}
+
+Bitmap::Bitmap(int w, int h, ColorIndex* p, int t)
+ : type(t), width(w), height(h),
+ ownpix(false), alpha_loaded(false), texture(false),
+ pix(p), hipix(0), mapsize(w*h),
+ last_modified(GetRealTime())
+{
+ sprintf(filename, "Bitmap(%d, %d, index, type=%d)", w, h, (int) t);
+}
+
+Bitmap::Bitmap(int w, int h, Color* p, int t)
+ : type(t), width(w), height(h),
+ ownpix(false), alpha_loaded(false), texture(false),
+ pix(0), hipix(p), mapsize(w*h),
+ last_modified(GetRealTime())
+{
+ sprintf(filename, "Bitmap(%d, %d, hicolor, type=%d)", w, h, (int) t);
+}
+
+// +--------------------------------------------------------------------+
+
+Bitmap::~Bitmap()
+{
+ if (ownpix) {
+ delete [] pix;
+ delete [] hipix;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Bitmap::BmpSize() const
+{
+ return mapsize * PixSize();
+}
+
+int
+Bitmap::RowSize() const
+{
+ return width;
+}
+
+int
+Bitmap::Pitch() const
+{
+ return width * PixSize();
+}
+
+int
+Bitmap::PixSize() const
+{
+ if (hipix)
+ return sizeof(Color);
+
+ else if (pix)
+ return sizeof(ColorIndex);
+
+ return 0;
+}
+
+BYTE*
+Bitmap::GetSurface()
+{
+ if (ownpix) {
+ if (hipix)
+ return (BYTE*) hipix;
+
+ return (BYTE*) pix;
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bitmap::BitBlt(int x, int y, const Bitmap& srcBmp, int sx, int sy, int w, int h, bool blend)
+{
+ if (!ownpix || x < 0 || y < 0 || x >= width || y >= height)
+ return;
+
+ if (sx < 0 || sy < 0 || sx >= srcBmp.Width() || sy >= srcBmp.Height())
+ return;
+
+ if (hipix) {
+ if (srcBmp.HiPixels()) {
+ int dpitch = width;
+ int spitch = srcBmp.Width();
+ int rowlen = w * sizeof(Color);
+ Color* dst = hipix + (y*dpitch) + x;
+ Color* src = srcBmp.HiPixels() + (sy*spitch) + sx;
+
+ if (!blend) {
+ for (int i = 0; i < h; i++) {
+ memcpy(dst, src, rowlen);
+ dst += dpitch;
+ src += spitch;
+ }
+ }
+ else {
+ for (int i = 0; i < h; i++) {
+ Color* ps = src;
+ Color* pd = dst;
+
+ for (int n = 0; n < w; n++) {
+ if (ps->Value())
+ pd->Set(Color::FormattedBlend(ps->Value(), pd->Value()));
+ ps++;
+ pd++;
+ }
+
+ dst += dpitch;
+ src += spitch;
+ }
+ }
+ }
+
+ else {
+ int dpitch = width;
+ int spitch = srcBmp.Width();
+ Color* dst = hipix + (y*dpitch) + x;
+ ColorIndex* src = srcBmp.Pixels() + (sy*spitch) + sx;
+
+ if (!blend) {
+ for (int j = 0; j < h; j++) {
+ for (int i = 0; i < w; i++) {
+ dst[i].Set(src[i].Formatted());
+ }
+
+ dst += dpitch;
+ src += spitch;
+ }
+ }
+ else {
+ for (int i = 0; i < h; i++) {
+ ColorIndex* ps = src;
+ Color* pd = dst;
+
+ for (int n = 0; n < w; n++) {
+ if (ps->Index())
+ pd->Set(Color::FormattedBlend(ps->Formatted(), pd->Value()));
+ ps++;
+ pd++;
+ }
+ }
+
+ dst += dpitch;
+ src += spitch;
+ }
+ }
+ }
+
+ else if (pix) {
+ if (srcBmp.Pixels()) {
+ int dpitch = width;
+ int spitch = srcBmp.Width();
+ int rowlen = w;
+ Color* dst = hipix + (y*dpitch) + x;
+ Color* src = srcBmp.HiPixels() + (sy*spitch) + sx;
+
+ for (int i = 0; i < h; i++) {
+ memcpy(dst, src, rowlen);
+ dst += dpitch;
+ src += spitch;
+ }
+ }
+ }
+
+ alpha_loaded = srcBmp.alpha_loaded;
+ last_modified = GetRealTime();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bitmap::CopyBitmap(const Bitmap& rhs)
+{
+ if (ownpix) {
+ delete [] pix;
+ delete [] hipix;
+ pix = 0;
+ hipix = 0;
+ }
+
+ type = rhs.type;
+ width = rhs.width;
+ height = rhs.height;
+ alpha_loaded = rhs.alpha_loaded;
+ texture = rhs.texture;
+ ownpix = true;
+
+ mapsize = width * height;
+
+ if (rhs.pix) {
+ pix = new(__FILE__,__LINE__) ColorIndex[mapsize];
+
+ if (!pix) {
+ width = 0;
+ height = 0;
+ mapsize = 0;
+ }
+
+ else {
+ memcpy(pix, rhs.pix, mapsize*sizeof(ColorIndex));
+ }
+ }
+
+ if (rhs.hipix) {
+ hipix = new(__FILE__,__LINE__) Color[mapsize];
+
+ if (!hipix && !pix) {
+ width = 0;
+ height = 0;
+ mapsize = 0;
+ }
+
+ else {
+ memcpy(hipix, rhs.hipix, mapsize*sizeof(Color));
+ }
+ }
+
+ last_modified = GetRealTime();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bitmap::ClearImage()
+{
+ if (ownpix) {
+ delete [] pix;
+ delete [] hipix;
+ pix = 0;
+ hipix = 0;
+ }
+
+ type = BMP_SOLID;
+ width = 0;
+ height = 0;
+ mapsize = 0;
+ ownpix = false;
+ texture = false;
+
+ last_modified = GetRealTime();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bitmap::CopyImage(int w, int h, BYTE* p, int t)
+{
+ if (ownpix) {
+ delete [] pix;
+ pix = 0;
+ }
+ else {
+ hipix = 0;
+ }
+
+ type = t;
+ width = w;
+ height = h;
+ ownpix = true;
+ texture = false;
+ mapsize = w * h;
+
+ pix = new(__FILE__,__LINE__) ColorIndex[mapsize];
+
+ if (!pix) {
+ width = 0;
+ height = 0;
+ mapsize = 0;
+ }
+
+ else {
+ memcpy(pix, p, mapsize);
+ }
+
+ last_modified = GetRealTime();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bitmap::CopyHighColorImage(int w, int h, DWORD* p, int t)
+{
+ if (ownpix) {
+ delete [] hipix;
+ hipix = 0;
+ }
+ else {
+ pix = 0;
+ }
+
+ type = t;
+ width = w;
+ height = h;
+ ownpix = true;
+ texture = false;
+ mapsize = w * h;
+
+ hipix = new(__FILE__,__LINE__) Color[mapsize];
+
+ if (!hipix) {
+ width = 0;
+ height = 0;
+ mapsize = 0;
+ }
+
+ else {
+ memcpy(hipix, p, mapsize*sizeof(DWORD));
+ }
+
+ last_modified = GetRealTime();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bitmap::CopyAlphaImage(int w, int h, BYTE* a)
+{
+ if (!hipix || width != w || height != h)
+ return;
+
+ type = BMP_TRANSLUCENT;
+ alpha_loaded = true;
+
+ Color* p = hipix;
+
+ for (int i = 0; i < mapsize; i++) {
+ p->SetAlpha(*a);
+ p++;
+ a++;
+ }
+
+ last_modified = GetRealTime();
+}
+
+void
+Bitmap::CopyAlphaRedChannel(int w, int h, DWORD* a)
+{
+ if (!hipix || width != w || height != h)
+ return;
+
+ type = BMP_TRANSLUCENT;
+ alpha_loaded = true;
+
+ Color* p = hipix;
+
+ for (int i = 0; i < mapsize; i++) {
+ p->SetAlpha((BYTE) ((*a & Color::RMask) >> Color::RShift));
+ p++;
+ a++;
+ }
+
+ last_modified = GetRealTime();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bitmap::AutoMask(DWORD mask)
+{
+ if (!hipix || !mapsize || alpha_loaded)
+ return;
+
+ type = BMP_TRANSLUCENT;
+ alpha_loaded = true;
+
+ Color* p = hipix;
+ DWORD m = mask & Color::RGBMask;
+
+ for (int i = 0; i < mapsize; i++) {
+ if ((p->Value() & Color::RGBMask) == m)
+ p->SetAlpha(0);
+ else
+ p->SetAlpha(255);
+
+ p++;
+ }
+
+ last_modified = GetRealTime();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bitmap::FillColor(Color c)
+{
+ if (!width || !height)
+ return;
+
+ if (pix) {
+ ColorIndex* p = pix;
+ BYTE index = c.Index();
+
+ for (int i = 0; i < mapsize; i++)
+ *p++ = index;
+ }
+
+ if (hipix) {
+ Color* p = hipix;
+ DWORD value = c.Value();
+
+ for (int i = 0; i < mapsize; i++) {
+ p->Set(value);
+ p++;
+ }
+ }
+
+ last_modified = GetRealTime();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bitmap::ScaleTo(int w, int h)
+{
+ if (w < 1 || h < 1)
+ return;
+
+ double dx = (double) width / (double) w;
+ double dy = (double) height / (double) h;
+
+ bool mem_ok = true;
+
+ if (hipix) {
+ Color* src = hipix;
+ Color* buf = new(__FILE__,__LINE__) Color[w*h];
+ Color* dst = buf;
+
+ if (!buf) {
+ mem_ok = false;
+ }
+
+ else {
+ for (int y = 0; y < h; y++) {
+ int y_offset = (int) (y * dy);
+ for (int x = 0; x < w; x++) {
+ int x_offset = (int) (x * dx);
+ src = hipix + (y_offset * width) + x_offset;
+ *dst++ = *src;
+ }
+ }
+
+ if (ownpix)
+ delete [] hipix;
+
+ hipix = buf;
+ ownpix = true;
+ }
+ }
+
+ if (pix) {
+ ColorIndex* src = pix;
+ ColorIndex* buf = new(__FILE__,__LINE__) ColorIndex[w*h];
+ ColorIndex* dst = buf;
+
+ if (!buf) {
+ mem_ok = false;
+ }
+
+ else {
+ for (int y = 0; y < h; y++) {
+ int y_offset = (int) (y * dy);
+ for (int x = 0; x < w; x++) {
+ int x_offset = (int) (x * dx);
+ src = pix + (y_offset * width) + x_offset;
+ *dst++ = *src;
+ }
+ }
+
+ if (ownpix)
+ delete [] pix;
+
+ pix = buf;
+ ownpix = true;
+ }
+ }
+
+ if (mem_ok) {
+ width = w;
+ height = h;
+ mapsize = width * height;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bitmap::MakeIndexed()
+{
+ if (hipix) {
+ if (pix && ownpix)
+ delete [] pix;
+ pix = new(__FILE__,__LINE__) ColorIndex[mapsize];
+
+ if (pix) {
+ Color* src = hipix;
+ ColorIndex* dst = pix;
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ *dst++ = src->Index();
+ src++;
+ }
+ }
+
+ if (!ownpix)
+ hipix = 0;
+
+ ownpix = true;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bitmap::MakeHighColor()
+{
+ if (pix) {
+ if (hipix && ownpix)
+ delete [] hipix;
+
+ hipix = new(__FILE__,__LINE__) Color[mapsize];
+
+ if (hipix) {
+ ColorIndex* src = pix;
+ Color* dst = hipix;
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ *dst++ = src->Index();
+ src++;
+ }
+ }
+
+ if (!ownpix)
+ pix = 0;
+
+ ownpix = true;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+static int FindBestTexSize(int n, int max_size)
+{
+ int delta = 100000;
+ int best = 1;
+
+ for (int i = 0; i < 12; i++) {
+ int size = 1 << i;
+
+ if (size > max_size)
+ break;
+
+ int dx = abs(n-size);
+
+ if (size < n)
+ dx *= 4;
+
+ if (dx < delta) {
+ delta = dx;
+ best = size;
+ }
+ }
+
+ return best;
+}
+
+void
+Bitmap::MakeTexture()
+{
+ if (width < 1 || height < 1 || (!pix && !hipix)) {
+ if (ownpix) {
+ delete [] pix;
+ delete [] hipix;
+ }
+
+ width = 0;
+ height = 0;
+ pix = 0;
+ hipix = 0;
+ texture = false;
+ return;
+ }
+
+ // texture surface format is 32-bit RGBA:
+ if (pix && !hipix) {
+ MakeHighColor();
+ }
+
+ // check size and aspect ratio:
+ int max_tex_size = Game::MaxTexSize();
+ int max_tex_aspect = Game::MaxTexAspect();
+
+ int best_width = FindBestTexSize(width, max_tex_size);
+ int best_height = FindBestTexSize(height, max_tex_size);
+ int aspect = 1;
+
+ // correct sizes for aspect if necessary:
+ if (best_width > best_height) {
+ aspect = best_width / best_height;
+
+ if (aspect > max_tex_aspect)
+ best_height = best_width / max_tex_aspect;
+ }
+
+ else {
+ aspect = best_height / best_width;
+
+ if (aspect > max_tex_aspect)
+ best_width = best_height / max_tex_aspect;
+ }
+
+ // rescale if necessary:
+ if (width != best_width || height != best_height)
+ ScaleTo(best_width, best_width);
+
+ texture = true;
+}
+
+// +--------------------------------------------------------------------+
+
+ColorIndex
+Bitmap::GetIndex(int x, int y) const
+{
+ ColorIndex result(0);
+
+ if (x < 0 || y < 0 || x > width-1 || y > height-1)
+ return result;
+
+ if (pix) {
+ result = *(pix + y*width + x);
+ }
+ else if (hipix) {
+ result = (hipix + y*width + x)->Index();
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+Color
+Bitmap::GetColor(int x, int y) const
+{
+ Color result = Color::Black;
+
+ if (x < 0 || y < 0 || x > width-1 || y > height-1)
+ return result;
+
+ if (pix) {
+ result = (pix + y*width + x)->Index();
+ }
+ else if (hipix) {
+ result = *(hipix + y*width + x);
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bitmap::SetIndex(int x, int y, ColorIndex c)
+{
+ if (x < 0 || y < 0 || x > width || y > height)
+ return;
+
+ if (pix) {
+ *(pix + y*width + x) = c;
+ }
+ else if (hipix) {
+ *(hipix + y*width + x) = c.Index();
+ }
+
+ last_modified = GetRealTime();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bitmap::SetColor(int x, int y, Color c)
+{
+ if (x < 0 || y < 0 || x > width || y > height)
+ return;
+
+ if (pix) {
+ *(pix + y*width + x) = c.Index();
+ }
+ else if (hipix) {
+ *(hipix + y*width + x) = c;
+ }
+
+ last_modified = GetRealTime();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bitmap::SetFilename(const char* s)
+{
+ if (s) {
+ int n = strlen(s);
+
+ if (n >= 60) {
+ ZeroMemory(filename, sizeof(filename));
+ strcpy(filename, "...");
+ strcat(filename, s + n - 59);
+ filename[63] = 0;
+ }
+
+ else {
+ strcpy(filename, s);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Bitmap*
+Bitmap::Default()
+{
+ static Bitmap def;
+
+ if (!def.width) {
+ def.width = def.height = 64;
+ def.mapsize = 64*64;
+ def.ownpix = true;
+ def.pix = new(__FILE__,__LINE__) ColorIndex[def.mapsize];
+
+ // 8 bit palette mode
+ if (def.pix) {
+ ColorIndex* p = def.pix;
+
+ for (int y = 0; y < 64; y++) {
+ for (int x = 0; x < 64; x++) {
+ double distance = sqrt((x-32.0)*(x-32.0) + (y-32.0)*(y-32.0));
+ if (distance > 31.0) distance = 31.0;
+ BYTE color = 24 + (BYTE) distance;
+
+ if (x == 0 || y == 0) color = 255;
+ else if (x == 32 || y == 32) color = 251;
+
+ *p++ = color;
+ }
+ }
+ }
+ }
+
+ return &def;
+}
+
+// +--------------------------------------------------------------------+
+
+static List<Bitmap> bitmap_cache;
+
+Bitmap*
+Bitmap::GetBitmapByID(HANDLE bmp_id)
+{
+ for (int i = 0; i < bitmap_cache.size(); i++) {
+ if (bitmap_cache[i]->Handle() == bmp_id) {
+ return bitmap_cache[i];
+ }
+ }
+
+ return 0;
+}
+
+Bitmap*
+Bitmap::CheckCache(const char* filename)
+{
+ for (int i = 0; i < bitmap_cache.size(); i++) {
+ if (!stricmp(bitmap_cache[i]->GetFilename(), filename)) {
+ return bitmap_cache[i];
+ }
+ }
+
+ return 0;
+}
+
+void
+Bitmap::AddToCache(Bitmap* bmp)
+{
+ bitmap_cache.append(bmp);
+}
+
+void
+Bitmap::CacheUpdate()
+{
+ for (int i = 0; i < bitmap_cache.size(); i++) {
+ Bitmap* bmp = bitmap_cache[i];
+
+ if (bmp->IsTexture())
+ bmp->MakeTexture();
+ }
+}
+
+void
+Bitmap::ClearCache()
+{
+ bitmap_cache.destroy();
+}
+
+DWORD
+Bitmap::CacheMemoryFootprint()
+{
+ DWORD result = sizeof(bitmap_cache);
+ result += bitmap_cache.size() * sizeof(Bitmap*);
+
+ for (int i = 0; i < bitmap_cache.size(); i++) {
+ Bitmap* bmp = bitmap_cache[i];
+
+ if (bmp->pix)
+ result += bmp->mapsize * sizeof(ColorIndex);
+
+ if (bmp->hipix)
+ result += bmp->mapsize * sizeof(Color);
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Bitmap::ClipLine(int& x1, int& y1, int& x2, int& y2)
+{
+ // vertical lines:
+ if (x1==x2) {
+clip_vertical:
+ sort(y1,y2);
+ if (x1 < 0 || x1 >= width) return false;
+ if (y1 < 0) y1 = 0;
+ if (y2 >= height) y2 = height;
+ return true;
+ }
+
+ // horizontal lines:
+ if (y1==y2) {
+clip_horizontal:
+ sort(x1,x2);
+ if (y1 < 0 || y1 >= height) return false;
+ if (x1 < 0) x1 = 0;
+ if (x2 > width) x2 = width;
+ return true;
+ }
+
+ // general lines:
+
+ // sort left to right:
+ if (x1 > x2) {
+ swap(x1,x2);
+ swap(y1,y2);
+ }
+
+ double m = (double)(y2-y1) / (double)(x2-x1);
+ double b = (double) y1 - (m * x1);
+
+ // clip:
+ if (x1 < 0) { x1 = 0; y1 = (int) b; }
+ if (x1 >= width) return false;
+ if (x2 < 0) return false;
+ if (x2 > width-1) { x2 = width-1; y2 = (int) (m * x2 + b); }
+
+ if (y1 < 0 && y2 < 0) return false;
+ if (y1 >= height && y2 >= height) return false;
+
+ if (y1 < 0) { y1 = 0; x1 = (int) (-b/m); }
+ if (y1 >= height) { y1 = height-1; x1 = (int) ((y1-b)/m); }
+ if (y2 < 0) { y2 = 0; x2 = (int) (-b/m); }
+ if (y2 >= height) { y2 = height-1; x2 = (int) ((y2-b)/m); }
+
+ if (x1 == x2)
+ goto clip_vertical;
+
+ if (y1 == y2)
+ goto clip_horizontal;
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Bitmap::ClipLine(double& x1, double& y1, double& x2, double& y2)
+{
+ // vertical lines:
+ if (x1==x2) {
+clip_vertical:
+ sort(y1,y2);
+ if (x1 < 0 || x1 >= width) return false;
+ if (y1 < 0) y1 = 0;
+ if (y2 >= height) y2 = height;
+ return true;
+ }
+
+ // horizontal lines:
+ if (y1==y2) {
+clip_horizontal:
+ sort(x1,x2);
+ if (y1 < 0 || y1 >= height) return false;
+ if (x1 < 0) x1 = 0;
+ if (x2 > width) x2 = width;
+ return true;
+ }
+
+ // general lines:
+
+ // sort left to right:
+ if (x1 > x2) {
+ swap(x1,x2);
+ swap(y1,y2);
+ }
+
+ double m = (double)(y2-y1) / (double)(x2-x1);
+ double b = (double) y1 - (m * x1);
+
+ // clip:
+ if (x1 < 0) { x1 = 0; y1 = b; }
+ if (x1 >= width) return false;
+ if (x2 < 0) return false;
+ if (x2 > width-1) { x2 = width-1; y2 = (m * x2 + b); }
+
+ if (y1 < 0 && y2 < 0) return false;
+ if (y1 >= height && y2 >= height) return false;
+
+ if (y1 < 0) { y1 = 0; x1 = (-b/m); }
+ if (y1 >= height) { y1 = height-1; x1 = ((y1-b)/m); }
+ if (y2 < 0) { y2 = 0; x2 = (-b/m); }
+ if (y2 >= height) { y2 = height-1; x2 = ((y2-b)/m); }
+
+ if (x1 == x2)
+ goto clip_vertical;
+
+ if (y1 == y2)
+ goto clip_horizontal;
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bitmap::DrawLine(int x1, int y1, int x2, int y2, Color color)
+{
+ BYTE* s = GetSurface();
+
+ if (!s) return;
+
+ last_modified = GetRealTime();
+
+ // vertical lines:
+ if (x1==x2) {
+draw_vertical:
+ sort(y1,y2);
+ int fh = y2-y1;
+ if (x1 < 0 || x1 >= width) return;
+ if (y1 < 0) y1 = 0;
+ if (y2 >= height) y2 = height;
+ fh = y2-y1;
+ draw_vline(s, Pitch(), PixSize(), x1, y1, fh, color);
+ return;
+ }
+
+ // horizontal lines:
+ if (y1==y2) {
+draw_horizontal:
+ sort(x1,x2);
+ int fw = x2-x1;
+ if (y1 < 0 || y1 >= height) return;
+ if (x1 < 0) x1 = 0;
+ if (x2 > width) x2 = width;
+ fw = x2-x1;
+ draw_strip(s, Pitch(), PixSize(), x1, y1, fw, color);
+ return;
+ }
+
+ // general lines:
+
+ // sort left to right:
+ if (x1 > x2) {
+ swap(x1,x2);
+ swap(y1,y2);
+ }
+
+ double m = (double)(y2-y1) / (double)(x2-x1);
+ double b = (double) y1 - (m * x1);
+
+ // clip:
+ if (x1 < 0) { x1 = 0; y1 = (int) b; }
+ if (x1 >= width) return;
+ if (x2 < 0) return;
+ if (x2 > width-1) { x2 = width-1; y2 = (int) (m * x2 + b); }
+
+ if (y1 < 0 && y2 < 0) return;
+ if (y1 >= height && y2 >= height) return;
+
+ if (y1 < 0) { y1 = 0; x1 = (int) (-b/m); }
+ if (y1 >= height) { y1 = height-1; x1 = (int) ((y1-b)/m); }
+ if (y2 < 0) { y2 = 0; x2 = (int) (-b/m); }
+ if (y2 >= height) { y2 = height-1; x2 = (int) ((y2-b)/m); }
+
+ if (x1 > x2)
+ return;
+
+ if (x1 == x2)
+ goto draw_vertical;
+
+ if (y1 == y2)
+ goto draw_horizontal;
+
+ // plot the line using
+ /*
+ Symmetric Double Step Line Algorithm
+ by Brian Wyvill
+ from "Graphics Gems", Academic Press, 1990
+ */
+
+ WinPlot plotter(this);
+
+ DWORD pix = color.Value();
+ int sign_x=1, sign_y=1, step, reflect;
+ int i, inc1, inc2, c, D, x_end, pixleft;
+ int dx = x2 - x1;
+ int dy = y2 - y1;
+
+ if (dx < 0) {
+ sign_x = -1;
+ dx *= -1;
+ }
+ if (dy < 0) {
+ sign_y = -1;
+ dy *= -1;
+ }
+
+ // decide increment sign by the slope sign
+ if (sign_x == sign_y)
+ step = 1;
+ else
+ step = -1;
+
+ if (dy > dx) { // chooses axis of greatest movement (make * dx)
+ swap(x1, y1);
+ swap(x2, y2);
+ swap(dx, dy);
+ reflect = 1;
+ } else
+ reflect = 0;
+
+ if (x1 > x2) { // start from the smaller coordinate
+ swap(x1,x2);
+ swap(y1,y2);
+ }
+
+ /* Note dx=n implies 0 - n or (dx+1) pixels to be set */
+ /* Go round loop dx/4 times then plot last 0,1,2 or 3 pixels */
+ /* In fact (dx-1)/4 as 2 pixels are already plottted */
+ x_end = (dx - 1) / 4;
+ pixleft = (dx - 1) % 4; /* number of pixels left over at the end */
+
+ plotter.plot(x1, y1, pix, reflect);
+ plotter.plot(x2, y2, pix, reflect); /* plot first two points */
+
+ inc2 = 4 * dy - 2 * dx;
+ if (inc2 < 0) { /* slope less than 1/2 */
+ c = 2 * dy;
+ inc1 = 2 * c;
+ D = inc1 - dx;
+
+ for (i = 0; i < x_end; i++) { /* plotting loop */
+ ++x1;
+ --x2;
+ if (D < 0) {
+ /* pattern 1 forwards */
+ plotter.plot(x1, y1, pix, reflect);
+ plotter.plot(++x1, y1, pix, reflect);
+ /* pattern 1 backwards */
+ plotter.plot(x2, y2, pix, reflect);
+ plotter.plot(--x2, y2, pix, reflect);
+ D += inc1;
+ }
+ else {
+ if (D < c) {
+ /* pattern 2 forwards */
+ plotter.plot(x1, y1, pix, reflect);
+ plotter.plot(++x1, y1 += step, pix, reflect);
+ /* pattern 2 backwards */
+ plotter.plot(x2, y2, pix, reflect);
+ plotter.plot(--x2, y2 -= step, pix, reflect);
+ }
+ else {
+ /* pattern 3 forwards */
+ plotter.plot(x1, y1 += step, pix, reflect);
+ plotter.plot(++x1, y1, pix, reflect);
+ /* pattern 3 backwards */
+ plotter.plot(x2, y2 -= step, pix, reflect);
+ plotter.plot(--x2, y2, pix, reflect);
+ }
+ D += inc2;
+ }
+ } /* end for */
+
+ /* plot last pattern */
+ if (pixleft) {
+ if (D < 0) {
+ plotter.plot(++x1, y1, pix, reflect); /* pattern 1 */
+ if (pixleft > 1)
+ plotter.plot(++x1, y1, pix, reflect);
+ if (pixleft > 2)
+ plotter.plot(--x2, y2, pix, reflect);
+ }
+ else {
+ if (D < c) {
+ plotter.plot(++x1, y1, pix, reflect); /* pattern 2 */
+ if (pixleft > 1)
+ plotter.plot(++x1, y1 += step, pix, reflect);
+ if (pixleft > 2)
+ plotter.plot(--x2, y2, pix, reflect);
+ }
+ else {
+ /* pattern 3 */
+ plotter.plot(++x1, y1 += step, pix, reflect);
+ if (pixleft > 1)
+ plotter.plot(++x1, y1, pix, reflect);
+ if (pixleft > 2)
+ plotter.plot(--x2, y2 -= step, pix, reflect);
+ }
+ }
+ } /* end if pixleft */
+ }
+ /* end slope < 1/2 */
+ else { /* slope greater than 1/2 */
+ c = 2 * (dy - dx);
+ inc1 = 2 * c;
+ D = inc1 + dx;
+ for (i = 0; i < x_end; i++) {
+ ++x1;
+ --x2;
+ if (D > 0) {
+ /* pattern 4 forwards */
+ plotter.plot(x1, y1 += step, pix, reflect);
+ plotter.plot(++x1, y1 += step, pix, reflect);
+ /* pattern 4 backwards */
+ plotter.plot(x2, y2 -= step, pix, reflect);
+ plotter.plot(--x2, y2 -= step, pix, reflect);
+ D += inc1;
+ } else {
+ if (D < c) {
+ /* pattern 2 forwards */
+ plotter.plot(x1, y1, pix, reflect);
+ plotter.plot(++x1, y1 += step, pix, reflect);
+
+ /* pattern 2 backwards */
+ plotter.plot(x2, y2, pix, reflect);
+ plotter.plot(--x2, y2 -= step, pix, reflect);
+ } else {
+ /* pattern 3 forwards */
+ plotter.plot(x1, y1 += step, pix, reflect);
+ plotter.plot(++x1, y1, pix, reflect);
+ /* pattern 3 backwards */
+ plotter.plot(x2, y2 -= step, pix, reflect);
+ plotter.plot(--x2, y2, pix, reflect);
+ }
+ D += inc2;
+ }
+ } /* end for */
+ /* plot last pattern */
+ if (pixleft) {
+ if (D > 0) {
+ plotter.plot(++x1, y1 += step, pix, reflect); /* pattern 4 */
+ if (pixleft > 1)
+ plotter.plot(++x1, y1 += step, pix, reflect);
+ if (pixleft > 2)
+ plotter.plot(--x2, y2 -= step, pix, reflect);
+ } else {
+ if (D < c) {
+ plotter.plot(++x1, y1, pix, reflect); /* pattern 2 */
+ if (pixleft > 1)
+ plotter.plot(++x1, y1 += step, pix, reflect);
+ if (pixleft > 2)
+ plotter.plot(--x2, y2, pix, reflect);
+ } else {
+ /* pattern 3 */
+ plotter.plot(++x1, y1 += step, pix, reflect);
+ if (pixleft > 1)
+ plotter.plot(++x1, y1, pix, reflect);
+ if (pixleft > 2) {
+ if (D > c) /* step 3 */
+ plotter.plot(--x2, y2 -= step, pix, reflect);
+ else /* step 2 */
+ plotter.plot(--x2, y2, pix, reflect);
+ }
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bitmap::DrawRect(int x1, int y1, int x2, int y2, Color color)
+{
+ sort(x1,x2);
+ sort(y1,y2);
+
+ int fw = x2-x1;
+ int fh = y2-y1;
+
+ if (fw == 0 || fh == 0) return;
+
+ // perform clip
+ int left = (x1 >= 0);
+ int right = (x2 <= width);
+ int top = (y1 >= 0);
+ int bottom = (y2 <= height);
+
+ BYTE* s = GetSurface();
+ if (!s) return;
+ int pitch = Pitch();
+ int pixsize = PixSize();
+
+ if (left) draw_vline(s, pitch, pixsize, x1, y1, fh, color);
+ if (right) draw_vline(s, pitch, pixsize, x2, y1, fh, color);
+ if (top) draw_strip(s, pitch, pixsize, x1, y1, fw, color);
+ if (bottom) draw_strip(s, pitch, pixsize, x1, y2, fw, color);
+
+ last_modified = GetRealTime();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bitmap::DrawRect(const Rect& r, Color color)
+{
+ if (r.w == 0 || r.h == 0) return;
+
+ int x1 = r.x;
+ int y1 = r.y;
+ int x2 = r.x + r.w;
+ int y2 = r.y + r.h;
+
+ // perform clip
+ int left = (x1 >= 0);
+ int right = (x2 <= width);
+ int top = (y1 >= 0);
+ int bottom = (y2 <= height);
+
+ BYTE* s = GetSurface();
+ if (!s) return;
+ int pitch = Pitch();
+ int pixsize = PixSize();
+
+ if (left) draw_vline(s, pitch, pixsize, x1, y1, r.h, color);
+ if (right) draw_vline(s, pitch, pixsize, x2, y1, r.h, color);
+ if (top) draw_strip(s, pitch, pixsize, x1, y1, r.w, color);
+ if (bottom) draw_strip(s, pitch, pixsize, x1, y2, r.w, color);
+
+ last_modified = GetRealTime();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bitmap::FillRect(int x1, int y1, int x2, int y2, Color color)
+{
+ // perform clip
+ if (x1 < 0) x1 = 0;
+ if (x2 > width-1) x2 = width-1;
+ if (y1 < 0) y1 = 0;
+ if (y2 > height) y2 = height;
+
+ int fw = x2-x1;
+ int fh = y2-y1;
+
+ if (fw == 0 || fh == 0) return;
+
+ BYTE* s = GetSurface();
+ if (!s) return;
+ int pitch = Pitch();
+ int pixsize = PixSize();
+
+ for (int i = 0; i < fh; i++)
+ draw_strip(s, pitch, pixsize, x1, y1+i, fw, color);
+
+ last_modified = GetRealTime();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bitmap::FillRect(const Rect& r, Color color)
+{
+ int x1 = r.x;
+ int y1 = r.y;
+ int x2 = r.x + r.w;
+ int y2 = r.y + r.h;
+
+ // perform clip
+ if (x1 < 0) x1 = 0;
+ if (x2 > width-1) x2 = width-1;
+ if (y1 < 0) y1 = 0;
+ if (y2 > height) y2 = height;
+
+ int fw = x2-x1;
+ int fh = y2-y1;
+
+ if (fw == 0 || fh == 0) return;
+
+ BYTE* s = GetSurface();
+ if (!s) return;
+ int pitch = Pitch();
+ int pixsize = PixSize();
+
+ for (int i = 0; i < fh; i++)
+ draw_strip(s, pitch, pixsize, x1, y1+i, fw, color);
+
+ last_modified = GetRealTime();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bitmap::DrawEllipse(int x1, int y1, int x2, int y2, Color color, BYTE quad)
+{
+ BYTE* orig = GetSurface();
+ BYTE* s = orig;
+
+ if (!s) return;
+
+ sort(x1,x2);
+ sort(y1,y2);
+
+ int fw = x2-x1;
+ int fh = y2-y1;
+
+ if (fw < 1 || fh < 1) return;
+
+ // clip:
+ if (x1 >= width || x2 < 0) return;
+ if (y1 >= height || y2 < 0) return;
+
+ double a = fw / 2.0;
+ double b = fh / 2.0;
+ double a2 = a*a;
+ double b2 = b*b;
+
+ int x = 0;
+ int y = (int) b;
+ int x0 = (x1+x2)/2;
+ int y0 = (y1+y2)/2;
+
+ // clip super-giant ellipses:
+ if (x1 < 0 && y1 < 0 && x2 > width && y2 > height) {
+ double r2 = (a2<b2) ? a2 : b2;
+
+ if (r2 > 32 * height)
+ return;
+
+ double ul = (x1-x0)*(x1-x0) + (y1-y0)*(y1-y0);
+ double ur = (x2-x0)*(x2-x0) + (y1-y0)*(y1-y0);
+ double ll = (x1-x0)*(x1-x0) + (y2-y0)*(y2-y0);
+ double lr = (x2-x0)*(x2-x0) + (y2-y0)*(y2-y0);
+
+ if (ul > r2 && ur > r2 && ll > r2 && lr > r2)
+ return;
+ }
+
+ DrawEllipsePoints(x0,y0,x,y,color,quad);
+
+ // region 1
+ double d1 = (b2)-(a2*b)+(0.25*a2);
+ while ((a2)*(y-0.5) > (b2)*(x+1)) {
+ if (d1 < 0)
+ d1 += b2*(2*x+3);
+ else {
+ d1 += b2*(2*x+3) + a2*(-2*y+2);
+ y--;
+ }
+ x++;
+
+ DrawEllipsePoints(x0,y0,x,y,color,quad);
+ }
+
+ // region 2
+ double d2 = b2*(x+0.5)*(x+0.5) + a2*(y-1)*(y-1) - a2*b2;
+ while (y > 0) {
+ if (d2 < 0) {
+ d2 += b2*(2*x+2) + a2*(-2*y+3);
+ x++;
+ }
+ else
+ d2 += a2*(-2*y+3);
+ y--;
+
+ DrawEllipsePoints(x0,y0,x,y,color,quad);
+ }
+
+ last_modified = GetRealTime();
+}
+
+void
+Bitmap::DrawEllipsePoints(int x0, int y0, int x, int y, Color c, BYTE quad)
+{
+ BYTE* s = GetSurface();
+
+ if (!s) return;
+
+ int pitch = Pitch();
+ int pixsize = PixSize();
+
+ int left = x0-x;
+ int right = x0+x+1;
+ int top = y0-y;
+ int bottom = y0+y+1;
+
+ // clip:
+ if (left >= width || right < 0) return;
+ if (top >= height || bottom < 0) return;
+
+ BYTE* dst = 0;
+ DWORD cf = c.Value();
+
+ if (left >= 0 && top >= 0 && quad&1) {
+ dst = s + top*pitch + left*pixsize;
+
+ switch (pixsize) {
+ case 1: *dst = (BYTE) cf; break;
+ case 2: { LPWORD sw = (LPWORD) dst; *sw = (WORD) cf; } break;
+ case 4: { LPDWORD sd = (LPDWORD) dst; *sd = (DWORD) cf; } break;
+ }
+ }
+
+ if (right < width && top >= 0 && quad&2) {
+ dst = s + top*pitch + right*pixsize;
+
+ switch (pixsize) {
+ case 1: *dst = (BYTE) cf; break;
+ case 2: { LPWORD sw = (LPWORD) dst; *sw = (WORD) cf; } break;
+ case 4: { LPDWORD sd = (LPDWORD) dst; *sd = (DWORD) cf; } break;
+ }
+ }
+
+ if (left >= 0 && bottom < height && quad&4) {
+ dst = s + bottom*pitch + left*pixsize;
+
+ switch (pixsize) {
+ case 1: *dst = (BYTE) cf; break;
+ case 2: { LPWORD sw = (LPWORD) dst; *sw = (WORD) cf; } break;
+ case 4: { LPDWORD sd = (LPDWORD) dst; *sd = (DWORD) cf; } break;
+ }
+ }
+
+ if (right < width && bottom < height && quad&4) {
+ dst = s + bottom*pitch + right*pixsize;
+
+ switch (pixsize) {
+ case 1: *dst = (BYTE) cf; break;
+ case 2: { LPWORD sw = (LPWORD) dst; *sw = (WORD) cf; } break;
+ case 4: { LPDWORD sd = (LPDWORD) dst; *sd = (DWORD) cf; } break;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+static void draw_strip(BYTE* s, int pitch, int pixsize, int x, int y, int len, Color color)
+{
+ if (!s) return;
+ s += y*pitch + x*pixsize;
+
+ DWORD value = color.Formatted();
+
+ switch (pixsize) {
+ case 1: {
+ if (len > 1)
+ memset(s, (BYTE) value, len);
+ }
+ break;
+
+ case 2: {
+ LPWORD sw = (LPWORD) s;
+ for (int x = 0; x < len; x++) {
+ *sw++ = (WORD) value;
+ }
+ }
+ break;
+
+ case 4: {
+ Color* sd = (Color*) s;
+ for (int x = 0; x < len; x++) {
+ *sd++ = color;
+ }
+ }
+ break;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+static void draw_vline(BYTE* s, int pitch, int pixsize, int x, int y, int len, Color color)
+{
+ if (!s) return;
+ s += (y)*pitch + (x)*pixsize;
+
+ DWORD value = color.Formatted();
+
+ switch (pixsize) {
+ case 1: {
+ for (int y = 0; y < len; y++) {
+ *s = (BYTE) value;
+ s += pitch;
+ }
+ }
+ break;
+
+ case 2: {
+ LPWORD sw = (LPWORD) s;
+ pitch /= 2;
+
+ for (int y = 0; y < len; y++) {
+ *sw = (WORD) value;
+ sw += pitch;
+ }
+ }
+ break;
+
+ case 4: {
+ Color* sd = (Color*) s;
+ pitch /= 4;
+
+ for (int y = 0; y < len; y++) {
+ *sd = color;
+ sd += pitch;
+ }
+ }
+ break;
+ }
+}
+
+
diff --git a/nGenEx/Bitmap.h b/nGenEx/Bitmap.h
new file mode 100644
index 0000000..519f589
--- /dev/null
+++ b/nGenEx/Bitmap.h
@@ -0,0 +1,124 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Bitmap.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Bitmap Resource class
+*/
+
+#ifndef Bitmap_h
+#define Bitmap_h
+
+#include "Res.h"
+#include "Types.h"
+#include "Geometry.h"
+
+// +--------------------------------------------------------------------+
+
+class Color;
+class ColorIndex;
+
+class Bitmap : public Resource
+{
+public:
+ static const char* TYPENAME() { return "Bitmap"; }
+
+ enum BMP_TYPES { BMP_SOLID, BMP_TRANSPARENT, BMP_TRANSLUCENT };
+
+ Bitmap();
+ Bitmap(int w, int h, ColorIndex* p=0, int t=BMP_SOLID);
+ Bitmap(int w, int h, Color* p, int t=BMP_SOLID);
+ virtual ~Bitmap();
+
+ int IsIndexed() const { return pix != 0; }
+ int IsHighColor() const { return hipix != 0; }
+ int IsDual() const { return IsIndexed() &&
+ IsHighColor(); }
+
+ void SetType(int t) { type = t; }
+ int Type() const { return type; }
+ bool IsSolid() const { return type==BMP_SOLID; }
+ bool IsTransparent() const { return type==BMP_TRANSPARENT; }
+ bool IsTranslucent() const { return type==BMP_TRANSLUCENT; }
+
+ int Width() const { return width; }
+ int Height() const { return height; }
+ ColorIndex* Pixels() const { return pix; }
+ Color* HiPixels() const { return hipix; }
+ int BmpSize() const;
+ int RowSize() const;
+
+ ColorIndex GetIndex(int x, int y) const;
+ Color GetColor(int x, int y) const;
+ void SetIndex(int x, int y, ColorIndex c);
+ void SetColor(int x, int y, Color c);
+
+ void FillColor(Color c);
+
+ void ClearImage();
+ void BitBlt(int x, int y, const Bitmap& srcImage, int sx, int sy, int w, int h, bool blend=false);
+ void CopyBitmap(const Bitmap& rhs);
+ void CopyImage(int w, int h, BYTE* p, int t=BMP_SOLID);
+ void CopyHighColorImage(int w, int h, DWORD* p, int t=BMP_SOLID);
+ void CopyAlphaImage(int w, int h, BYTE* p);
+ void CopyAlphaRedChannel(int w, int h, DWORD* p);
+ void AutoMask(DWORD mask=0);
+
+ virtual BYTE* GetSurface();
+ virtual int Pitch() const;
+ virtual int PixSize() const;
+ bool ClipLine(int& x1, int& y1, int& x2, int& y2);
+ bool ClipLine(double& x1, double& y1, double& x2, double& y2);
+ void DrawLine(int x1, int y1, int x2, int y2, Color color);
+ void DrawRect(int x1, int y1, int x2, int y2, Color color);
+ void DrawRect(const Rect& r, Color color);
+ void FillRect(int x1, int y1, int x2, int y2, Color color);
+ void FillRect(const Rect& r, Color color);
+ void DrawEllipse(int x1, int y1, int x2, int y2, Color color, BYTE quad=0x0f);
+ void DrawEllipsePoints(int x0, int y0, int x, int y, Color c, BYTE quad);
+
+ void ScaleTo(int w, int h);
+ void MakeIndexed();
+ void MakeHighColor();
+ void MakeTexture();
+ bool IsTexture() const { return texture; }
+ void TakeOwnership() { ownpix = true; }
+
+ const char* GetFilename() const { return filename; }
+ void SetFilename(const char* s);
+
+ DWORD LastModified() const { return last_modified; }
+
+ static Bitmap* Default();
+
+ static Bitmap* GetBitmapByID(HANDLE bmp_id);
+ static Bitmap* CheckCache(const char* filename);
+ static void AddToCache(Bitmap* bmp);
+ static void CacheUpdate();
+ static void ClearCache();
+ static DWORD CacheMemoryFootprint();
+
+protected:
+ int type;
+ int width;
+ int height;
+ int mapsize;
+
+ bool ownpix;
+ bool alpha_loaded;
+ bool texture;
+
+ ColorIndex* pix;
+ Color* hipix;
+ DWORD last_modified;
+ char filename[64];
+};
+
+#endif Bitmap_h
+
diff --git a/nGenEx/Bmp.cpp b/nGenEx/Bmp.cpp
new file mode 100644
index 0000000..18c5870
--- /dev/null
+++ b/nGenEx/Bmp.cpp
@@ -0,0 +1,251 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Bmp.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ BMP image file loader
+*/
+
+
+#include "MemDebug.h"
+#include "BMP.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+// +--------------------------------------------------------------------+
+
+BmpImage::BmpImage()
+ : width(0), height(0), image(0)
+{ }
+
+BmpImage::BmpImage(short w, short h, unsigned long* hibits)
+{
+ ZeroMemory(this, sizeof(BmpImage));
+
+ width = w;
+ height = h;
+
+ file_hdr.type = 19778; // 'BM'
+ file_hdr.size = sizeof(BmpFileHeader) +
+ sizeof(BmpInfoHeader) +
+ w * h * 3;
+
+ file_hdr.offset = sizeof(BmpFileHeader) +
+ sizeof(BmpInfoHeader);
+
+ info_hdr.hdr_size = sizeof(BmpInfoHeader);
+ info_hdr.width = width;
+ info_hdr.height = height;
+ info_hdr.planes = 1;
+ info_hdr.bit_count = 24;
+
+ int pixels = width * height;
+
+ image = new(__FILE__,__LINE__) DWORD [pixels];
+
+ if (image && pixels) {
+ for (int i = 0; i < pixels; i++)
+ image[i] = hibits[i];
+ }
+}
+
+BmpImage::~BmpImage()
+{
+ delete [] image;
+}
+
+// +--------------------------------------------------------------------+
+
+int BmpImage::Load(char *filename)
+{
+ int status = BMP_INVALID;
+ FILE* f;
+
+ f = fopen(filename,"rb");
+ if (f == NULL)
+ return BMP_NOFILE;
+
+ fread(&file_hdr.type, sizeof(WORD), 1, f);
+ fread(&file_hdr.size, sizeof(DWORD), 1, f);
+ fread(&file_hdr.rsvd1, sizeof(WORD), 1, f);
+ fread(&file_hdr.rsvd2, sizeof(WORD), 1, f);
+ fread(&file_hdr.offset, sizeof(DWORD), 1, f);
+ fread(&info_hdr, BMP_INFO_HDR_SIZE, 1, f);
+
+ if (info_hdr.width > 32768 || info_hdr.height > 32768 ||
+ (info_hdr.width&3) || (info_hdr.height&3) ||
+ info_hdr.compression != 0) {
+ fclose(f);
+ return BMP_INVALID;
+ }
+
+ width = (WORD) info_hdr.width;
+ height = (WORD) info_hdr.height;
+
+ // read 256 color BMP file
+ if (info_hdr.bit_count == 8) {
+ fread(palette, sizeof(palette), 1, f);
+
+ int pixels = width*height;
+
+ delete [] image;
+ image = new(__FILE__,__LINE__) DWORD[pixels];
+ if (image == NULL)
+ return BMP_NOMEM;
+
+ for (int row = height-1; row >= 0; row--) {
+ for (int col = 0; col < width; col++) {
+ BYTE index = fgetc(f);
+ image[row*width+col] = palette[index];
+ }
+ }
+
+ status = BMP_OK;
+ }
+
+ // read 24-bit (true COLOR) BMP file
+ else if (info_hdr.bit_count == 24) {
+ int pixels = width*height;
+
+ delete [] image;
+ image = new(__FILE__,__LINE__) DWORD[pixels];
+ if (image == NULL)
+ return BMP_NOMEM;
+
+ for (int row = height-1; row >= 0; row--) {
+ for (int col = 0; col < width; col++) {
+ DWORD blue = fgetc(f);
+ DWORD green = fgetc(f);
+ DWORD red = fgetc(f);
+
+ image[row*width+col] = 0xff000000 | (red << 16) | (green << 8) | blue;
+ }
+ }
+
+ status = BMP_OK;
+ }
+
+ fclose(f);
+ return status;
+}
+
+// +--------------------------------------------------------------------+
+
+int BmpImage::LoadBuffer(unsigned char* buf, int len)
+{
+ int status = BMP_INVALID;
+ BYTE* fp;
+
+ if (buf == NULL)
+ return BMP_NOFILE;
+
+ fp = buf;
+ memcpy(&info_hdr, buf + BMP_FILE_HDR_SIZE, BMP_INFO_HDR_SIZE);
+ fp += BMP_FILE_HDR_SIZE + BMP_INFO_HDR_SIZE;
+
+ if (info_hdr.width > 32768 || info_hdr.height > 32768 ||
+ (info_hdr.width&3) || (info_hdr.height&3) ||
+ info_hdr.compression != 0) {
+ return BMP_INVALID;
+ }
+
+ width = (WORD) info_hdr.width;
+ height = (WORD) info_hdr.height;
+
+ // read 256 color BMP file
+ if (info_hdr.bit_count == 8) {
+ memcpy(palette, fp, sizeof(palette));
+ fp += sizeof(palette);
+
+ int pixels = width*height;
+
+ delete [] image;
+ image = new(__FILE__,__LINE__) DWORD[pixels];
+ if (image == NULL)
+ return BMP_NOMEM;
+
+ for (int row = height-1; row >= 0; row--) {
+ for (int col = 0; col < width; col++) {
+ BYTE index = *fp++;
+ image[row*width+col] = palette[index];
+ }
+ }
+
+ status = BMP_OK;
+ }
+
+ // read 24-bit (true COLOR) BMP file
+ else if (info_hdr.bit_count == 24) {
+ int pixels = width*height;
+
+ delete [] image;
+ image = new(__FILE__,__LINE__) DWORD[pixels];
+ if (image == NULL)
+ return BMP_NOMEM;
+
+ for (int row = height-1; row >= 0; row--) {
+ for (int col = 0; col < width; col++) {
+ DWORD blue = *fp++;
+ DWORD green = *fp++;
+ DWORD red = *fp++;
+
+ image[row*width+col] = 0xff000000 | (red << 16) | (green << 8) | blue;
+ }
+ }
+
+ status = BMP_OK;
+ }
+
+ return status;
+}
+
+// +--------------------------------------------------------------------+
+
+int BmpImage::Save(char *filename)
+{
+ int status = BMP_INVALID;
+ FILE* f;
+
+ f = fopen(filename,"wb");
+ if (f == NULL)
+ return BMP_NOFILE;
+
+ info_hdr.bit_count = 24;
+ info_hdr.compression = 0;
+
+ fwrite(&file_hdr.type, sizeof(WORD), 1, f);
+ fwrite(&file_hdr.size, sizeof(DWORD), 1, f);
+ fwrite(&file_hdr.rsvd1, sizeof(WORD), 1, f);
+ fwrite(&file_hdr.rsvd2, sizeof(WORD), 1, f);
+ fwrite(&file_hdr.offset, sizeof(DWORD), 1, f);
+ fwrite(&info_hdr, BMP_INFO_HDR_SIZE, 1, f);
+
+ // write 24-bit (TRUE COLOR) BMP file
+ for (int row = height-1; row >= 0; row--) {
+ for (int col = 0; col < width; col++) {
+ DWORD pixel = image[row*width+col];
+
+ BYTE blue = (BYTE) ((pixel & 0x000000ff) >> 0);
+ BYTE green = (BYTE) ((pixel & 0x0000ff00) >> 8);
+ BYTE red = (BYTE) ((pixel & 0x00ff0000) >> 16);
+
+ fwrite(&blue, 1, 1, f);
+ fwrite(&green, 1, 1, f);
+ fwrite(&red, 1, 1, f);
+ }
+ }
+
+ status = BMP_OK;
+
+ fclose(f);
+ return status;
+}
+
diff --git a/nGenEx/Bmp.h b/nGenEx/Bmp.h
new file mode 100644
index 0000000..b4b0762
--- /dev/null
+++ b/nGenEx/Bmp.h
@@ -0,0 +1,76 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Bmp.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ BMP image file loader
+*/
+
+#ifndef BMP_H
+#define BMP_H
+
+// +--------------------------------------------------------------------+
+
+enum { BMP_OK, BMP_NOMEM, BMP_INVALID, BMP_NOFILE };
+
+struct BmpFileHeader
+{
+ WORD type;
+ DWORD size;
+ WORD rsvd1;
+ WORD rsvd2;
+ DWORD offset;
+};
+
+struct BmpInfoHeader
+{
+ DWORD hdr_size;
+ DWORD width;
+ DWORD height;
+ WORD planes;
+ WORD bit_count;
+ DWORD compression;
+ DWORD img_size;
+ DWORD x_pixels_per_meter;
+ DWORD y_pixels_per_meter;
+ DWORD colors_used;
+ DWORD colors_important;
+};
+
+const int BMP_FILE_HDR_SIZE = 14;
+const int BMP_INFO_HDR_SIZE = 40;
+
+// +--------------------------------------------------------------------+
+
+struct BmpImage
+{
+ static const char* TYPENAME() { return "BmpImage"; }
+
+ BmpImage(short w, short h, unsigned long* hibits);
+
+ BmpImage();
+ ~BmpImage();
+
+ int Load(char *filename);
+ int Save(char *filename);
+
+ int LoadBuffer(unsigned char* buf, int len);
+
+ BmpFileHeader file_hdr;
+ BmpInfoHeader info_hdr;
+ DWORD palette[256];
+ DWORD* image;
+ WORD width;
+ WORD height;
+};
+
+// +--------------------------------------------------------------------+
+
+
+#endif BMP_H
diff --git a/nGenEx/Bolt.cpp b/nGenEx/Bolt.cpp
new file mode 100644
index 0000000..af758b8
--- /dev/null
+++ b/nGenEx/Bolt.cpp
@@ -0,0 +1,176 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Bolt.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ 3D Bolt (Polygon) Object
+*/
+
+#include "MemDebug.h"
+#include "Bolt.h"
+#include "Bitmap.h"
+#include "Camera.h"
+#include "Video.h"
+
+void Print(const char* fmt, ...);
+
+// +--------------------------------------------------------------------+
+
+Bolt::Bolt(double len, double wid, Bitmap* tex, int share)
+ : vset(4), poly(0), texture(tex),
+ length(len), width(wid), shade(1.0), vpn(0, 1, 0), shared(share)
+{
+ trans = true;
+
+ loc = Vec3(0.0f, 0.0f, 1000.0f);
+
+ vset.nverts = 4;
+
+ vset.loc[0] = Point( width, 0, 1000);
+ vset.loc[1] = Point( width, -length, 1000);
+ vset.loc[2] = Point(-width, -length, 1000);
+ vset.loc[3] = Point(-width, 0, 1000);
+
+ vset.tu[0] = 0.0f;
+ vset.tv[0] = 0.0f;
+ vset.tu[1] = 1.0f;
+ vset.tv[1] = 0.0f;
+ vset.tu[2] = 1.0f;
+ vset.tv[2] = 1.0f;
+ vset.tu[3] = 0.0f;
+ vset.tv[3] = 1.0f;
+
+ Plane plane(vset.loc[0], vset.loc[1], vset.loc[2]);
+
+ for (int i = 0; i < 4; i++) {
+ vset.nrm[i] = plane.normal;
+ }
+
+ mtl.Ka = Color::White;
+ mtl.Kd = Color::White;
+ mtl.Ks = Color::Black;
+ mtl.Ke = Color::White;
+ mtl.tex_diffuse = texture;
+ mtl.tex_emissive = texture;
+ mtl.blend = Video::BLEND_ADDITIVE;
+
+ poly.nverts = 4;
+ poly.vertex_set = &vset;
+ poly.material = &mtl;
+ poly.verts[0] = 0;
+ poly.verts[1] = 1;
+ poly.verts[2] = 2;
+ poly.verts[3] = 3;
+
+ radius = (float) ((length>width) ? (length) : (width*2));
+
+ if (texture) {
+ strncpy(name, texture->GetFilename(), 31);
+ name[31] = 0;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Bolt::~Bolt()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bolt::Render(Video* video, DWORD flags)
+{
+ if ((flags & RENDER_ADDITIVE) == 0)
+ return;
+
+ if (visible && !hidden && video && life) {
+ const Camera* camera = video->GetCamera();
+
+ Point head = loc;
+ Point tail = origin;
+ Point vtail = tail - head;
+ Point vcam = camera->Pos() - loc;
+ Point vtmp = vcam.cross(vtail);
+ vtmp.Normalize();
+ Point vlat = vtmp * -width;
+ Vec3 vnrm = camera->vpn() * -1;
+
+ vset.loc[0] = head + vlat;
+ vset.loc[1] = tail + vlat;
+ vset.loc[2] = tail - vlat;
+ vset.loc[3] = head - vlat;
+
+ vset.nrm[0] = vnrm;
+ vset.nrm[1] = vnrm;
+ vset.nrm[2] = vnrm;
+ vset.nrm[3] = vnrm;
+
+ ColorValue white((float) shade, (float) shade, (float) shade);
+ mtl.Ka = white;
+ mtl.Kd = white;
+ mtl.Ks = Color::Black;
+ mtl.Ke = white;
+
+ video->DrawPolys(1, &poly);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bolt::Update()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bolt::TranslateBy(const Point& ref)
+{
+ loc = loc - ref;
+ origin = origin - ref;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Bolt::SetOrientation(const Matrix& o)
+{
+ vpn = Point(o(2,0), o(2,1), o(2,2));
+ origin = loc + (vpn * -length);
+}
+
+void
+Bolt::SetDirection(const Point& v)
+{
+ vpn = v;
+ origin = loc + (vpn * -length);
+}
+
+void
+Bolt::SetEndPoints(const Point& from, const Point& to)
+{
+ loc = to;
+ origin = from;
+ vpn = to - from;
+ length = vpn.Normalize();
+ radius = (float) length;
+}
+
+void
+Bolt::SetTextureOffset(double from, double to)
+{
+ vset.tu[0] = (float) from;
+ vset.tu[1] = (float) to;
+ vset.tu[2] = (float) to;
+ vset.tu[3] = (float) from;
+}
+
+
diff --git a/nGenEx/Bolt.h b/nGenEx/Bolt.h
new file mode 100644
index 0000000..3cf3842
--- /dev/null
+++ b/nGenEx/Bolt.h
@@ -0,0 +1,65 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Bolt.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ 3D Bolt (Polygon) Object
+*/
+
+#ifndef Bolt_h
+#define Bolt_h
+
+#include "Graphic.h"
+#include "Polygon.h"
+
+// +--------------------------------------------------------------------+
+
+class Bolt : public Graphic
+{
+public:
+ static const char* TYPENAME() { return "Bolt"; }
+
+ Bolt(double len=16, double wid=1, Bitmap* tex=0, int share=0);
+ virtual ~Bolt();
+
+ // operations
+ virtual void Render(Video* video, DWORD flags);
+ virtual void Update();
+
+ // accessors / mutators
+ virtual void SetOrientation(const Matrix& o);
+ void SetDirection(const Point& v);
+ void SetEndPoints(const Point& from, const Point& to);
+ void SetTextureOffset(double from, double to);
+
+ virtual void TranslateBy(const Point& ref);
+
+ double Shade() const { return shade; }
+ void SetShade(double s) { shade = s; }
+ virtual bool IsBolt() const { return true; }
+
+protected:
+ double length;
+ double width;
+ double shade;
+
+ Poly poly;
+ Material mtl;
+ VertexSet vset;
+ Bitmap* texture;
+ int shared;
+
+ Point vpn;
+ Point origin;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif Bolt_h
+
diff --git a/nGenEx/Button.cpp b/nGenEx/Button.cpp
new file mode 100644
index 0000000..2a52b57
--- /dev/null
+++ b/nGenEx/Button.cpp
@@ -0,0 +1,687 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Button.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Button class
+*/
+
+#include "MemDebug.h"
+#include "Button.h"
+#include "Video.h"
+#include "Bitmap.h"
+#include "Font.h"
+#include "Sound.h"
+#include "DataLoader.h"
+
+// +--------------------------------------------------------------------+
+
+static Sound* button_sound = 0;
+static Sound* click_sound = 0;
+static Sound* swish_sound = 0;
+static Sound* chirp_sound = 0;
+static Sound* accept_sound = 0;
+static Sound* reject_sound = 0;
+static Sound* confirm_sound = 0;
+static Sound* list_select_sound = 0;
+static Sound* list_scroll_sound = 0;
+static Sound* list_drop_sound = 0;
+static Sound* combo_open_sound = 0;
+static Sound* combo_close_sound = 0;
+static Sound* combo_hilite_sound = 0;
+static Sound* combo_select_sound = 0;
+static Sound* menu_open_sound = 0;
+static Sound* menu_close_sound = 0;
+static Sound* menu_select_sound = 0;
+static Sound* menu_hilite_sound = 0;
+
+static int gui_volume = 0;
+
+// +--------------------------------------------------------------------+
+
+Button::Button(Screen* s, int ax, int ay, int aw, int ah, DWORD aid)
+ : ActiveWindow(s, ax, ay, aw, ah, aid)
+{
+ animated = true;
+ bevel_width = 5;
+ border = false;
+ button_state = 0;
+ drop_shadow = false;
+ sticky = false;
+ picture_loc = 1;
+ captured = 0;
+ pre_state = 0;
+ text_align = DT_CENTER;
+
+ standard_image = 0;
+ activated_image = 0;
+ transition_image = 0;
+
+ char buf[32];
+ sprintf(buf, "Button %d", id);
+ desc = buf;
+}
+
+Button::Button(ActiveWindow* p, int ax, int ay, int aw, int ah, DWORD aid)
+ : ActiveWindow(p->GetScreen(), ax, ay, aw, ah, aid, 0, p)
+{
+ animated = true;
+ bevel_width = 5;
+ border = false;
+ button_state = 0;
+ drop_shadow = false;
+ sticky = false;
+ picture_loc = 1;
+ captured = 0;
+ pre_state = 0;
+ text_align = DT_CENTER;
+
+ standard_image = 0;
+ activated_image = 0;
+ transition_image = 0;
+
+ char buf[32];
+ sprintf(buf, "Button %d", id);
+ desc = buf;
+}
+
+// +--------------------------------------------------------------------+
+
+Button::~Button()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+static void LoadInterfaceSound(DataLoader* loader, const char* wave, Sound*& s)
+{
+ loader->LoadSound(wave, s, 0, true); // optional sound effect
+
+ if (s)
+ s->SetFlags(s->GetFlags() | Sound::INTERFACE);
+}
+
+void
+Button::Initialize()
+{
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath("Sounds/");
+
+ LoadInterfaceSound(loader, "button.wav", button_sound);
+ LoadInterfaceSound(loader, "click.wav", click_sound);
+ LoadInterfaceSound(loader, "swish.wav", swish_sound);
+ LoadInterfaceSound(loader, "chirp.wav", chirp_sound);
+ LoadInterfaceSound(loader, "accept.wav", accept_sound);
+ LoadInterfaceSound(loader, "reject.wav", reject_sound);
+ LoadInterfaceSound(loader, "confirm.wav", confirm_sound);
+ LoadInterfaceSound(loader, "list_select.wav", list_select_sound);
+ LoadInterfaceSound(loader, "list_scroll.wav", list_scroll_sound);
+ LoadInterfaceSound(loader, "list_drop.wav", list_drop_sound);
+ LoadInterfaceSound(loader, "combo_open.wav", combo_open_sound);
+ LoadInterfaceSound(loader, "combo_close.wav", combo_close_sound);
+ LoadInterfaceSound(loader, "combo_hilite.wav", combo_hilite_sound);
+ LoadInterfaceSound(loader, "combo_select.wav", combo_select_sound);
+ LoadInterfaceSound(loader, "menu_open.wav", menu_open_sound);
+ LoadInterfaceSound(loader, "menu_close.wav", menu_close_sound);
+ LoadInterfaceSound(loader, "menu_select.wav", menu_select_sound);
+ LoadInterfaceSound(loader, "menu_hilite.wav", menu_hilite_sound);
+
+ loader->SetDataPath(0);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Button::Close()
+{
+ delete button_sound;
+ delete click_sound;
+ delete swish_sound;
+ delete chirp_sound;
+ delete accept_sound;
+ delete reject_sound;
+ delete confirm_sound;
+ delete list_select_sound;
+ delete list_scroll_sound;
+ delete list_drop_sound;
+ delete combo_open_sound;
+ delete combo_close_sound;
+ delete combo_hilite_sound;
+ delete combo_select_sound;
+ delete menu_open_sound;
+ delete menu_close_sound;
+ delete menu_select_sound;
+ delete menu_hilite_sound;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Button::Draw()
+{
+ if (!IsShown()) return;
+
+ int x = 0;
+ int y = 0;
+ int w = rect.w;
+ int h = rect.h;
+ int img_w = picture.Width();
+ int img_h = picture.Height();
+
+ float old_alpha = alpha;
+
+ if (!enabled)
+ SetAlpha(0.35);
+
+ Rect btn_rect(x,y,w,h);
+
+ if (!transparent) {
+ if (standard_image) {
+ if (!enabled) {
+ texture = standard_image;
+ }
+
+ else {
+ switch (button_state) {
+ case -1:
+ texture = activated_image;
+ break;
+
+ default:
+ case 0:
+ texture = standard_image;
+ break;
+
+ case 1:
+ if (sticky)
+ texture = activated_image;
+ else
+ texture = transition_image;
+ break;
+
+ case 2:
+ texture = transition_image;
+ break;
+ }
+ }
+
+ if (!texture)
+ texture = standard_image;
+
+ DrawTextureGrid();
+ }
+
+ else {
+ FillRect(0, 0, w, h, ShadeColor(back_color, 1.0));
+ DrawStyleRect(0, 0, w, h, style);
+ }
+ }
+
+ // draw the picture (if any)
+ if (picture.Width()) {
+ Rect irect = CalcPictureRect();
+ DrawImage(&picture, irect);
+ }
+
+ // draw text here:
+ if (font && text.length()) {
+ Rect label_rect = CalcLabelRect(img_w,img_h);
+ int vert_space = label_rect.h;
+ int horz_space = label_rect.w;
+ int align = DT_WORDBREAK | text_align;
+
+ DrawText(text.data(), 0, label_rect, DT_CALCRECT | align);
+ vert_space = (vert_space - label_rect.h)/2;
+
+ label_rect.w = horz_space;
+
+ if (vert_space > 0)
+ label_rect.y += vert_space;
+
+ if (animated && button_state > 0) {
+ label_rect.x += button_state;
+ label_rect.y += button_state;
+ }
+
+ if (drop_shadow) {
+ label_rect.x++;
+ label_rect.y++;
+
+ font->SetColor(back_color);
+ DrawText(text.data(), text.length(), label_rect, align);
+
+ label_rect.x--;
+ label_rect.y--;
+ }
+
+ font->SetColor(fore_color);
+ DrawText(text.data(), text.length(), label_rect, align);
+ }
+
+ if (!enabled)
+ SetAlpha(old_alpha);
+}
+
+Rect Button::CalcLabelRect(int img_w, int img_h)
+{
+ // fit the text in the bevel:
+ Rect label_rect;
+ label_rect.x = 0;
+ label_rect.y = 0;
+ label_rect.w = rect.w;
+ label_rect.h = rect.h;
+
+ if (text_align == DT_LEFT)
+ label_rect.Deflate(bevel_width + 8, bevel_width + 1);
+ else
+ label_rect.Deflate(bevel_width + 1, bevel_width + 1);
+
+ // and around the picture, if any:
+ if (img_h != 0)
+ {
+ switch (picture_loc)
+ {
+ default:
+ case 0: // the four corner positions
+ case 2: // and the center position
+ case 4: // don't affect the text position
+ case 6:
+ case 8:
+ break;
+
+ case 1: // north
+ label_rect.y += img_h;
+ label_rect.h -= img_h;
+ break;
+
+ case 3: // west
+ label_rect.x += img_w;
+ label_rect.w -= img_w;
+ break;
+
+ case 5: // east
+ label_rect.w -= img_w;
+ break;
+
+ case 7: // south
+ label_rect.h -= img_h;
+ break;
+ }
+ }
+
+ return label_rect;
+}
+
+// +--------------------------------------------------------------------+
+
+Rect
+Button::CalcPictureRect()
+{
+ int w = rect.w;
+ int h = rect.h;
+ int img_w = picture.Width();
+ int img_h = picture.Height();
+
+ if (img_h > h) img_h = h-2;
+ if (img_w > w) img_w = w-2;
+
+ int img_x_offset = bevel_width;
+ int img_y_offset = bevel_width;
+
+ switch (picture_loc)
+ {
+ default:
+ // TOP ROW:
+ case 0: break;
+
+ case 1: img_x_offset = (w/2-img_w/2);
+ break;
+
+ case 2: img_x_offset = w - img_w - bevel_width;
+ break;
+
+ // MIDDLE ROW:
+ case 3: img_y_offset = (h/2-img_h/2);
+ break;
+ case 4: img_x_offset = (w/2-img_w/2);
+ img_y_offset = (h/2-img_h/2);
+ break;
+ case 5: img_x_offset = w - img_w - bevel_width;
+ img_y_offset = (h/2-img_h/2);
+ break;
+
+ // BOTTOM ROW:
+ case 6:
+ img_y_offset = h - img_h - bevel_width;
+ break;
+ case 7: img_x_offset = (w/2-img_w/2);
+ img_y_offset = h - img_h - bevel_width;
+ break;
+ case 8: img_x_offset = w - img_w - bevel_width;
+ img_y_offset = h - img_h - bevel_width;
+ break;
+ }
+
+ Rect img_rect;
+ img_rect.x = img_x_offset;
+ img_rect.y = img_y_offset;
+
+ if (animated && button_state > 0) {
+ img_rect.x += button_state;
+ img_rect.y += button_state;
+ }
+
+ img_rect.w = img_w;
+ img_rect.h = img_h;
+
+ return img_rect;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Button::DrawImage(Bitmap* bmp, const Rect& irect)
+{
+ if (bmp) {
+ DrawBitmap(irect.x,
+ irect.y,
+ irect.x + irect.w,
+ irect.y + irect.h,
+ bmp,
+ Video::BLEND_ALPHA);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+int Button::OnMouseMove(int x, int y)
+{
+ bool dirty = false;
+
+ if (captured)
+ {
+ ActiveWindow* test = GetCapture();
+
+ if (test != this)
+ {
+ captured = false;
+ button_state = pre_state;
+ dirty = true;
+ }
+
+ else if (sticky)
+ {
+ if (button_state == 2)
+ {
+ if (!rect.Contains(x,y))
+ {
+ button_state = pre_state;
+ dirty = true;
+ }
+ }
+ else
+ {
+ if (rect.Contains(x,y))
+ {
+ button_state = 2;
+ dirty = true;
+ }
+ }
+ }
+ else
+ {
+ if (button_state == 1)
+ {
+ if (!rect.Contains(x,y))
+ {
+ button_state = 0;
+ dirty = true;
+ }
+ }
+ else
+ {
+ if (rect.Contains(x,y))
+ {
+ button_state = 1;
+ dirty = true;
+ }
+ }
+ }
+ }
+
+ return ActiveWindow::OnMouseMove(x,y);
+}
+
+int Button::OnLButtonDown(int x, int y)
+{
+ if (!captured)
+ captured = SetCapture();
+
+ if (sticky)
+ button_state = 2;
+ else
+ button_state = 1;
+
+ return ActiveWindow::OnLButtonDown(x,y);
+}
+
+int Button::OnLButtonUp(int x, int y)
+{
+ if (captured) {
+ ReleaseCapture();
+ captured = 0;
+ }
+
+ button_state = pre_state;
+ return ActiveWindow::OnLButtonUp(x,y);
+}
+
+void Button::SetVolume(int vol)
+{
+ if (vol >= -10000 && vol <= 0)
+ gui_volume = vol;
+}
+
+void Button::PlaySound(int n)
+{
+ Sound* sound = 0;
+
+ switch (n) {
+ default:
+ case SND_BUTTON: if (button_sound) sound = button_sound->Duplicate(); break;
+ case SND_CLICK: if (click_sound) sound = click_sound->Duplicate(); break;
+ case SND_SWISH: if (swish_sound) sound = swish_sound->Duplicate(); break;
+ case SND_CHIRP: if (chirp_sound) sound = chirp_sound->Duplicate(); break;
+ case SND_ACCEPT: if (accept_sound) sound = accept_sound->Duplicate(); break;
+ case SND_REJECT: if (reject_sound) sound = reject_sound->Duplicate(); break;
+ case SND_CONFIRM: if (confirm_sound) sound = confirm_sound->Duplicate(); break;
+ case SND_LIST_SELECT: if (list_select_sound) sound = list_select_sound->Duplicate(); break;
+ case SND_LIST_SCROLL: if (list_scroll_sound) sound = list_scroll_sound->Duplicate(); break;
+ case SND_LIST_DROP: if (list_drop_sound) sound = list_drop_sound->Duplicate(); break;
+ case SND_COMBO_OPEN: if (combo_open_sound) sound = combo_open_sound->Duplicate(); break;
+ case SND_COMBO_CLOSE: if (combo_close_sound) sound = combo_close_sound->Duplicate(); break;
+ case SND_COMBO_HILITE: if (combo_hilite_sound) sound = combo_hilite_sound->Duplicate(); break;
+ case SND_COMBO_SELECT: if (combo_select_sound) sound = combo_select_sound->Duplicate(); break;
+ case SND_MENU_OPEN: if (menu_open_sound) sound = menu_open_sound->Duplicate(); break;
+ case SND_MENU_CLOSE: if (menu_close_sound) sound = menu_close_sound->Duplicate(); break;
+ case SND_MENU_SELECT: if (menu_select_sound) sound = menu_select_sound->Duplicate(); break;
+ case SND_MENU_HILITE: if (menu_hilite_sound) sound = menu_hilite_sound->Duplicate(); break;
+ }
+
+ if (sound) {
+ sound->SetVolume(gui_volume);
+ sound->Play();
+ }
+}
+
+int Button::OnClick()
+{
+ PlaySound(SND_BUTTON);
+
+ if (sticky)
+ button_state = !pre_state;
+
+ pre_state = button_state;
+
+ return ActiveWindow::OnClick();
+}
+
+int Button::OnMouseEnter(int mx, int my)
+{
+ if (button_state >= 0)
+ pre_state = button_state;
+
+ if (button_state == 0)
+ button_state = -1;
+
+ if (IsEnabled() && IsShown())
+ PlaySound(SND_SWISH);
+
+ return ActiveWindow::OnMouseEnter(mx, my);
+}
+
+int Button::OnMouseExit(int mx, int my)
+{
+ if (button_state == -1)
+ button_state = pre_state;
+
+ return ActiveWindow::OnMouseExit(mx, my);
+}
+
+// +--------------------------------------------------------------------+
+
+void Button::SetStandardImage(Bitmap* img)
+{
+ standard_image = img;
+ texture = standard_image;
+}
+
+void Button::SetActivatedImage(Bitmap* img)
+{
+ activated_image = img;
+}
+
+void Button::SetTransitionImage(Bitmap* img)
+{
+ transition_image = img;
+}
+
+// +--------------------------------------------------------------------+
+
+short Button::GetBevelWidth()
+{
+ return bevel_width;
+}
+
+void Button::SetBevelWidth(short nNewValue)
+{
+ if (nNewValue < 0) nNewValue = 0;
+ if (nNewValue > rect.w/2) nNewValue = rect.w/2;
+ bevel_width = nNewValue;
+}
+
+bool Button::GetBorder()
+{
+ return border;
+}
+
+void Button::SetBorder(bool bNewValue)
+{
+ border = bNewValue;
+}
+
+Color Button::GetBorderColor()
+{
+ return border_color;
+}
+
+void Button::SetBorderColor(Color newValue)
+{
+ border_color = newValue;
+}
+
+Color Button::GetActiveColor()
+{
+ return active_color;
+}
+
+void Button::SetActiveColor(Color newValue)
+{
+ active_color = newValue;
+}
+
+bool Button::GetAnimated()
+{
+ return animated;
+}
+
+void Button::SetAnimated(bool bNewValue)
+{
+ animated = bNewValue;
+}
+
+bool Button::GetDropShadow()
+{
+ return drop_shadow;
+}
+
+void Button::SetDropShadow(bool bNewValue)
+{
+ drop_shadow = bNewValue;
+}
+
+// +--------------------------------------------------------------------+
+
+short Button::GetButtonState()
+{
+ return button_state;
+}
+
+void Button::SetButtonState(short n)
+{
+ if (button_state != n && n >= -2 && n <= 2) {
+ button_state = n;
+ pre_state = n;
+ }
+}
+
+void Button::GetPicture(Bitmap& img)
+{
+ img.CopyBitmap(picture);
+}
+
+void Button::SetPicture(const Bitmap& img)
+{
+ picture.CopyBitmap(img);
+ picture.AutoMask();
+}
+
+short Button::GetPictureLocation()
+{
+ return picture_loc;
+}
+
+void Button::SetPictureLocation(short n)
+{
+ if (picture_loc != n && n >= 0 && n <= 8) {
+ picture_loc = n;
+ }
+}
+
+bool Button::GetSticky()
+{
+ return sticky;
+}
+
+void Button::SetSticky(bool n)
+{
+ if (sticky != n)
+ sticky = n;
+}
+
diff --git a/nGenEx/Button.h b/nGenEx/Button.h
new file mode 100644
index 0000000..ac16bfb
--- /dev/null
+++ b/nGenEx/Button.h
@@ -0,0 +1,121 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Button.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Button class
+*/
+
+#ifndef Button_h
+#define Button_h
+
+#include "Types.h"
+#include "ActiveWindow.h"
+#include "Bitmap.h"
+
+// +--------------------------------------------------------------------+
+
+class Button : public ActiveWindow
+{
+public:
+ enum SOUNDS {
+ SND_BUTTON,
+ SND_CLICK,
+ SND_SWISH,
+ SND_CHIRP,
+ SND_ACCEPT,
+ SND_REJECT,
+ SND_CONFIRM,
+ SND_LIST_SELECT,
+ SND_LIST_SCROLL,
+ SND_LIST_DROP,
+ SND_COMBO_OPEN,
+ SND_COMBO_CLOSE,
+ SND_COMBO_HILITE,
+ SND_COMBO_SELECT,
+ SND_MENU_OPEN,
+ SND_MENU_CLOSE,
+ SND_MENU_SELECT,
+ SND_MENU_HILITE
+ };
+
+ Button(Screen* s, int ax, int ay, int aw, int ah, DWORD id=0);
+ Button(ActiveWindow* p, int ax, int ay, int aw, int ah, DWORD id=0);
+ virtual ~Button();
+
+ static void Initialize();
+ static void Close();
+ static void PlaySound(int n=0);
+ static void SetVolume(int vol);
+
+ // Operations:
+ virtual void Draw(); // refresh backing store
+
+ // Event Target Interface:
+ virtual int OnMouseMove(int x, int y);
+ virtual int OnLButtonDown(int x, int y);
+ virtual int OnLButtonUp(int x, int y);
+ virtual int OnClick();
+ virtual int OnMouseEnter(int x, int y);
+ virtual int OnMouseExit(int x, int y);
+
+ // Property accessors:
+ Color GetActiveColor();
+ void SetActiveColor(Color c);
+ bool GetAnimated();
+ void SetAnimated(bool bNewValue);
+ short GetBevelWidth();
+ void SetBevelWidth(short nNewValue);
+ bool GetBorder();
+ void SetBorder(bool bNewValue);
+ Color GetBorderColor();
+ void SetBorderColor(Color c);
+ short GetButtonState();
+ void SetButtonState(short nNewValue);
+ bool GetDropShadow();
+ void SetDropShadow(bool bNewValue);
+ void GetPicture(Bitmap& img);
+ void SetPicture(const Bitmap& img);
+ short GetPictureLocation();
+ void SetPictureLocation(short nNewValue);
+ bool GetSticky();
+ void SetSticky(bool bNewValue);
+
+ void SetStandardImage(Bitmap* img);
+ void SetActivatedImage(Bitmap* img);
+ void SetTransitionImage(Bitmap* img);
+
+protected:
+ Rect CalcLabelRect(int img_w, int img_h);
+ Rect CalcPictureRect();
+ void DrawImage(Bitmap* bmp, const Rect& irect);
+
+ bool animated;
+ bool drop_shadow;
+ bool sticky;
+ bool border;
+
+ Color active_color;
+ Color border_color;
+
+ int captured;
+ int pre_state;
+ short bevel_width;
+ short button_state;
+
+ short picture_loc;
+ Bitmap picture;
+
+ Bitmap* standard_image; // state = 0
+ Bitmap* activated_image; // state = 1 (if sticky)
+ Bitmap* transition_image; // state = 2 (if sticky)
+};
+
+#endif Button_h
+
diff --git a/nGenEx/Camera.cpp b/nGenEx/Camera.cpp
new file mode 100644
index 0000000..bbc72dd
--- /dev/null
+++ b/nGenEx/Camera.cpp
@@ -0,0 +1,212 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Camera.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Camera Class - Position and Point of View
+*/
+
+#include "MemDebug.h"
+#include "Camera.h"
+
+// +--------------------------------------------------------------------+
+
+Camera::Camera(double x, double y, double z)
+ : pos(x,y,z)
+{ }
+
+Camera::~Camera()
+{ }
+
+// +--------------------------------------------------------------------+
+
+void
+Camera::MoveTo(double x, double y, double z)
+{
+ pos.x = x;
+ pos.y = y;
+ pos.z = z;
+}
+
+void
+Camera::MoveTo(const Point& p)
+{
+ pos.x = p.x;
+ pos.y = p.y;
+ pos.z = p.z;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Camera::MoveBy(double dx, double dy, double dz)
+{
+ pos.x += dx;
+ pos.y += dz;
+ pos.z += dy;
+}
+
+void
+Camera::MoveBy(const Point& p)
+{
+ pos.x += p.x;
+ pos.y += p.y;
+ pos.z += p.z;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Camera::Clone(const Camera& cam)
+{
+ pos = cam.pos;
+ orientation = cam.orientation;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Camera::LookAt(const Point& target, const Point& eye, const Point& up)
+{
+ Point zaxis = target - eye; zaxis.Normalize();
+ Point xaxis = up.cross(zaxis); xaxis.Normalize();
+ Point yaxis = zaxis.cross(xaxis); yaxis.Normalize();
+
+ orientation(0,0) = xaxis.x;
+ orientation(0,1) = xaxis.y;
+ orientation(0,2) = xaxis.z;
+
+ orientation(1,0) = yaxis.x;
+ orientation(1,1) = yaxis.y;
+ orientation(1,2) = yaxis.z;
+
+ orientation(2,0) = zaxis.x;
+ orientation(2,1) = zaxis.y;
+ orientation(2,2) = zaxis.z;
+
+ pos = eye;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Camera::LookAt(const Point& target)
+{
+ // No navel gazing:
+ if (target == Pos())
+ return;
+
+ Point tgt, tmp = target - Pos();
+
+ // Rotate into the view orientation:
+ tgt.x = (tmp * vrt());
+ tgt.y = (tmp * vup());
+ tgt.z = (tmp * vpn());
+
+ if (tgt.z == 0) {
+ Pitch(0.5);
+ Yaw(0.5);
+ LookAt(target);
+ return;
+ }
+
+ double az = atan(tgt.x/tgt.z);
+ double el = atan(tgt.y/tgt.z);
+
+ // if target is behind, offset by 180 degrees:
+ if (tgt.z < 0)
+ az -= PI;
+
+ Pitch(-el);
+ Yaw(az);
+
+ // roll to upright position:
+ double deflection = vrt().y;
+ while (fabs(deflection) > 0.001) {
+ double theta = asin(deflection/vrt().length());
+ Roll(-theta);
+
+ deflection = vrt().y;
+ }
+}
+
+
+// +--------------------------------------------------------------------+
+
+bool
+Camera::Padlock(const Point& target, double alimit, double e_lo, double e_hi)
+{
+ // No navel gazing:
+ if (target == Pos())
+ return false;
+
+ Point tgt, tmp = target - Pos();
+
+ // Rotate into the view orientation:
+ tgt.x = (tmp * vrt());
+ tgt.y = (tmp * vup());
+ tgt.z = (tmp * vpn());
+
+ if (tgt.z == 0) {
+ Yaw(0.1);
+
+ tgt.x = (tmp * vrt());
+ tgt.y = (tmp * vup());
+ tgt.z = (tmp * vpn());
+
+ if (tgt.z == 0)
+ return false;
+ }
+
+ bool locked = true;
+ double az = atan(tgt.x/tgt.z);
+ double orig = az;
+
+ // if target is behind, offset by 180 degrees:
+ if (tgt.z < 0)
+ az -= PI;
+
+ while (az > PI) az -= 2*PI;
+ while (az < -PI) az += 2*PI;
+
+ if (alimit > 0) {
+ if (az < -alimit) {
+ az = -alimit;
+ locked = false;
+ }
+ else if (az > alimit) {
+ az = alimit;
+ locked = false;
+ }
+ }
+
+ Yaw(az);
+
+ // Rotate into the new view orientation:
+ tgt.x = (tmp * vrt());
+ tgt.y = (tmp * vup());
+ tgt.z = (tmp * vpn());
+
+ double el = atan(tgt.y/tgt.z);
+
+ if (e_lo > 0 && el < -e_lo) {
+ el = -e_lo;
+ locked = false;
+ }
+
+ else if (e_hi > 0 && el > e_hi) {
+ el = e_hi;
+ locked = false;
+ }
+
+ Pitch(-el);
+
+ return locked;
+}
+
diff --git a/nGenEx/Camera.h b/nGenEx/Camera.h
new file mode 100644
index 0000000..498d112
--- /dev/null
+++ b/nGenEx/Camera.h
@@ -0,0 +1,61 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Camera.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Camera class - Position and Point of View
+*/
+
+#ifndef Camera_h
+#define Camera_h
+
+// +--------------------------------------------------------------------+
+
+#include "Types.h"
+#include "Geometry.h"
+
+// +--------------------------------------------------------------------+
+
+class Camera
+{
+public:
+ static const char* TYPENAME() { return "Camera"; }
+
+ Camera(double x=0.0, double y=0.0, double z=0.0);
+ virtual ~Camera();
+
+ void Aim(double roll, double pitch, double yaw) { orientation.Rotate(roll, pitch, yaw); }
+ void Roll(double roll) { orientation.Roll(roll); }
+ void Pitch(double pitch) { orientation.Pitch(pitch); }
+ void Yaw(double yaw) { orientation.Yaw(yaw); }
+
+ void MoveTo(double x, double y, double z);
+ void MoveTo(const Point& p);
+ void MoveBy(double dx, double dy, double dz);
+ void MoveBy(const Point& p);
+
+ void Clone(const Camera& cam);
+ void LookAt(const Point& target);
+ void LookAt(const Point& target, const Point& eye, const Point& up);
+ bool Padlock(const Point& target, double alimit=-1, double e_lo=-1, double e_hi=-1);
+
+ Point Pos() const { return pos; }
+ Point vrt() const { return Point(orientation(0,0), orientation(0,1), orientation(0,2)); }
+ Point vup() const { return Point(orientation(1,0), orientation(1,1), orientation(1,2)); }
+ Point vpn() const { return Point(orientation(2,0), orientation(2,1), orientation(2,2)); }
+
+ const Matrix& Orientation() const { return orientation; }
+
+protected:
+ Point pos;
+ Matrix orientation;
+};
+
+#endif Camera_h
+
diff --git a/nGenEx/CameraView.cpp b/nGenEx/CameraView.cpp
new file mode 100644
index 0000000..4b7d1d7
--- /dev/null
+++ b/nGenEx/CameraView.cpp
@@ -0,0 +1,805 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: CameraView.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ 3D Projection Camera View class
+ uses abstract PolyRender class to draw the triangles
+*/
+
+#include "MemDebug.h"
+#include "CameraView.h"
+#include "Color.h"
+#include "Window.h"
+#include "Scene.h"
+#include "Light.h"
+#include "Solid.h"
+#include "Shadow.h"
+#include "Sprite.h"
+#include "Video.h"
+#include "Bitmap.h"
+#include "Screen.h"
+#include "Game.h"
+
+// +--------------------------------------------------------------------+
+
+void Print(const char* fmt, ...);
+
+// +--------------------------------------------------------------------+
+
+static Camera emergency_cam;
+static Scene emergency_scene;
+
+// +--------------------------------------------------------------------+
+
+CameraView::CameraView(Window* c, Camera* cam, Scene* s)
+ : View(c), video(0), camera(cam), projector(c, cam), scene(s),
+ lens_flare_enable(0), halo_bitmap(0), infinite(0),
+ projection_type(Video::PROJECTION_PERSPECTIVE)
+{
+ elem_bitmap[0] = 0;
+ elem_bitmap[1] = 0;
+ elem_bitmap[2] = 0;
+
+ if (!camera)
+ camera = &emergency_cam;
+
+ if (!scene)
+ scene = &emergency_scene;
+
+ Rect r = window->GetRect();
+ width = r.w;
+ height = r.h;
+}
+
+CameraView::~CameraView()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CameraView::UseCamera(Camera* cam)
+{
+ if (cam)
+ camera = cam;
+ else
+ camera = &emergency_cam;
+
+ projector.UseCamera(camera);
+}
+
+void
+CameraView::UseScene(Scene* s)
+{
+ if (s)
+ scene = s;
+ else
+ scene = &emergency_scene;
+}
+
+void
+CameraView::SetFieldOfView(double fov)
+{
+ projector.SetFieldOfView(fov);
+}
+
+double
+CameraView::GetFieldOfView() const
+{
+ return projector.GetFieldOfView();
+}
+
+void
+CameraView::SetProjectionType(DWORD pt)
+{
+ projector.SetOrthogonal(pt == Video::PROJECTION_ORTHOGONAL);
+ projection_type = pt;
+}
+
+DWORD
+CameraView::GetProjectionType() const
+{
+ return projection_type;
+}
+
+void
+CameraView::OnWindowMove()
+{
+ Rect r = window->GetRect();
+ projector.UseWindow(window);
+
+ width = r.w;
+ height = r.h;
+}
+
+
+// +--------------------------------------------------------------------+
+// Enable or disable lens flare effect, and provide bitmaps for rendering
+// +--------------------------------------------------------------------+
+
+void
+CameraView::LensFlare(int on, double dim)
+{
+ lens_flare_enable = on;
+ lens_flare_dim = dim;
+}
+
+void
+CameraView::LensFlareElements(Bitmap* halo, Bitmap* e1, Bitmap* e2, Bitmap* e3)
+{
+ if (halo)
+ halo_bitmap = halo;
+
+ if (e1)
+ elem_bitmap[0] = e1;
+
+ if (e2)
+ elem_bitmap[1] = e2;
+
+ if (e3)
+ elem_bitmap[2] = e3;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+CameraView::SetInfinite(int i)
+{
+ int old = infinite;
+ infinite = i;
+ projector.SetInfinite(i);
+ return old;
+}
+
+// +--------------------------------------------------------------------+
+// Compute the Depth of a Graphic
+// +--------------------------------------------------------------------+
+
+void
+CameraView::FindDepth(Graphic* g)
+{
+ if (infinite) {
+ g->SetDepth(1.0e20f);
+ return;
+ }
+
+ // Translate into a viewpoint-relative coordinate
+ Vec3 loc = g->Location() - camera->Pos();
+
+ // Rotate into the view orientation
+ float z = (float) (loc * camera->vpn());
+ g->SetDepth(z);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CameraView::Refresh()
+{
+ // disabled:
+ if (camera == &emergency_cam)
+ return;
+
+ // prologue:
+ video = Video::GetInstance();
+ if (!video)
+ return;
+
+ int cw = window->Width();
+ int ch = window->Height();
+
+ cvrt = camera->vrt();
+ cvup = camera->vup();
+ cvpn = camera->vpn();
+
+ TranslateScene();
+ MarkVisibleObjects();
+
+ Rect old_rect;
+ video->GetWindowRect(old_rect);
+
+ video->SetCamera(camera);
+ video->SetWindowRect(window->GetRect());
+ video->SetProjection((float) GetFieldOfView(), 1.0f, 1.0e6f, projection_type);
+
+ // project and render:
+ RenderBackground();
+ RenderScene();
+ RenderForeground();
+ RenderSprites();
+ RenderLensFlare();
+
+ UnTranslateScene();
+
+ video->SetWindowRect(old_rect);
+}
+
+// +--------------------------------------------------------------------+
+// Translate all objects and lights to camera relative coordinates:
+// +--------------------------------------------------------------------+
+
+void
+CameraView::TranslateScene()
+{
+ camera_loc = camera->Pos();
+
+ ListIter<Graphic> g_iter = scene->Graphics();
+ while (++g_iter) {
+ Graphic* graphic = g_iter.value();
+
+ if (!graphic->IsInfinite())
+ graphic->TranslateBy(camera_loc);
+ }
+
+ g_iter.attach(scene->Foreground());
+ while (++g_iter) {
+ Graphic* graphic = g_iter.value();
+ graphic->TranslateBy(camera_loc);
+ }
+
+ g_iter.attach(scene->Sprites());
+ while (++g_iter) {
+ Graphic* graphic = g_iter.value();
+
+ if (!graphic->IsInfinite())
+ graphic->TranslateBy(camera_loc);
+ }
+
+ ListIter<Light> l_iter = scene->Lights();
+ while (++l_iter) {
+ Light* light = l_iter.value();
+ light->TranslateBy(camera_loc);
+ }
+
+ camera->MoveTo(0,0,0);
+}
+
+// +--------------------------------------------------------------------+
+// Translate all objects and lights back to original positions:
+// +--------------------------------------------------------------------+
+
+void
+CameraView::UnTranslateScene()
+{
+ Point reloc = -camera_loc;
+
+ ListIter<Graphic> g_iter = scene->Graphics();
+ while (++g_iter) {
+ Graphic* graphic = g_iter.value();
+
+ if (!graphic->IsInfinite())
+ graphic->TranslateBy(reloc);
+ }
+
+ g_iter.attach(scene->Foreground());
+ while (++g_iter) {
+ Graphic* graphic = g_iter.value();
+ graphic->TranslateBy(reloc);
+ }
+
+ g_iter.attach(scene->Sprites());
+ while (++g_iter) {
+ Graphic* graphic = g_iter.value();
+
+ if (!graphic->IsInfinite())
+ graphic->TranslateBy(reloc);
+ }
+
+ ListIter<Light> l_iter = scene->Lights();
+ while (++l_iter) {
+ Light* light = l_iter.value();
+ light->TranslateBy(reloc);
+ }
+
+ camera->MoveTo(camera_loc);
+}
+
+// +--------------------------------------------------------------------+
+// Mark visible objects
+// +--------------------------------------------------------------------+
+
+void
+CameraView::MarkVisibleObjects()
+{
+ projector.StartFrame();
+ graphics.clear();
+
+ ListIter<Graphic> graphic_iter = scene->Graphics();
+ while (++graphic_iter) {
+ Graphic* graphic = graphic_iter.value();
+
+ if (graphic->Hidden())
+ continue;
+
+ if (graphic->CheckVisibility(projector)) {
+ graphic->Update();
+ graphics.append(graphic);
+ }
+ else {
+ graphic->ProjectScreenRect(0);
+ }
+ }
+}
+
+void
+CameraView::MarkVisibleLights(Graphic* graphic, DWORD flags)
+{
+ if (flags < Graphic::RENDER_FIRST_LIGHT) {
+ flags = flags | Graphic::RENDER_FIRST_LIGHT | Graphic::RENDER_ADD_LIGHT;
+ }
+
+ if (graphic->IsVisible()) {
+ Vec3 eye = camera->Pos();
+
+ ListIter<Light> light_iter = scene->Lights();
+
+ while (++light_iter) {
+ Light* light = light_iter.value();
+ bool bright_enough = light->Type() == Light::LIGHT_DIRECTIONAL ||
+ light->Intensity() >= 1e9;
+
+ if (!bright_enough) {
+ Point test = graphic->Location() - light->Location();
+ if (test.length() < light->Intensity()*10)
+ bright_enough = true;
+ }
+
+ // turn off lights that won't be used this pass:
+ if (light->CastsShadow()) {
+ if ((flags & Graphic::RENDER_ADD_LIGHT) == 0)
+ bright_enough = false;
+ }
+ else {
+ if ((flags & Graphic::RENDER_FIRST_LIGHT) == 0)
+ bright_enough = false;
+ }
+
+ double obs_radius = graphic->Radius();
+ if (obs_radius < 100)
+ obs_radius = 100;
+
+ light->SetActive(bright_enough);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CameraView::RenderBackground()
+{
+ if (scene->Background().isEmpty()) return;
+
+ video->SetRenderState(Video::FILL_MODE, Video::FILL_SOLID);
+ video->SetRenderState(Video::Z_ENABLE, FALSE);
+ video->SetRenderState(Video::Z_WRITE_ENABLE, FALSE);
+ video->SetRenderState(Video::STENCIL_ENABLE, FALSE);
+ video->SetRenderState(Video::LIGHTING_ENABLE, TRUE);
+
+ // solid items:
+ ListIter<Graphic> iter = scene->Background();
+ while (++iter) {
+ Graphic* g = iter.value();
+
+ if (!g->Hidden())
+ Render(g, Graphic::RENDER_SOLID);
+ }
+
+ // blended items:
+ iter.reset();
+ while (++iter) {
+ Graphic* g = iter.value();
+
+ if (!g->Hidden())
+ Render(g, Graphic::RENDER_ALPHA);
+ }
+
+ // glowing items:
+ iter.reset();
+ while (++iter) {
+ Graphic* g = iter.value();
+
+ if (!g->Hidden())
+ Render(g, Graphic::RENDER_ADDITIVE);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CameraView::RenderForeground()
+{
+ bool foregroundVisible = false;
+
+ ListIter<Graphic> iter = scene->Foreground();
+ while (++iter && !foregroundVisible) {
+ Graphic* g = iter.value();
+ if (g && !g->Hidden())
+ foregroundVisible = true;
+ }
+
+ if (!foregroundVisible)
+ return;
+
+ video->SetRenderState(Video::FILL_MODE, Video::FILL_SOLID);
+ video->SetRenderState(Video::Z_ENABLE, TRUE);
+ video->SetRenderState(Video::Z_WRITE_ENABLE, TRUE);
+ video->SetRenderState(Video::STENCIL_ENABLE, FALSE);
+ video->SetRenderState(Video::LIGHTING_ENABLE, TRUE);
+ video->SetProjection((float) GetFieldOfView(), 1.0f, 1.0e6f, projection_type);
+
+ if (video->IsShadowEnabled() || video->IsBumpMapEnabled()) {
+ // solid items, ambient and non-shadow lights:
+ iter.reset();
+ while (++iter) {
+ Graphic* g = iter.value();
+ Render(g, Graphic::RENDER_SOLID | Graphic::RENDER_FIRST_LIGHT);
+ }
+
+ video->SetAmbient(Color::Black);
+ video->SetRenderState(Video::LIGHTING_PASS, 2);
+
+ // solid items, shadow lights:
+ iter.reset();
+ while (++iter) {
+ Graphic* g = iter.value();
+ Render(g, Graphic::RENDER_SOLID | Graphic::RENDER_ADD_LIGHT);
+ }
+ }
+
+ else {
+ // solid items:
+ iter.reset();
+ while (++iter) {
+ Graphic* g = iter.value();
+ Render(g, Graphic::RENDER_SOLID);
+ }
+ }
+
+ video->SetAmbient(scene->Ambient());
+ video->SetRenderState(Video::LIGHTING_PASS, 0);
+ video->SetRenderState(Video::STENCIL_ENABLE, FALSE);
+ video->SetRenderState(Video::Z_ENABLE, TRUE);
+ video->SetRenderState(Video::Z_WRITE_ENABLE, FALSE);
+
+ // blended items:
+ iter.reset();
+ while (++iter) {
+ Graphic* g = iter.value();
+ Render(g, Graphic::RENDER_ALPHA);
+ g->ProjectScreenRect(&projector);
+ }
+
+ // glowing items:
+ iter.reset();
+ while (++iter) {
+ Graphic* g = iter.value();
+ Render(g, Graphic::RENDER_ADDITIVE);
+ g->ProjectScreenRect(&projector);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+CameraView::RenderSprites()
+{
+ if (scene->Sprites().isEmpty()) return;
+
+ video->SetRenderState(Video::FILL_MODE, Video::FILL_SOLID);
+ video->SetRenderState(Video::Z_ENABLE, TRUE);
+ video->SetRenderState(Video::Z_WRITE_ENABLE, FALSE);
+ video->SetRenderState(Video::STENCIL_ENABLE, FALSE);
+ video->SetRenderState(Video::LIGHTING_ENABLE, TRUE);
+
+ // compute depth:
+ ListIter<Graphic> iter = scene->Sprites();
+ while (++iter) {
+ Graphic* g = iter.value();
+ if (g && g->IsVisible() && !g->Hidden()) {
+ FindDepth(g);
+ }
+ }
+
+ // sort the list:
+ scene->Sprites().sort();
+
+ // blended items:
+ iter.reset();
+ while (++iter) {
+ Graphic* g = iter.value();
+ Render(g, Graphic::RENDER_ALPHA);
+ }
+
+ // glowing items:
+ iter.reset();
+ while (++iter) {
+ Graphic* g = iter.value();
+ Render(g, Graphic::RENDER_ADDITIVE);
+ }
+}
+
+// +--------------------------------------------------------------------+
+// Render the whole scene, sorted back to front
+// +--------------------------------------------------------------------+
+
+void
+CameraView::RenderScene()
+{
+ if (graphics.isEmpty()) return;
+
+ int i = 0;
+ int ngraphics = graphics.size();
+
+ // compute depth:
+ ListIter<Graphic> iter = graphics;
+ while (++iter) {
+ Graphic* g = iter.value();
+ if (g && !g->Hidden()) {
+ FindDepth(g);
+
+ if (g->IsSolid()) {
+ Solid* solid = (Solid*) g;
+
+ solid->SelectDetail(&projector);
+
+ if (video->IsShadowEnabled()) {
+ MarkVisibleLights(solid, Graphic::RENDER_ADD_LIGHT);
+ solid->UpdateShadows(scene->Lights());
+ }
+ }
+ }
+ }
+
+ // sort the list:
+ graphics.sort();
+
+ Graphic* g = graphics.last();
+ if (g->Depth() > 5e6) {
+ RenderSceneObjects(true);
+ video->ClearDepthBuffer();
+ }
+
+ RenderSceneObjects(false);
+}
+
+void
+CameraView::RenderSceneObjects(bool distant)
+{
+ ListIter<Graphic> iter = graphics;
+
+ video->SetAmbient(scene->Ambient());
+ video->SetRenderState(Video::FILL_MODE, Video::FILL_SOLID);
+ video->SetRenderState(Video::Z_ENABLE, TRUE);
+ video->SetRenderState(Video::Z_WRITE_ENABLE, TRUE);
+ video->SetRenderState(Video::LIGHTING_ENABLE, TRUE);
+
+ if (distant)
+ video->SetProjection((float) GetFieldOfView(), 5.0e6f, 1.0e12f, projection_type);
+ else
+ video->SetProjection((float) GetFieldOfView(), 1.0f, 1.0e6f, projection_type);
+
+ if (video->IsShadowEnabled() || video->IsBumpMapEnabled()) {
+ // solid items, ambient and non-shadow lights:
+ iter.reset();
+ while (++iter) {
+ Graphic* g = iter.value();
+
+ if (distant && g->Depth() > 5e6 || !distant && g->Depth() < 5e6) {
+ Render(g, Graphic::RENDER_SOLID | Graphic::RENDER_FIRST_LIGHT);
+ }
+ }
+
+ // send shadows to stencil buffer:
+ if (video->IsShadowEnabled()) {
+ iter.reset();
+ while (++iter) {
+ Graphic* g = iter.value();
+ if (distant && g->Depth() > 5e6 || !distant && g->Depth() < 5e6) {
+ if (g->IsSolid()) {
+ Solid* solid = (Solid*) g;
+
+ ListIter<Shadow> shadow_iter = solid->GetShadows();
+ while (++shadow_iter) {
+ Shadow* shadow = shadow_iter.value();
+ shadow->Render(video);
+ }
+ }
+ }
+ }
+ }
+
+ video->SetAmbient(Color::Black);
+ video->SetRenderState(Video::LIGHTING_PASS, 2);
+ video->SetRenderState(Video::STENCIL_ENABLE, TRUE);
+
+ // solid items, shadow lights:
+ iter.reset();
+ while (++iter) {
+ Graphic* g = iter.value();
+
+ if (distant && g->Depth() > 5e6 || !distant && g->Depth() < 5e6) {
+ Render(g, Graphic::RENDER_SOLID | Graphic::RENDER_ADD_LIGHT);
+ }
+ }
+ }
+
+ else {
+ // solid items:
+ iter.reset();
+ while (++iter) {
+ Graphic* g = iter.value();
+
+ if (distant && g->Depth() > 5e6 || !distant && g->Depth() < 5e6) {
+ Render(g, Graphic::RENDER_SOLID);
+ }
+ }
+ }
+
+ video->SetAmbient(scene->Ambient());
+ video->SetRenderState(Video::LIGHTING_PASS, 0);
+ video->SetRenderState(Video::STENCIL_ENABLE, FALSE);
+ video->SetRenderState(Video::Z_ENABLE, TRUE);
+ video->SetRenderState(Video::Z_WRITE_ENABLE, FALSE);
+
+ // blended items:
+ iter.reset();
+ while (++iter) {
+ Graphic* g = iter.value();
+
+ if (distant && g->Depth() > 5e6 || !distant && g->Depth() < 5e6) {
+ Render(g, Graphic::RENDER_ALPHA);
+ g->ProjectScreenRect(&projector);
+ }
+ }
+
+ // glowing items:
+ iter.reset();
+ while (++iter) {
+ Graphic* g = iter.value();
+
+ if (distant && g->Depth() > 5e6 || !distant && g->Depth() < 5e6) {
+ Render(g, Graphic::RENDER_ADDITIVE);
+ g->ProjectScreenRect(&projector);
+ }
+ }
+}
+
+void
+CameraView::Render(Graphic* g, DWORD flags)
+{
+ if (g && g->IsVisible() && !g->Hidden()) {
+ if (g->IsSolid()) {
+ MarkVisibleLights(g, flags);
+ video->SetLights(scene->Lights());
+ }
+
+ g->Render(video, flags);
+ }
+}
+
+// +--------------------------------------------------------------------+
+// Draw the lens flare effect, if enabled and light source visible
+// +--------------------------------------------------------------------+
+
+void
+CameraView::RenderLensFlare()
+{
+ if (!lens_flare_enable || lens_flare_dim < 0.01)
+ return;
+
+ if (!halo_bitmap)
+ return;
+
+ video->SetRenderState(Video::STENCIL_ENABLE, FALSE);
+ video->SetRenderState(Video::Z_ENABLE, FALSE);
+ video->SetRenderState(Video::Z_WRITE_ENABLE, FALSE);
+
+ Vec3 flare_pos;
+ Vec3 sun_pos;
+ Vec3 center((float)width/2.0f, (float)height/2.0f, 1.0f);
+ int flare_visible = 0;
+
+ ListIter<Light> light_iter = scene->Lights();
+ while (++light_iter) {
+ Light* light = light_iter.value();
+
+ if (!light->IsActive())
+ continue;
+
+ if (light->Type() == Light::LIGHT_DIRECTIONAL && light->Intensity() < 1)
+ continue;
+
+ double distance = (light->Location()-camera->Pos()).length();
+
+ // only do lens flare for the sun:
+ if (distance > 1e9) {
+ if (projector.IsVisible(light->Location(), 1.0f)) {
+ // FOUND IT: TRANSFORM/PROJECT FLARE LOCATION
+ Point sun_pos = light->Location();
+
+ if (light->CastsShadow() && scene->IsLightObscured(camera->Pos(), sun_pos, -1))
+ continue;
+
+ projector.Transform(sun_pos);
+
+ if (sun_pos.z < 100)
+ continue;
+
+ projector.Project(sun_pos, false);
+
+ int x = (int) (sun_pos.x);
+ int y = (int) (sun_pos.y);
+ int w = (int) (window->Width() / 4.0);
+ int h = w;
+
+ // halo:
+ window->DrawBitmap(x-w,y-h,x+w,y+h, halo_bitmap, Video::BLEND_ADDITIVE);
+
+ // lens elements:
+ if (elem_bitmap[0]) {
+ Point vector = center - sun_pos;
+ float vlen = (float) vector.length();
+ vector.Normalize();
+
+ static int nelem = 12;
+ static int elem_indx[] = { 0, 1, 1, 1, 0, 0, 0, 0, 2, 0, 0, 2 };
+ static float elem_dist[] = { -0.2f, 0.5f, 0.55f, 0.62f, 1.23f, 1.33f, 1.35f, 0.8f, 0.9f, 1.4f, 1.7f, 1.8f };
+ static float elem_size[] = { 0.3f, 0.2f, 0.4f, 0.3f, 0.4f, 0.2f, 0.6f, 0.1f, 0.1f, 1.6f, 1.0f, 0.2f };
+
+ for (int elem = 0; elem < nelem; elem++) {
+ Bitmap* img = elem_bitmap[elem_indx[elem]];
+
+ /***
+ if (elem == 10)
+ shade *= 0.5;
+ ***/
+
+ if (img == 0)
+ img = elem_bitmap[0];
+
+ flare_pos = sun_pos + (vector * elem_dist[elem] * vlen);
+ x = (int) (flare_pos.x);
+ y = (int) (flare_pos.y);
+ w = (int) (window->Width() / 8.0 * elem_size[elem]);
+ h = w;
+
+ window->DrawBitmap(x-w,y-h,x+w,y+h, img, Video::BLEND_ADDITIVE);
+ }
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+// Rotate and translate a plane in world space to view space.
+// +--------------------------------------------------------------------+
+
+void
+CameraView::WorldPlaneToView(Plane& plane)
+{
+ // Determine the distance from the viewpoint
+ Vec3 tnormal = plane.normal;
+
+ if (!infinite)
+ plane.distance -= (float) (camera->Pos() * tnormal);
+
+ // Rotate the normal into view orientation
+ plane.normal.x = tnormal * cvrt;
+ plane.normal.y = tnormal * cvup;
+ plane.normal.z = tnormal * cvpn;
+}
+
+void
+CameraView::SetDepthScale(float scale)
+{
+ projector.SetDepthScale(scale);
+}
diff --git a/nGenEx/CameraView.h b/nGenEx/CameraView.h
new file mode 100644
index 0000000..493e4f7
--- /dev/null
+++ b/nGenEx/CameraView.h
@@ -0,0 +1,114 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: CameraView.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ 3D Projection Camera View class
+*/
+
+#ifndef CameraView_h
+#define CameraView_h
+
+#include "Types.h"
+#include "View.h"
+#include "Camera.h"
+#include "Projector.h"
+#include "Video.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class Video;
+class Scene;
+class Bitmap;
+
+class Graphic;
+
+// +--------------------------------------------------------------------+
+
+class CameraView : public View
+{
+public:
+ static const char* TYPENAME() { return "CameraView"; }
+
+ CameraView(Window* c, Camera* cam, Scene* s);
+ virtual ~CameraView();
+
+ // Operations:
+ virtual void Refresh();
+ virtual void OnWindowMove();
+ virtual void UseCamera(Camera* cam);
+ virtual void UseScene(Scene* scene);
+ virtual void LensFlareElements(Bitmap* halo, Bitmap* e1=0, Bitmap* e2=0, Bitmap* e3=0);
+ virtual void LensFlare(int on, double dim = 1);
+ virtual void SetDepthScale(float scale);
+
+ // accessors:
+ Camera* GetCamera() const { return camera; }
+ Projector* GetProjector() { return &projector; }
+ Scene* GetScene() const { return scene; }
+ virtual void SetFieldOfView(double fov);
+ virtual double GetFieldOfView() const;
+ virtual void SetProjectionType(DWORD pt);
+ virtual DWORD GetProjectionType() const;
+
+ Point Pos() const { return camera->Pos(); }
+ Point vrt() { return camera->vrt(); }
+ Point vup() { return camera->vup(); }
+ Point vpn() { return camera->vpn(); }
+ const Matrix& Orientation() const { return camera->Orientation(); }
+
+ Point SceneOffset() const { return camera_loc; }
+
+ // projection and clipping geometry:
+ virtual void TranslateScene();
+ virtual void UnTranslateScene();
+ virtual void MarkVisibleObjects();
+ virtual void MarkVisibleLights(Graphic* g, DWORD flags);
+
+ virtual void RenderScene();
+ virtual void RenderSceneObjects(bool distant=false);
+ virtual void RenderForeground();
+ virtual void RenderBackground();
+ virtual void RenderSprites();
+ virtual void RenderLensFlare();
+ virtual void Render(Graphic* g, DWORD flags);
+
+ virtual void FindDepth(Graphic* g);
+ virtual int SetInfinite(int i);
+
+protected:
+ Camera* camera;
+ Scene* scene;
+ Video* video;
+
+ virtual void WorldPlaneToView(Plane& plane);
+
+ Point camera_loc;
+ Vec3 cvrt;
+ Vec3 cvup;
+ Vec3 cvpn;
+
+ Projector projector;
+ int infinite;
+ int width;
+ int height;
+ DWORD projection_type;
+
+ // lens flare:
+ int lens_flare_enable;
+ double lens_flare_dim;
+ Bitmap* halo_bitmap;
+ Bitmap* elem_bitmap[3];
+
+ List<Graphic> graphics;
+};
+
+#endif CameraView_h
+
diff --git a/nGenEx/Color.cpp b/nGenEx/Color.cpp
new file mode 100644
index 0000000..41657eb
--- /dev/null
+++ b/nGenEx/Color.cpp
@@ -0,0 +1,682 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Color.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Universal Color Format class
+*/
+
+#include "MemDebug.h"
+#include "Color.h"
+#include "Video.h"
+#include "PCX.h"
+#include "Fix.h"
+
+// +--------------------------------------------------------------------+
+
+void Print(const char* fmt, ...);
+
+Color Color::White = Color(255,255,255);
+Color Color::Black = Color( 0, 0, 0);
+Color Color::Gray = Color(128,128,128);
+Color Color::LightGray = Color(192,192,192);
+Color Color::DarkGray = Color( 64, 64, 64);
+Color Color::BrightRed = Color(255, 0, 0);
+Color Color::BrightBlue = Color( 0, 0,255);
+Color Color::BrightGreen= Color( 0,255, 0);
+Color Color::DarkRed = Color(128, 0, 0);
+Color Color::DarkBlue = Color( 0, 0,128);
+Color Color::DarkGreen = Color( 0,128, 0);
+Color Color::Yellow = Color(255,255, 0);
+Color Color::Cyan = Color( 0,255,255);
+Color Color::Magenta = Color(255, 0,255);
+Color Color::Tan = Color(180,150,120);
+Color Color::Brown = Color(128,100, 80);
+Color Color::Violet = Color(128, 0,128);
+Color Color::Orange = Color(255,150, 20);
+
+bool Color::standard_format = false;
+int Color::texture_alpha_level = 0;
+ColorFormat Color::format = ColorFormat(256);
+ColorFormat Color::texture_format[4] = { ColorFormat(256), ColorFormat(256), ColorFormat(256), ColorFormat(256) };
+PALETTEENTRY Color::palette[256];
+BYTE Color::table[32768];
+double Color::fade = 1.0;
+Color Color::fade_color;
+Video* Color::video = 0;
+DWORD ColorIndex::texture_palettes[4][256];
+DWORD ColorIndex::unfaded_palette[256];
+DWORD ColorIndex::formatted_palette[256];
+DWORD ColorIndex::shade_table[256*256];
+BYTE ColorIndex::blend_table[256*256];
+DWORD* ColorIndex::texture_palette = ColorIndex::texture_palettes[0];
+
+// +--------------------------------------------------------------------+
+
+Color::Color(BYTE index)
+{
+ PALETTEENTRY* p = &palette[index];
+
+ Set(p->peRed, p->peGreen, p->peBlue);
+}
+
+// +--------------------------------------------------------------------+
+
+Color&
+Color::operator+=(const Color& c)
+{
+ int r = Red() + c.Red(); if (r > 255) r = 255;
+ int g = Green() + c.Green(); if (g > 255) g = 255;
+ int b = Blue() + c.Blue(); if (b > 255) b = 255;
+
+ Set((BYTE) r, (BYTE) g, (BYTE) b);
+ return *this;
+}
+
+Color
+Color::operator+(DWORD d) const
+{
+ int r = Red() + ((d & RMask) >> RShift); if (r > 255) r = 255;
+ int g = Green() + ((d & GMask) >> GShift); if (g > 255) g = 255;
+ int b = Blue() + ((d & BMask) >> BShift); if (b > 255) b = 255;
+
+ return Color((BYTE) r,(BYTE) g,(BYTE) b);
+}
+
+// +--------------------------------------------------------------------+
+
+Color
+Color::operator+(const Color& c) const
+{
+ float src_alpha = c.fAlpha();
+ float dst_alpha = 1.0f - src_alpha;
+
+ BYTE r = (BYTE)((fRed() *dst_alpha + c.fRed() *src_alpha)*255.0f);
+ BYTE g = (BYTE)((fGreen()*dst_alpha + c.fGreen()*src_alpha)*255.0f);
+ BYTE b = (BYTE)((fBlue() *dst_alpha + c.fBlue() *src_alpha)*255.0f);
+
+ return Color(r, g, b);
+}
+
+// +--------------------------------------------------------------------+
+
+Color
+Color::operator*(const Color& c) const
+{
+ BYTE r = (BYTE) ((fRed() * c.fRed()) *255.0f);
+ BYTE g = (BYTE) ((fGreen() * c.fGreen()) *255.0f);
+ BYTE b = (BYTE) ((fBlue() * c.fBlue()) *255.0f);
+
+ return Color(r, g, b);
+}
+
+// +--------------------------------------------------------------------+
+
+Color
+Color::operator*(double scale) const
+{
+ int r = fast_f2i(Red() * scale); if (r > 255) r = 255;
+ int g = fast_f2i(Green() * scale); if (g > 255) g = 255;
+ int b = fast_f2i(Blue() * scale); if (b > 255) b = 255;
+ int a = fast_f2i(Alpha() * scale); if (a > 255) a = 255;
+
+ return Color((BYTE) r, (BYTE) g, (BYTE) b, (BYTE) a);
+}
+
+Color
+Color::dim(double scale) const
+{
+ int r = fast_f2i(Red() * scale);
+ int g = fast_f2i(Green() * scale);
+ int b = fast_f2i(Blue() * scale);
+
+ return Color((BYTE) r, (BYTE) g, (BYTE) b, (BYTE) Alpha());
+}
+
+// +--------------------------------------------------------------------+
+
+DWORD
+Color::Formatted() const
+{
+ if (format.pal) {
+ return Index();
+ }
+
+ else {
+ if (fade != 1.0) {
+ double step = (1.0 - fade);
+
+ DWORD r = ((int) ((fRed() - (fRed() - fade_color.fRed()) * step)*255.0)) >> format.rdown;
+ DWORD g = ((int) ((fGreen() - (fGreen() - fade_color.fGreen())* step)*255.0)) >> format.gdown;
+ DWORD b = ((int) ((fBlue() - (fBlue() - fade_color.fBlue()) * step)*255.0)) >> format.bdown;
+ DWORD a = Alpha()>>format.adown;
+
+ return (r<<format.rshift)|(g<<format.gshift)|(b<<format.bshift)|(a<<format.ashift);
+
+ }
+
+ else if (standard_format) {
+ return rgba;
+ }
+
+ else {
+ DWORD r = Red() >>format.rdown;
+ DWORD g = Green()>>format.gdown;
+ DWORD b = Blue() >>format.bdown;
+ DWORD a = Alpha()>>format.adown;
+
+ return (r<<format.rshift)|(g<<format.gshift)|(b<<format.bshift)|(a<<format.ashift);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Color
+Color::Faded() const
+{
+ if (fade != 1.0) {
+ double step = (1.0 - fade);
+
+ DWORD r = ((int) ((fRed() - (fRed() - fade_color.fRed()) * step)*255.0));
+ DWORD g = ((int) ((fGreen() - (fGreen() - fade_color.fGreen())* step)*255.0));
+ DWORD b = ((int) ((fBlue() - (fBlue() - fade_color.fBlue()) * step)*255.0));
+ DWORD a = Alpha();
+
+ return Color((BYTE) r, (BYTE) g, (BYTE) b, (BYTE) a);
+
+ }
+
+ else {
+ return *this;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+DWORD
+Color::Unfaded() const
+{
+ if (standard_format) {
+ return rgba;
+ }
+
+ if (format.pal) {
+ return Index();
+ }
+ else {
+ DWORD r = Red() >>format.rdown;
+ DWORD g = Green()>>format.gdown;
+ DWORD b = Blue() >>format.bdown;
+ DWORD a = Alpha()>>format.adown;
+
+ return (r<<format.rshift)|(g<<format.gshift)|(b<<format.bshift)|(a<<format.ashift);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+DWORD
+Color::TextureFormat(int keep_alpha) const
+{
+ if (texture_format[texture_alpha_level].pal) {
+ return Index();
+ }
+ else if (rgba == 0) {
+ return 0;
+ }
+ else {
+ ColorFormat& tf = texture_format[texture_alpha_level];
+
+ DWORD r = Red();
+ DWORD g = Green();
+ DWORD b = Blue();
+ DWORD a = 0;
+
+ if (keep_alpha) {
+ a = Alpha()>>tf.adown;
+ }
+
+ else if (texture_alpha_level == 1) {
+ // transparent:
+ a = 255>>tf.adown;
+ }
+
+ else if (texture_alpha_level == 2) {
+ // translucent:
+ if (r || g || b)
+ a = ((r+g+b+255)>>2)>>tf.adown;
+ }
+
+ r = r >>tf.rdown;
+ g = g >>tf.gdown;
+ b = b >>tf.bdown;
+
+ return (r<<tf.rshift)|(g<<tf.gshift)|(b<<tf.bshift)|(a<<tf.ashift);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Color
+Color::ShadeColor(int shade) const
+{
+ double fr = fRed(), sr = fr;
+ double fg = fGreen(), sg = fg;
+ double fb = fBlue(), sb = fb;
+ double range = SHADE_LEVELS;
+
+ // first shade:
+ if (shade < SHADE_LEVELS) { // shade towards black
+ sr = fr * (shade/range);
+ sg = fg * (shade/range);
+ sb = fb * (shade/range);
+ }
+ else if (shade > SHADE_LEVELS) { // shade towards white
+ double step = (shade - range)/range;
+
+ sr = fr - (fr - 1.0) * step;
+ sg = fg - (fg - 1.0) * step;
+ sb = fb - (fb - 1.0) * step;
+ }
+
+ return Color((BYTE) (sr*255.0), (BYTE) (sg*255.0), (BYTE) (sb*255.0), (BYTE) Alpha());
+}
+
+// +--------------------------------------------------------------------+
+
+DWORD
+Color::Shaded(int shade) const
+{
+ return ShadeColor(shade).Formatted();
+}
+
+// +--------------------------------------------------------------------+
+
+Color
+Color::Unformat(DWORD formatted_color)
+{
+ if (format.pal) {
+ return Color((BYTE) formatted_color);
+ }
+ else if (standard_format) {
+ Color c;
+ c.Set(formatted_color);
+ return c;
+ }
+ else {
+ BYTE r = (BYTE) ((formatted_color & format.rmask)>>format.rshift) << format.rdown;
+ BYTE g = (BYTE) ((formatted_color & format.gmask)>>format.gshift) << format.gdown;
+ BYTE b = (BYTE) ((formatted_color & format.bmask)>>format.bshift) << format.bdown;
+ BYTE a = (BYTE) ((formatted_color & format.amask)>>format.ashift) << format.adown;
+
+ return Color(r,g,b,a);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Color
+Color::Scale(const Color& c1, const Color& c2, double scale)
+{
+ BYTE r = (BYTE) ((c1.fRed() + (c2.fRed() - c1.fRed() )*scale) * 255);
+ BYTE g = (BYTE) ((c1.fGreen() + (c2.fGreen() - c1.fGreen())*scale) * 255);
+ BYTE b = (BYTE) ((c1.fBlue() + (c2.fBlue() - c1.fBlue() )*scale) * 255);
+ BYTE a = (BYTE) ((c1.fAlpha() + (c2.fAlpha() - c1.fAlpha())*scale) * 255);
+
+ return Color(r,g,b,a);
+}
+
+// +--------------------------------------------------------------------+
+
+DWORD
+Color::FormattedBlend(DWORD c1, DWORD c2)
+{
+ if (format.pal) {
+ return ColorIndex::blend_table[(BYTE) c1 * 256 + (BYTE) c2];
+ }
+ else {
+ ColorFormat& cf = format;
+
+ DWORD r = (c1 & cf.rmask) + (c2 & cf.rmask);
+ DWORD g = (c1 & cf.gmask) + (c2 & cf.gmask);
+ DWORD b = (c1 & cf.bmask) + (c2 & cf.bmask);
+
+ if (r & ~cf.rmask) r = cf.rmask;
+ if (g & ~cf.gmask) g = cf.gmask;
+ if (b & ~cf.bmask) b = cf.bmask;
+
+ return (DWORD) (r|g|b);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Color::UseVideo(Video* v)
+{
+ video = v;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Color::UseFormat(const ColorFormat& cf)
+{
+ format = cf;
+
+ if (format.rmask == RMask && format.gmask == GMask && format.bmask == BMask)
+ standard_format = true;
+ else
+ standard_format = false;
+
+ if (cf.pal) {
+ for (int i = 0; i < 256; i++) {
+ ColorIndex::formatted_palette[i] = i;
+ ColorIndex::unfaded_palette[i] = i;
+ }
+ }
+ else {
+ double old_fade = fade;
+
+ for (int i = 0; i < 256; i++) {
+ ColorIndex::formatted_palette[i] = Color(i).Formatted();
+
+ fade = 1.0;
+ ColorIndex::unfaded_palette[i] = Color(i).Formatted();
+ fade = old_fade;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Color::UseTextureFormat(const ColorFormat& cf, int alpha_level)
+{
+ texture_format[alpha_level] = cf;
+
+ if (cf.pal) {
+ for (int i = 0; i < 256; i++) {
+ ColorIndex::texture_palettes[alpha_level][i] = i;
+ }
+ }
+ else {
+ double old_fade = fade;
+
+ for (int i = 0; i < 256; i++) {
+ int old_texture_alpha_level = texture_alpha_level;
+ texture_alpha_level = alpha_level;
+ ColorIndex::texture_palettes[alpha_level][i] = Color(i).TextureFormat();
+ texture_alpha_level = old_texture_alpha_level;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Color::WithTextureFormat(int alpha_level)
+{
+ texture_alpha_level = alpha_level;
+ ColorIndex::texture_palette = ColorIndex::texture_palettes[alpha_level];
+}
+
+// +--------------------------------------------------------------------+
+
+static BYTE MatchRGB(PALETTEENTRY* pal, BYTE r, BYTE g, BYTE b)
+{
+ double mindist = 100000000.0;
+ BYTE match = 0;
+
+ for (int i = 0; i < 256; i++) {
+ PALETTEENTRY* p = pal++;
+
+ double dr = p->peRed - r;
+ double dg = p->peGreen - g;
+ double db = p->peBlue - b;
+
+ double d = (dr*dr) + (dg*dg) + (db*db);
+
+ if (d < mindist) {
+ mindist = d;
+ match = i;
+
+ if (d < 1.0)
+ return match;
+ }
+ }
+
+ return match;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Color::SetPalette(PALETTEENTRY* pal, int palsize, BYTE* invpal)
+{
+ for (int i = 0; i < palsize; i++)
+ palette[i] = pal[i];
+
+ if (invpal) {
+ for (i = 0; i < 32768; i++)
+ table[i] = invpal[i];
+ }
+ else {
+ for (i = 0; i < 32768; i++) {
+ BYTE r = (i >> 10) & 0x1f;
+ BYTE g = (i >> 5) & 0x1f;
+ BYTE b = (i ) & 0x1f;
+
+ Color c(r<<3, g<<3, b<<3);
+
+ table[i] = MatchRGB(palette, r<<3, g<<3, b<<3);
+ }
+ }
+
+ // set up formatted palette:
+ UseFormat(format);
+
+ for (i = 0; i < 4; i++)
+ UseTextureFormat(texture_format[i], i);
+
+ // set up shade table:
+ double old_fade = fade;
+ fade = 1.0;
+ BuildShadeTable();
+ fade = old_fade;
+
+ // and blend table:
+ BuildBlendTable();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Color::SavePalette(const char* basename)
+{
+ char filename[256];
+
+ sprintf(filename, "%s.ipl", basename);
+ FILE* f = fopen(filename, "wb");
+ if (f) {
+ fwrite(table, sizeof(table), 1, f);
+ fclose(f);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Color::SetFade(double f, Color c, int build_shade)
+{
+ static int shade_built = 0;
+
+ if (fade == f && fade_color == c && (build_shade ? shade_built : 1))
+ return;
+
+ fade = f;
+ fade_color = c;
+
+ // set up formatted palette:
+ UseFormat(format);
+
+ // if this is a paletted video mode,
+ // modify the video palette as well:
+ if (format.pal && video) {
+ PALETTEENTRY fade_palette[256];
+
+ double step = (1.0 - fade);
+ for (int i = 0; i < 256; i++) {
+ PALETTEENTRY& entry = fade_palette[i];
+ ColorIndex c = ColorIndex(i);
+
+ entry.peRed = ((int) ((c.fRed() - (c.fRed() - fade_color.fRed()) * step)*255.0));
+ entry.peGreen = ((int) ((c.fGreen() - (c.fGreen() - fade_color.fGreen())* step)*255.0));
+ entry.peBlue = ((int) ((c.fBlue() - (c.fBlue() - fade_color.fBlue()) * step)*255.0));
+ entry.peFlags = 0;
+ }
+ }
+
+ // otherwise, we need to re-compute
+ // the shade table:
+ else {
+ if (build_shade) {
+ BuildShadeTable();
+ shade_built = 1;
+ }
+ else {
+ shade_built = 0;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Color::BuildShadeTable()
+{
+ for (int shade = 0; shade < SHADE_LEVELS*2; shade++)
+ for (int index = 0; index < 256; index++)
+ ColorIndex::shade_table[shade*256+index] = Color(index).Shaded(shade);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Color::BuildBlendTable()
+{
+ for (int src = 0; src < 256; src++) {
+ for (int dst = 0; dst < 256; dst++) {
+ ColorIndex src_clr = ColorIndex(src);
+ ColorIndex dst_clr = ColorIndex(dst);
+
+ int r = src_clr.Red() + dst_clr.Red();
+ int g = src_clr.Green() + dst_clr.Green();
+ int b = src_clr.Blue() + dst_clr.Blue();
+
+ if (r>255) r=255;
+ if (g>255) g=255;
+ if (b>255) b=255;
+
+ ColorIndex::blend_table[src*256+dst] = Color((BYTE)r,(BYTE)g,(BYTE)b).Index();
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Color::SaveShadeTable(const char* basename)
+{
+ if (!format.pal)
+ return;
+
+ char filename[256];
+ sprintf(filename, "%s_clut.pcx", basename);
+
+ BYTE clut[256*256];
+ BYTE* pc = clut;
+
+ for (int i = 0; i < SHADE_LEVELS*2; i++)
+ for (int j = 0; j < 256; j++)
+ *pc++ = (BYTE) ColorIndex::shade_table[i*256+j];
+
+ for (; i < 256; i++)
+ for (int j = 0; j < 256; j++)
+ *pc++ = (BYTE) 0;
+
+ PcxImage pcx(256, 256, clut, (BYTE*) palette);
+ pcx.Save(filename);
+}
+
+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+
+
+ColorValue&
+ColorValue::operator+=(const ColorValue& c)
+{
+ r += c.r;
+ g += c.g;
+ b += c.b;
+
+ return *this;
+}
+
+ColorValue
+ColorValue::operator+(const ColorValue& c) const
+{
+ float src_alpha = c.a;
+ float dst_alpha = 1.0f - a;
+
+ float fr = (r * dst_alpha) + (c.r * src_alpha);
+ float fg = (g * dst_alpha) + (c.g * src_alpha);
+ float fb = (b * dst_alpha) + (c.b * src_alpha);
+
+ return ColorValue(fr, fg, fb);
+}
+
+ColorValue
+ColorValue::operator*(const ColorValue& c) const
+{
+ return ColorValue(r*c.r, g*c.g, b*c.b);
+}
+
+ColorValue
+ColorValue::operator*(double scale) const
+{
+ return ColorValue((float) (r*scale),
+ (float) (g*scale),
+ (float) (b*scale),
+ (float) (a*scale));
+}
+
+ColorValue
+ColorValue::dim(double scale) const
+{
+ return ColorValue((float) (r*scale),
+ (float) (g*scale),
+ (float) (b*scale));
+}
+
+// +--------------------------------------------------------------------+
+
+inline BYTE bclamp(float x) { return (BYTE) ((x<0) ? 0 : (x>255) ? 255 : x); }
+
+Color
+ColorValue::ToColor() const
+{
+ return Color(bclamp(r * 255.0f),
+ bclamp(g * 255.0f),
+ bclamp(b * 255.0f),
+ bclamp(a * 255.0f));
+}
diff --git a/nGenEx/Color.h b/nGenEx/Color.h
new file mode 100644
index 0000000..d719f34
--- /dev/null
+++ b/nGenEx/Color.h
@@ -0,0 +1,295 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Color.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Universal Color Format class
+*/
+
+#ifndef Color_h
+#define Color_h
+
+#include "Types.h"
+
+// +--------------------------------------------------------------------+
+
+class Video;
+
+// +--------------------------------------------------------------------+
+
+struct ColorFormat
+{
+ ColorFormat(int palsize)
+ : pal(palsize), bpp(8),
+ rdown(0), rshift(0), rmask(0),
+ gdown(0), gshift(0), gmask(0),
+ bdown(0), bshift(0), bmask(0),
+ adown(0), ashift(0), amask(0) { }
+
+ ColorFormat(int size, BYTE r, BYTE rs, BYTE g, BYTE gs, BYTE b, BYTE bs, BYTE a=0, BYTE as=0)
+ : pal(0), bpp(size),
+ rdown(8-r), rshift(rs), rmask(((1<<r)-1)<<rs),
+ gdown(8-g), gshift(gs), gmask(((1<<g)-1)<<gs),
+ bdown(8-b), bshift(bs), bmask(((1<<b)-1)<<bs),
+ adown(8-a), ashift(as), amask(((1<<a)-1)<<as) { }
+
+ int pal;
+ int bpp;
+ BYTE rdown, rshift;
+ BYTE gdown, gshift;
+ BYTE bdown, bshift;
+ BYTE adown, ashift;
+
+ DWORD rmask;
+ DWORD gmask;
+ DWORD bmask;
+ DWORD amask;
+};
+
+// +--------------------------------------------------------------------+
+
+class Color
+{
+ friend class ColorIndex;
+ friend class ColorValue;
+
+public:
+ static const char* TYPENAME() { return "Color"; }
+
+ enum Misc { SHADE_LEVELS = 64 }; // max 128!
+ enum Mask { RMask = 0x00ff0000,
+ GMask = 0x0000ff00,
+ BMask = 0x000000ff,
+ AMask = 0xff000000,
+ RGBMask = 0x00ffffff };
+ enum Shift { RShift = 16,
+ GShift = 8,
+ BShift = 0,
+ AShift = 24 };
+
+ Color() : rgba(0) { }
+ Color(const Color& c) : rgba(c.rgba) { }
+ Color(BYTE r, BYTE g, BYTE b, BYTE a=255) {
+ rgba = (r<<RShift)|(g<<GShift)|(b<<BShift)|(a<<AShift); }
+
+ Color(BYTE index);
+
+ Color& operator= (const Color& c) { rgba = c.rgba; return *this; }
+ int operator==(const Color& c) const { return rgba == c.rgba; }
+ int operator!=(const Color& c) const { return rgba != c.rgba; }
+ Color& operator+=(const Color& c); // simple summation
+
+ Color operator+(DWORD d) const;
+
+ Color operator+(const Color& c) const; // color alpha blending
+ Color operator*(const Color& c) const; // color modulation
+ Color operator*(double scale) const;
+ Color dim(double scale) const;
+
+ void Set(DWORD value) { rgba = value; }
+ void Set(BYTE r, BYTE g, BYTE b, BYTE a=255) {
+ rgba = (r<<RShift)|(g<<GShift)|(b<<BShift)|(a<<AShift); }
+
+ DWORD Value() const { return rgba; }
+
+ void SetRed(BYTE r) { rgba = (rgba & ~RMask) | (r << RShift); }
+ void SetGreen(BYTE g) { rgba = (rgba & ~GMask) | (g << GShift); }
+ void SetBlue(BYTE b) { rgba = (rgba & ~BMask) | (b << BShift); }
+ void SetAlpha(BYTE a) { rgba = (rgba & ~AMask) | (a << AShift); }
+
+ DWORD Red() const { return (rgba & RMask) >> RShift; }
+ DWORD Green() const { return (rgba & GMask) >> GShift; }
+ DWORD Blue() const { return (rgba & BMask) >> BShift; }
+ DWORD Alpha() const { return (rgba & AMask) >> AShift; }
+
+ float fRed() const { return (float)(Red() /255.0); }
+ float fGreen() const { return (float)(Green()/255.0); }
+ float fBlue() const { return (float)(Blue() /255.0); }
+ float fAlpha() const { return (float)(Alpha()/255.0); }
+
+ BYTE Index() const { return table[((rgba&RMask)>>(RShift+3)<<10)|
+ ((rgba&GMask)>>(GShift+3)<< 5)|
+ ((rgba&BMask)>>(BShift+3))]; }
+
+ inline BYTE IndexedBlend(BYTE dst) const;
+ static DWORD FormattedBlend(DWORD c1, DWORD c2);
+
+ DWORD TextureFormat(int keep_alpha=0) const;
+ DWORD Formatted() const;
+ DWORD Unfaded() const;
+ Color ShadeColor(int shade) const;
+ DWORD Shaded(int shade) const;
+ Color Faded() const;
+
+ // some useful colors
+ static Color White;
+ static Color Black;
+ static Color Gray;
+ static Color LightGray;
+ static Color DarkGray;
+ static Color BrightRed;
+ static Color BrightBlue;
+ static Color BrightGreen;
+ static Color DarkRed;
+ static Color DarkBlue;
+ static Color DarkGreen;
+ static Color Yellow;
+ static Color Cyan;
+ static Color Magenta;
+ static Color Tan;
+ static Color Brown;
+ static Color Violet;
+ static Color Orange;
+
+ static void UseVideo(Video* v);
+ static void UseFormat(const ColorFormat& cf);
+ static void UseTextureFormat(const ColorFormat& cf, int alpha_level=0);
+ static void WithTextureFormat(int alpha_level=0);
+ static void SetPalette(PALETTEENTRY* pal, int palsize, BYTE* invpal=0);
+ static void SavePalette(const char* basename);
+ static void SetFade(double f, Color c=Black, int build_shade=false);
+
+ static const ColorFormat& GetTextureFormat(int alpha_level=0) { return texture_format[alpha_level]; }
+ static const ColorFormat& GetScreenFormat() { return format; }
+
+ // indexed color initialization:
+ static void BuildShadeTable();
+ static void SaveShadeTable(const char* basename);
+ static void BuildBlendTable();
+
+ static double GetFade() { return fade; }
+ static Color GetFadeColor() { return fade_color; }
+
+ static Color Scale(const Color& c1, const Color& c2, double scale);
+ static Color Unformat(DWORD formatted_color);
+
+private:
+ DWORD rgba;
+
+ static bool standard_format;
+ static PALETTEENTRY palette[256];
+ static BYTE table[32768];
+ static ColorFormat format;
+ static int texture_alpha_level;
+ static ColorFormat texture_format[4];
+ static double fade;
+ static Color fade_color;
+ static Video* video;
+};
+
+// +--------------------------------------------------------------------+
+
+class ColorValue
+{
+ friend class Color;
+
+public:
+ static const char* TYPENAME() { return "ColorValue"; }
+
+ ColorValue() : r(0), g(0), b(0), a(0) { }
+ ColorValue(const ColorValue& c) : r(c.r), g(c.g), b(c.b), a(c.a) { }
+ ColorValue(const Color& c) : r( c.fRed() ),
+ g( c.fGreen() ),
+ b( c.fBlue() ),
+ a( c.fAlpha() ) { }
+ ColorValue(float ar,
+ float ag,
+ float ab,
+ float aa=1.0f) : r(ar), g(ag), b(ab), a(aa) { }
+
+ int operator==(const ColorValue& c) const { return r==c.r && g==c.g && b==c.b && a==c.a; }
+ int operator!=(const ColorValue& c) const { return r!=c.r || g!=c.g || b!=c.b || a!=c.a; }
+ ColorValue& operator+=(const ColorValue& c); // simple summation
+
+ ColorValue operator+(const ColorValue& c) const; // color alpha blending
+ ColorValue operator*(const ColorValue& c) const; // color modulation
+ ColorValue operator*(double scale) const;
+ ColorValue dim(double scale) const;
+
+ void Set(float ar, float ag, float ab, float aa=1.0f) { r=ar; g=ag; b=ab; a=aa; }
+
+ void SetRed(float ar) { r=ar; }
+ void SetGreen(float ag) { g=ag; }
+ void SetBlue(float ab) { b=ab; }
+ void SetAlpha(float aa) { a=aa; }
+
+ float Red() const { return r; }
+ float Green() const { return g; }
+ float Blue() const { return b; }
+ float Alpha() const { return a; }
+
+ Color ToColor() const;
+ DWORD TextureFormat(int keep_alpha=0) const { return ToColor().TextureFormat(keep_alpha); }
+ DWORD Formatted() const { return ToColor().Formatted(); }
+ DWORD Unfaded() const { return ToColor().Unfaded(); }
+ Color ShadeColor(int shade) const { return ToColor().ShadeColor(shade); }
+ DWORD Shaded(int shade) const { return ToColor().Shaded(shade); }
+ Color Faded() const { return ToColor().Faded(); }
+
+private:
+ float r, g, b, a;
+};
+
+// +--------------------------------------------------------------------+
+
+class ColorIndex
+{
+ friend class Color;
+
+public:
+ static const char* TYPENAME() { return "ColorIndex"; }
+
+ ColorIndex() : index(0) { }
+ ColorIndex(const ColorIndex& c) : index(c.index) { }
+ ColorIndex(BYTE r, BYTE g, BYTE b) { index = Color(r,g,b).Index(); }
+ ColorIndex(BYTE i) : index(i) { }
+
+ ColorIndex& operator= (const ColorIndex& c) { index = c.index; return *this; }
+ int operator==(const ColorIndex& c) const { return index == c.index; }
+ int operator!=(const ColorIndex& c) const { return index != c.index; }
+
+ BYTE Index() const { return index; }
+
+ DWORD Red() const { return Color::palette[index].peRed; }
+ DWORD Green() const { return Color::palette[index].peGreen; }
+ DWORD Blue() const { return Color::palette[index].peBlue; }
+
+ float fRed() const { return (float)(Red() /255.0); }
+ float fGreen() const { return (float)(Green()/255.0); }
+ float fBlue() const { return (float)(Blue() /255.0); }
+
+ DWORD TextureFormat() const { return texture_palette[index]; }
+ DWORD Unfaded() const { return unfaded_palette[index]; }
+ DWORD Formatted() const { return formatted_palette[index]; }
+ DWORD Shaded(int shade) const { return shade_table[shade*256+index]; }
+ ColorIndex Faded() const { return ColorIndex(index); }
+
+ // note: this will only work in 8-bit color mode...
+ ColorIndex ShadeColor(int s) const { return ColorIndex((BYTE)(shade_table[s*256+index])); }
+
+ // for poly shading optimization
+ static DWORD* ShadeTable() { return shade_table; }
+
+ BYTE IndexedBlend(BYTE dst) const { return blend_table[dst*256+index]; }
+
+private:
+ BYTE index;
+
+ static DWORD* texture_palette;
+ static DWORD texture_palettes[4][256];
+ static DWORD unfaded_palette[256];
+ static DWORD formatted_palette[256];
+ static DWORD shade_table[256*256];
+ static BYTE blend_table[256*256];
+};
+
+inline BYTE Color::IndexedBlend(BYTE dst) const { return ColorIndex::blend_table[dst*256+Index()]; }
+
+#endif Color_h
+
diff --git a/nGenEx/ComboBox.cpp b/nGenEx/ComboBox.cpp
new file mode 100644
index 0000000..82880de
--- /dev/null
+++ b/nGenEx/ComboBox.cpp
@@ -0,0 +1,434 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: ComboBox.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Drop-down list of Buttons class
+*/
+
+#include "MemDebug.h"
+#include "ComboBox.h"
+#include "ComboList.h"
+#include "Button.h"
+#include "Video.h"
+#include "Screen.h"
+#include "Bitmap.h"
+#include "Font.h"
+#include "EventDispatch.h"
+
+// +--------------------------------------------------------------------+
+// DECLARE MAPPING FUNCTIONS:
+DEF_MAP_CLIENT(ComboBox, OnListSelect);
+DEF_MAP_CLIENT(ComboBox, OnListExit);
+
+// +--------------------------------------------------------------------+
+
+ComboBox::ComboBox(ActiveWindow* p, int ax, int ay, int aw, int ah, DWORD aid)
+ : ActiveWindow(p->GetScreen(), ax, ay, aw, ah, aid, 0, p)
+{
+ button_state = 0;
+ captured = 0;
+ pre_state = 0;
+ seln = 0;
+ list = 0;
+ list_showing = false;
+ border = true;
+ animated = true;
+ bevel_width = 5;
+
+ char buf[32];
+ sprintf(buf, "ComboBox %d", id);
+ desc = buf;
+}
+
+ComboBox::ComboBox(Screen* s, int ax, int ay, int aw, int ah, DWORD aid)
+ : ActiveWindow(s, ax, ay, aw, ah, aid)
+{
+ button_state = 0;
+ captured = 0;
+ pre_state = 0;
+ seln = 0;
+ list = 0;
+ list_showing = false;
+ border = true;
+ animated = true;
+ bevel_width = 5;
+
+ char buf[32];
+ sprintf(buf, "ComboBox %d", id);
+ desc = buf;
+}
+
+// +--------------------------------------------------------------------+
+
+ComboBox::~ComboBox()
+{
+ items.destroy();
+
+ if (list && !list_showing)
+ delete list;
+}
+
+// +--------------------------------------------------------------------+
+
+void ComboBox::SetBevelWidth(short nNewValue)
+{
+ if (nNewValue < 0) nNewValue = 0;
+ bevel_width = nNewValue;
+}
+
+void ComboBox::SetBorder(bool bNewValue)
+{
+ border = bNewValue;
+}
+
+void ComboBox::SetBorderColor(Color newValue)
+{
+ border_color = newValue;
+}
+
+void ComboBox::SetActiveColor(Color newValue)
+{
+ active_color = newValue;
+}
+
+void ComboBox::SetAnimated(bool bNewValue)
+{
+ animated = bNewValue;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ComboBox::Draw()
+{
+ int x = 0;
+ int y = 0;
+ int w = rect.w;
+ int h = rect.h;
+
+ if (w < 1 || h < 1 || !shown)
+ return;
+
+ Rect btn_rect(x,y,w,h);
+
+ // draw the bevel:
+ DrawRectSimple(btn_rect, button_state);
+
+ // draw the border:
+ if (border)
+ DrawRect(0,0,w-1,h-1,border_color);
+
+ // draw the arrow:
+ POINT arrow[3];
+ double h3 = (double)h/3.0;
+
+ arrow[0].x = (int) (w-2*h3);
+ arrow[0].y = (int) (h3);
+ arrow[1].x = (int) (w-h3);
+ arrow[1].y = (int) (h3);
+ arrow[2].x = (int) ((arrow[1].x + arrow[0].x)/2);
+ arrow[2].y = (int) (2*h3);
+
+ FillPoly(3, arrow, border_color);
+
+ // draw text here:
+ Text caption = text;
+ if (GetSelectedIndex() >= 0)
+ caption = GetSelectedItem();
+
+ if (font && caption.length()) {
+ int border_size = 4;
+
+ if (style & WIN_RAISED_FRAME && style & WIN_SUNK_FRAME)
+ border_size = 8;
+
+ Rect label_rect = CalcLabelRect();
+ int vert_space = label_rect.h;
+ int horz_space = label_rect.w;
+
+ DrawText(caption.data(), 0, label_rect, DT_CALCRECT | DT_WORDBREAK | text_align);
+ vert_space = (vert_space - label_rect.h)/2;
+
+ label_rect.w = horz_space;
+
+ if (vert_space > 0)
+ label_rect.y += vert_space;
+
+ if (animated && button_state > 0) {
+ label_rect.x += button_state;
+ label_rect.y += button_state;
+ }
+
+ font->SetColor(fore_color);
+ DrawText(caption.data(), 0, label_rect, DT_WORDBREAK | text_align);
+ }
+}
+
+Rect ComboBox::CalcLabelRect()
+{
+ // fit the text in the bevel:
+ Rect label_rect;
+ label_rect.x = 0;
+ label_rect.y = 0;
+ label_rect.w = rect.w;
+ label_rect.h = rect.h;
+
+ label_rect.Deflate(bevel_width, bevel_width);
+
+ return label_rect;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ComboBox::DrawRectSimple(Rect& rect, int state)
+{
+ if (state && active_color != Color::Black)
+ FillRect(rect, active_color);
+ else
+ FillRect(rect, back_color);
+}
+
+// +--------------------------------------------------------------------+
+
+int ComboBox::OnMouseMove(int x, int y)
+{
+ bool dirty = false;
+
+ if (captured)
+ {
+ ActiveWindow* test = GetCapture();
+
+ if (test != this)
+ {
+ captured = false;
+ button_state = 0;
+ dirty = true;
+ }
+
+ else
+ {
+ if (button_state == 1)
+ {
+ if (!rect.Contains(x,y))
+ {
+ button_state = 0;
+ dirty = true;
+ }
+ }
+ else
+ {
+ if (rect.Contains(x,y))
+ {
+ button_state = 1;
+ dirty = true;
+ }
+ }
+ }
+ }
+
+ return ActiveWindow::OnMouseMove(x,y);
+}
+
+int ComboBox::OnLButtonDown(int x, int y)
+{
+ if (!captured)
+ captured = SetCapture();
+
+ button_state = 1;
+
+ return ActiveWindow::OnLButtonDown(x,y);
+}
+
+int ComboBox::OnLButtonUp(int x, int y)
+{
+ if (captured) {
+ ReleaseCapture();
+ captured = 0;
+ }
+
+ button_state = -1;
+ ShowList();
+ Button::PlaySound(Button::SND_COMBO_OPEN);
+ return ActiveWindow::OnLButtonUp(x,y);
+}
+
+int ComboBox::OnClick()
+{
+ return ActiveWindow::OnClick();
+}
+
+int ComboBox::OnMouseEnter(int mx, int my)
+{
+ if (button_state == 0)
+ button_state = -1;
+
+ return ActiveWindow::OnMouseEnter(mx, my);
+}
+
+int ComboBox::OnMouseExit(int mx, int my)
+{
+ if (button_state == -1)
+ button_state = 0;
+
+ return ActiveWindow::OnMouseExit(mx, my);
+}
+
+// +--------------------------------------------------------------------+
+
+void ComboBox::MoveTo(const Rect& r)
+{
+ ActiveWindow::MoveTo(r);
+
+ if (list) {
+ delete list;
+ list = 0;
+ }
+}
+
+void ComboBox::ShowList()
+{
+ if (!list) {
+ list = new(__FILE__,__LINE__) ComboList(this, screen,
+ rect.x, rect.y,
+ rect.w, rect.h,
+ items.size());
+
+ }
+
+ if (list) {
+ list->SetTextAlign(text_align);
+ list->SetFont(font);
+ list->SetText(text);
+ list->SetSelection(seln);
+ list->SetItems(items);
+
+ list->Show();
+ list_showing = true;
+
+ EventDispatch* dispatch = EventDispatch::GetInstance();
+ if (dispatch) {
+ dispatch->MouseEnter(list);
+ dispatch->SetFocus(list);
+ }
+
+ REGISTER_CLIENT(EID_CLICK, list, ComboBox, OnListSelect);
+ REGISTER_CLIENT(EID_MOUSE_EXIT, list, ComboBox, OnListExit);
+ }
+}
+
+void ComboBox::HideList()
+{
+ if (list) {
+ // These will be handled by the list window itself (i hope)
+ UNREGISTER_CLIENT(EID_CLICK, list, ComboBox);
+ UNREGISTER_CLIENT(EID_MOUSE_EXIT, list, ComboBox);
+
+ list->Hide();
+ list_showing = false;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void ComboBox::OnListSelect(AWEvent* event)
+{
+ if (list) {
+ int new_seln = list->GetSelectedIndex();
+
+ if (new_seln >= 0 && new_seln < items.size())
+ seln = new_seln;
+
+ HideList();
+ OnSelect();
+ Button::PlaySound(Button::SND_COMBO_SELECT);
+ }
+}
+
+void ComboBox::OnListExit(AWEvent* event)
+{
+ //HideList();
+ //Button::PlaySound(Button::SND_COMBO_CLOSE);
+}
+
+// +--------------------------------------------------------------------+
+
+int ComboBox::NumItems()
+{
+ return items.size();
+}
+
+void ComboBox::ClearItems()
+{
+ items.destroy();
+ seln = 0;
+}
+
+void ComboBox::AddItem(const char* item)
+{
+ Text* t = new(__FILE__,__LINE__) Text(item);
+
+ if (t) items.append(t);
+}
+
+const char* ComboBox::GetItem(int index)
+{
+ if (index >= 0 && index < items.size())
+ return items[index]->data();
+ else
+ return 0;
+}
+
+void ComboBox::SetItem(int index, const char* item)
+{
+ if (index >= 0 && index < items.size()) {
+ *items[index] = item;
+ }
+
+ else {
+ Text* t = new(__FILE__,__LINE__) Text(item);
+ if (t)
+ items.append(t);
+ }
+}
+
+void ComboBox::SetLabel(const char* label)
+{
+ SetText(label);
+}
+
+int ComboBox::GetCount()
+{
+ return items.size();
+}
+
+const char* ComboBox::GetSelectedItem()
+{
+ if (seln >= 0 && seln < items.size())
+ return items[seln]->data();
+ else
+ return 0;
+}
+
+int ComboBox::GetSelectedIndex()
+{
+ if (seln >= 0 && seln < items.size())
+ return seln;
+ else
+ return -1;
+}
+
+void ComboBox::SetSelection(int index)
+{
+ if (seln != index && index >= 0 && index < items.size()) {
+ seln = index;
+ }
+}
+
diff --git a/nGenEx/ComboBox.h b/nGenEx/ComboBox.h
new file mode 100644
index 0000000..586dbba
--- /dev/null
+++ b/nGenEx/ComboBox.h
@@ -0,0 +1,105 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: ComboBox.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ ComboBox class
+*/
+
+#ifndef ComboBox_h
+#define ComboBox_h
+
+#include "Types.h"
+#include "ActiveWindow.h"
+#include "Bitmap.h"
+
+#include "List.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class ComboList;
+
+// +--------------------------------------------------------------------+
+
+class ComboBox : public ActiveWindow
+{
+public:
+ static const char* TYPENAME() { return "ComboBox"; }
+
+ ComboBox(ActiveWindow* p, int ax, int ay, int aw, int ah, DWORD aid=0);
+ ComboBox(Screen* s, int ax, int ay, int aw, int ah, DWORD aid=0);
+ virtual ~ComboBox();
+
+ // Operations:
+ virtual void Draw();
+ virtual void ShowList();
+ virtual void HideList();
+ virtual void MoveTo(const Rect& r);
+
+ // Event Target Interface:
+ virtual int OnMouseMove(int x, int y);
+ virtual int OnLButtonDown(int x, int y);
+ virtual int OnLButtonUp(int x, int y);
+ virtual int OnClick();
+ virtual int OnMouseEnter(int x, int y);
+ virtual int OnMouseExit(int x, int y);
+
+ virtual void OnListSelect(AWEvent* event);
+ virtual void OnListExit(AWEvent* event);
+
+ // Property accessors:
+ virtual int NumItems();
+ virtual void ClearItems();
+ virtual void AddItem(const char* item);
+ virtual const char* GetItem(int index);
+ virtual void SetItem(int index, const char* item);
+ virtual void SetLabel(const char* label);
+
+ virtual int GetCount();
+ virtual const char* GetSelectedItem();
+ virtual int GetSelectedIndex();
+ virtual void SetSelection(int index);
+
+ Color GetActiveColor() const { return active_color; }
+ void SetActiveColor(Color c);
+ bool GetAnimated() const { return animated; }
+ void SetAnimated(bool bNewValue);
+ short GetBevelWidth() const { return bevel_width; }
+ void SetBevelWidth(short nNewValue);
+ bool GetBorder() const { return border; }
+ void SetBorder(bool bNewValue);
+ Color GetBorderColor() const { return border_color; }
+ void SetBorderColor(Color c);
+ short GetButtonState() const { return button_state; }
+ void SetButtonState(short nNewValue);
+
+ bool IsListShowing() const { return list_showing; }
+
+protected:
+ Rect CalcLabelRect();
+ void DrawRectSimple(Rect& rect, int stat);
+
+ List<Text> items;
+ ComboList* list;
+ bool list_showing;
+ bool animated;
+ bool border;
+ int seln;
+ int captured;
+ int bevel_width;
+ int button_state;
+ int pre_state;
+
+ Color active_color;
+ Color border_color;
+};
+
+#endif ComboBox_h
+
diff --git a/nGenEx/ComboList.cpp b/nGenEx/ComboList.cpp
new file mode 100644
index 0000000..d6e1b2f
--- /dev/null
+++ b/nGenEx/ComboList.cpp
@@ -0,0 +1,434 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: ComboList.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Drop-down list of Buttons class
+*/
+
+#include "MemDebug.h"
+#include "ComboList.h"
+#include "ComboBox.h"
+#include "Button.h"
+#include "Video.h"
+#include "Bitmap.h"
+#include "Font.h"
+#include "Screen.h"
+
+// +--------------------------------------------------------------------+
+
+ComboList::ComboList(ComboBox* ctrl, ActiveWindow* p, int ax, int ay, int aw, int ah, int maxentries)
+ : ScrollWindow(p, ax, ay, aw, ah + (ah-2)*maxentries + 2, 0, 0, p), combo_box(ctrl)
+{
+ button_state = 0;
+ button_height = ah;
+ captured = 0;
+ seln = -1;
+ max_entries = maxentries;
+ scroll = 0;
+ scrolling = false;
+ border = true;
+ animated = true;
+ bevel_width = 5;
+
+ if (ctrl)
+ CopyStyle(*ctrl);
+
+ rect.h = (ah-2)*maxentries + 2;
+
+ int screen_height = Video::GetInstance()->Height();
+
+ if (rect.h > screen_height) {
+ rect.y = 0;
+ rect.h = screen_height;
+ }
+
+ else if (rect.y + rect.h > screen_height) {
+ rect.y = screen_height - rect.h;
+ }
+}
+
+ComboList::ComboList(ComboBox* ctrl, Screen* s, int ax, int ay, int aw, int ah, int maxentries)
+ : ScrollWindow(s, ax, ay, aw, ah + (ah-2)*maxentries + 2, 0), combo_box(ctrl)
+{
+ button_state = 0;
+ button_height = ah;
+ captured = 0;
+ seln = -1;
+ max_entries = maxentries;
+ scroll = 0;
+ scrolling = false;
+ border = true;
+ animated = true;
+ bevel_width = 5;
+
+ if (ctrl)
+ CopyStyle(*ctrl);
+
+ rect.h = (ah-2)*maxentries + 2;
+
+ int screen_height = Video::GetInstance()->Height();
+
+ if (rect.h > screen_height) {
+ rect.y = 0;
+ rect.h = screen_height;
+ }
+
+ else if (rect.y + rect.h > screen_height) {
+ rect.y = screen_height - rect.h;
+ }
+
+ screen->AddWindow(this); // just in case the base ctor couldn't do this
+}
+
+// +--------------------------------------------------------------------+
+
+ComboList::~ComboList()
+{
+ items.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ComboList::CopyStyle(const ComboBox& ctrl)
+{
+ active_color = ctrl.GetActiveColor();
+ border_color = ctrl.GetBorderColor();
+ fore_color = ctrl.GetForeColor();
+ back_color = ctrl.GetBackColor();
+
+ animated = ctrl.GetAnimated();
+ bevel_width = ctrl.GetBevelWidth();
+ border = ctrl.GetBorder();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ComboList::Show()
+{
+ if (!scrolling) {
+ scrolling = 1;
+ scroll = 0;
+ }
+
+ ScrollWindow::Show();
+}
+
+void
+ComboList::Hide()
+{
+ scrolling = 0;
+ scroll = 0;
+ ScrollWindow::Hide();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ComboList::Draw()
+{
+ int x = 0;
+ int y = 0;
+ int w = rect.w;
+ int h = button_height;
+
+ int index = 0;
+
+ // opening:
+ if (scrolling > 0) {
+ int limit = min(items.size(), max_entries);
+
+ if (scroll < limit) {
+ if (limit > 6)
+ scroll += 4;
+
+ else if (limit > 4)
+ scroll += 2;
+
+ else
+ scroll += 1;
+ }
+
+ if (scroll >= limit) {
+ scroll = limit;
+ scrolling = 0;
+ }
+ }
+
+ // closing:
+ else if (scrolling < 0) {
+ if (scroll > 0)
+ scroll--;
+ else
+ scrolling = 0;
+ }
+
+ if (items.size() < 1) {
+ Rect btn_rect(x,y,w,h);
+ DrawItem(" ", btn_rect, 0);
+ }
+ else {
+ ListIter<Text> item = items;
+ while (++item && index < max_entries) {
+ Rect btn_rect(x,y,w,h);
+ int sub_state = (index == seln) ? button_state : 0;
+
+ DrawItem(*item, btn_rect, sub_state);
+
+ y += button_height-2;
+ index++;
+ }
+ }
+
+ DrawRect(0,0,rect.w-1,rect.h-1,fore_color);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ComboList::DrawRectSimple(Rect& rect, int state)
+{
+ if (state && active_color != Color::Black)
+ FillRect(rect, active_color);
+ else
+ FillRect(rect, back_color);
+}
+
+void
+ComboList::DrawItem(Text label, Rect& btn_rect, int state)
+{
+ int x = btn_rect.x;
+ int y = btn_rect.y;
+ int w = btn_rect.w;
+ int h = btn_rect.h;
+
+ // draw the bevel:
+ DrawRectSimple(btn_rect, state);
+
+ // draw text here:
+ if (font && label.length()) {
+ int border_size = 4;
+
+ if (style & WIN_RAISED_FRAME && style & WIN_SUNK_FRAME)
+ border_size = 8;
+
+ Rect label_rect = CalcLabelRect(btn_rect);
+ int vert_space = label_rect.h;
+ int horz_space = label_rect.w;
+
+ DrawText(label.data(), 0, label_rect, DT_CALCRECT | DT_WORDBREAK | text_align);
+ vert_space = (vert_space - label_rect.h)/2;
+
+ label_rect.w = horz_space;
+
+ if (vert_space > 0)
+ label_rect.y += vert_space;
+
+ if (animated && state > 0) {
+ label_rect.x += state;
+ label_rect.y += state;
+ }
+
+ font->SetColor(fore_color);
+ DrawText(label.data(), 0, label_rect, DT_WORDBREAK | text_align);
+ }
+}
+
+Rect ComboList::CalcLabelRect(const Rect& btn_rect)
+{
+ // fit the text in the bevel:
+ Rect label_rect = btn_rect;
+ label_rect.Deflate(bevel_width, bevel_width);
+
+ return label_rect;
+}
+
+int ComboList::CalcSeln(int x, int y)
+{
+ y -= rect.y;
+
+ if (button_height < 1)
+ return 0;
+
+ return (int) (y / (button_height-2));
+}
+
+// +--------------------------------------------------------------------+
+
+int ComboList::OnMouseMove(int x, int y)
+{
+ if (captured)
+ {
+ ActiveWindow* test = GetCapture();
+
+ if (test != this)
+ {
+ captured = false;
+ button_state = -1;
+ }
+ }
+
+ int new_seln = CalcSeln(x,y);
+
+ if (new_seln != seln) {
+ seln = new_seln;
+ Button::PlaySound(Button::SND_COMBO_HILITE);
+ }
+
+ return ActiveWindow::OnMouseMove(x,y);
+}
+
+int ComboList::OnLButtonDown(int x, int y)
+{
+ if (!captured)
+ captured = SetCapture();
+
+ seln = CalcSeln(x,y);
+ button_state = 1;
+
+ return ActiveWindow::OnLButtonDown(x,y);
+}
+
+int ComboList::OnLButtonUp(int x, int y)
+{
+ if (captured) {
+ ReleaseCapture();
+ captured = 0;
+ }
+
+ seln = CalcSeln(x,y);
+ button_state = -1;
+ return ActiveWindow::OnLButtonUp(x,y);
+}
+
+int ComboList::OnClick()
+{
+ return ActiveWindow::OnClick();
+}
+
+int ComboList::OnMouseEnter(int mx, int my)
+{
+ if (button_state == 0)
+ button_state = -1;
+
+ return ActiveWindow::OnMouseEnter(mx, my);
+}
+
+int ComboList::OnMouseExit(int mx, int my)
+{
+ if (button_state == -1)
+ button_state = 0;
+
+ return ActiveWindow::OnMouseExit(mx, my);
+}
+
+void ComboList::KillFocus()
+{
+ if (combo_box)
+ combo_box->HideList();
+}
+
+// +--------------------------------------------------------------------+
+
+void ComboList::ClearItems()
+{
+ items.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void ComboList::AddItem(const char* item)
+{
+ items.append(new(__FILE__,__LINE__) Text(item));
+}
+
+void ComboList::AddItems(ListIter<Text> item)
+{
+ while (++item)
+ items.append(new(__FILE__,__LINE__) Text(*item));
+}
+
+void ComboList::SetItems(ListIter<Text> item)
+{
+ items.destroy();
+ while (++item)
+ items.append(new(__FILE__,__LINE__) Text(*item));
+
+ // Resize window:
+
+ int ah = button_height;
+ max_entries = items.size();
+ Rect r = rect;
+ r.y = combo_box->Y();
+ int length = max_entries;
+
+ if (length < 1)
+ length = 1;
+
+ r.h = (ah-2)*length + 2;
+
+ int screen_height = Video::GetInstance()->Height();
+
+ if (r.h > screen_height) {
+ r.y = 0;
+ r.h = screen_height;
+ }
+
+ else if (r.y + r.h > screen_height) {
+ r.y = screen_height - r.h;
+ }
+
+ MoveTo(r);
+}
+
+const char* ComboList::GetItem(int index)
+{
+ if (index >= 0 && index < items.size())
+ return items[index]->data();
+ else
+ return 0;
+}
+
+void ComboList::SetItem(int index, const char* item)
+{
+ if (index >= 0 && index < items.size())
+ *items[index] = item;
+ else
+ items.append(new(__FILE__,__LINE__) Text(item));
+}
+
+int ComboList::GetCount()
+{
+ return items.size();
+}
+
+const char* ComboList::GetSelectedItem()
+{
+ if (seln >= 0 && seln < items.size())
+ return items[seln]->data();
+ else
+ return 0;
+}
+
+int ComboList::GetSelectedIndex()
+{
+ if (seln >= 0 && seln < items.size())
+ return seln;
+ else
+ return -1;
+}
+
+void ComboList::SetSelection(int index)
+{
+ if (index >= 0 && index < items.size())
+ seln = index;
+}
+
diff --git a/nGenEx/ComboList.h b/nGenEx/ComboList.h
new file mode 100644
index 0000000..d4ea794
--- /dev/null
+++ b/nGenEx/ComboList.h
@@ -0,0 +1,92 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: ComboList.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ ComboList class
+*/
+
+#ifndef ComboList_h
+#define ComboList_h
+
+#include "Types.h"
+#include "ScrollWindow.h"
+#include "Bitmap.h"
+
+#include "List.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class ComboBox;
+
+// +--------------------------------------------------------------------+
+
+class ComboList : public ScrollWindow
+{
+public:
+ static const char* TYPENAME() { return "ComboList"; }
+
+ ComboList(ComboBox* ctrl, ActiveWindow* p, int ax, int ay, int aw, int ah, int maxentries);
+ ComboList(ComboBox* ctrl, Screen* s, int ax, int ay, int aw, int ah, int maxentries);
+ virtual ~ComboList();
+
+ // Operations:
+ virtual void Draw();
+ virtual void Show();
+ virtual void Hide();
+
+ // Event Target Interface:
+ virtual int OnMouseMove(int x, int y);
+ virtual int OnLButtonDown(int x, int y);
+ virtual int OnLButtonUp(int x, int y);
+ virtual int OnClick();
+ virtual int OnMouseEnter(int x, int y);
+ virtual int OnMouseExit(int x, int y);
+ virtual void KillFocus();
+
+ // Property accessors:
+ virtual void ClearItems();
+ virtual void AddItem(const char* item);
+ virtual void AddItems(ListIter<Text> item_list);
+ virtual void SetItems(ListIter<Text> item_list);
+ virtual const char* GetItem(int index);
+ virtual void SetItem(int index, const char* item);
+
+ virtual int GetCount();
+ virtual const char* GetSelectedItem();
+ virtual int GetSelectedIndex();
+ virtual void SetSelection(int index);
+
+protected:
+ void DrawRectSimple(Rect& rect, int stat);
+ void DrawItem(Text label, Rect& btn_rect, int state);
+ Rect CalcLabelRect(const Rect& btn_rect);
+ int CalcSeln(int x, int y);
+ void CopyStyle(const ComboBox& ctrl);
+
+ ComboBox* combo_box;
+ List<Text> items;
+ bool animated;
+ bool border;
+ int seln;
+ int captured;
+ int bevel_width;
+ int button_state;
+ int button_height;
+ int max_entries;
+ int scroll;
+ int scrolling;
+
+ Color active_color;
+ Color border_color;
+};
+
+#endif ComboList_h
+
diff --git a/nGenEx/ContentBundle.cpp b/nGenEx/ContentBundle.cpp
new file mode 100644
index 0000000..ca6c8e5
--- /dev/null
+++ b/nGenEx/ContentBundle.cpp
@@ -0,0 +1,137 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2006. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: ContentBundle.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Chained collection of localized strings
+*/
+
+#include "MemDebug.h"
+#include "ContentBundle.h"
+#include "DataLoader.h"
+
+void Print(const char* fmt, ...);
+
+// +--------------------------------------------------------------------+
+
+ContentBundle::ContentBundle(const char* bundle, Locale* locale)
+{
+ Text file = FindFile(bundle, locale);
+ if (file.length() > 0) {
+ LoadBundle(file);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+ContentBundle::~ContentBundle()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+Text
+ContentBundle::GetText(const char* key) const
+{
+ return values.find(key, Text(key));
+}
+
+// +--------------------------------------------------------------------+
+
+Text
+ContentBundle::FindFile(const char* bundle, Locale* locale)
+{
+ Text result;
+ Text basename = Text(bundle);
+ DataLoader* loader = DataLoader::GetLoader();
+
+ if (loader && bundle) {
+ if (locale) {
+ result = basename + locale->GetFullCode() + ".txt";
+
+ if (loader->FindFile(result))
+ return result;
+
+ result = basename + "_" + locale->GetLanguage() + ".txt";
+
+ if (loader->FindFile(result))
+ return result;
+ }
+
+ result = basename + ".txt";
+
+ if (loader->FindFile(result))
+ return result;
+ }
+
+ return Text();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ContentBundle::LoadBundle(const char* filename)
+{
+ DataLoader* loader = DataLoader::GetLoader();
+ if (loader && filename && *filename) {
+ BYTE* buffer = 0;
+ loader->LoadBuffer(filename, buffer, true, true);
+ if (buffer && *buffer) {
+ char key[1024];
+ char val[2048];
+ char* p = (char*) buffer;
+ int s = 0, ik = 0, iv = 0;
+
+ key[0] = 0;
+ val[0] = 0;
+
+ while (*p) {
+ if (*p == '=') {
+ s = 1;
+ }
+ else if (*p == '\n' || *p == '\r') {
+ if (key[0] && val[0])
+ values.insert(Text(key).trim(), Text(val).trim());
+
+ ZeroMemory(key, 1024);
+ ZeroMemory(val, 2048);
+ s = 0;
+ ik = 0;
+ iv = 0;
+ }
+ else if (s == 0) {
+ if (!key[0]) {
+ if (*p == '#') {
+ s = -1; // comment
+ }
+ else if (!isspace(*p)) {
+ key[ik++] = *p;
+ }
+ }
+ else {
+ key[ik++] = *p;
+ }
+ }
+ else if (s == 1) {
+ if (!isspace(*p)) {
+ s = 2;
+ val[iv++] = *p;
+ }
+ }
+ else if (s == 2) {
+ val[iv++] = *p;
+ }
+
+ p++;
+ }
+
+ loader->ReleaseBuffer(buffer);
+ }
+ }
+}
diff --git a/nGenEx/ContentBundle.h b/nGenEx/ContentBundle.h
new file mode 100644
index 0000000..d322466
--- /dev/null
+++ b/nGenEx/ContentBundle.h
@@ -0,0 +1,48 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2006. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: ContentBundle.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Chained collection of localized strings
+*/
+
+#ifndef ContentBundle_h
+#define ContentBundle_h
+
+#include "Types.h"
+#include "Dictionary.h"
+#include "Text.h"
+#include "Locale.h"
+
+// +--------------------------------------------------------------------+
+
+class ContentBundle
+{
+public:
+ static const char* TYPENAME() { return "ContentBundle"; }
+
+ ContentBundle(const char* bundle, Locale* locale);
+ virtual ~ContentBundle();
+
+ int operator == (const ContentBundle& that) const { return this == &that; }
+
+ const Text& GetName() const { return name; }
+ Text GetText(const char* key) const;
+ bool IsLoaded() const { return !values.isEmpty(); }
+
+protected:
+ void LoadBundle(const char* filename);
+ Text FindFile(const char* bundle, Locale* locale);
+
+ Text name;
+ Dictionary<Text> values;
+};
+
+#endif ContentBundle_h
+
diff --git a/nGenEx/D3DXImage.cpp b/nGenEx/D3DXImage.cpp
new file mode 100644
index 0000000..3300253
--- /dev/null
+++ b/nGenEx/D3DXImage.cpp
@@ -0,0 +1,222 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: D3DXImage.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ D3DX image file loader
+*/
+
+
+#include "MemDebug.h"
+#include "D3DXImage.h"
+#include "VideoDX9.h"
+
+// +--------------------------------------------------------------------+
+
+D3DXImage::D3DXImage()
+ : width(0), height(0), format(0), image(0)
+{ }
+
+D3DXImage::D3DXImage(WORD w, WORD h, DWORD* img)
+{
+ ZeroMemory(this, sizeof(D3DXImage));
+
+ width = w;
+ height = h;
+
+ int pixels = width * height;
+
+ image = new(__FILE__,__LINE__) DWORD [pixels];
+
+ if (image && pixels) {
+ for (int i = 0; i < pixels; i++)
+ image[i] = img[i];
+ }
+}
+
+D3DXImage::~D3DXImage()
+{
+ delete [] image;
+}
+
+// +--------------------------------------------------------------------+
+
+bool D3DXImage::Load(char *filename)
+{
+ bool success = false;
+ FILE* f;
+
+ f = fopen(filename,"rb");
+ if (f == NULL)
+ return success;
+
+ int len = 0;
+ BYTE* buf = 0;
+
+ fseek(f, 0, SEEK_END);
+ len = ftell(f);
+ fseek(f, 0, SEEK_SET);
+
+ buf = new(__FILE__,__LINE__) BYTE[len];
+
+ if (buf) {
+ fread(buf, len, 1, f);
+ fclose(f);
+
+ success = LoadBuffer(buf, len);
+ }
+
+ return success;
+}
+
+// +--------------------------------------------------------------------+
+
+bool D3DXImage::LoadBuffer(BYTE* buf, int len)
+{
+ bool success = false;
+ HRESULT hr = E_FAIL;
+ D3DXIMAGE_INFO info;
+
+ if (buf == NULL)
+ return success;
+
+ hr = D3DXGetImageInfoFromFileInMemory(buf, len, &info);
+
+ if (FAILED(hr))
+ return success;
+
+ width = info.Width;
+ height = info.Height;
+ format = info.Format;
+
+ if (image) {
+ delete [] image;
+ image = 0;
+ }
+
+ IDirect3DSurface9* surf = 0;
+ IDirect3DDevice9* dev = VideoDX9::GetD3DDevice9();
+
+
+ hr = dev->CreateOffscreenPlainSurface( width,
+ height,
+ D3DFMT_A8R8G8B8,
+ D3DPOOL_SYSTEMMEM,
+ &surf,
+ NULL);
+
+ if (FAILED(hr))
+ return success;
+
+ hr = D3DXLoadSurfaceFromFileInMemory( surf, // dest surface
+ NULL, // dest palette (none)
+ NULL, // dest rect (entire image)
+ buf, // memory file
+ len, // size of data
+ NULL, // source rect (entire image)
+ D3DX_DEFAULT, // filter operation
+ 0, // color key (none)
+ NULL); // image info
+
+ if (SUCCEEDED(hr)) {
+ D3DLOCKED_RECT locked_rect;
+ hr = surf->LockRect(&locked_rect, NULL, D3DLOCK_READONLY);
+
+ if (SUCCEEDED(hr)) {
+ // copy surface into image
+ image = new(__FILE__,__LINE__) DWORD[width*height];
+ if (image) {
+ for (DWORD i = 0; i < height; i++) {
+ BYTE* dst = (BYTE*) (image + i * width);
+ BYTE* src = (BYTE*) locked_rect.pBits + i * locked_rect.Pitch;
+
+ CopyMemory(dst, src, width * sizeof(DWORD));
+ }
+
+ success = true;
+ }
+
+ surf->UnlockRect();
+ }
+ }
+
+ surf->Release();
+ surf = 0;
+
+ return success;
+}
+
+// +--------------------------------------------------------------------+
+
+bool D3DXImage::Save(char *filename)
+{
+ bool success = false;
+ HRESULT hr = E_FAIL;
+
+ if (!image || !width || !height)
+ return success;
+
+ FILE* f = fopen(filename,"wb");
+ if (f == NULL)
+ return success;
+
+ IDirect3DSurface9* surf = 0;
+ IDirect3DDevice9* dev = VideoDX9::GetD3DDevice9();
+
+ hr = dev->CreateOffscreenPlainSurface( width,
+ height,
+ D3DFMT_A8R8G8B8,
+ D3DPOOL_SYSTEMMEM,
+ &surf,
+ NULL);
+
+
+ if (SUCCEEDED(hr)) {
+ D3DLOCKED_RECT locked_rect;
+ hr = surf->LockRect(&locked_rect, NULL, 0);
+
+ if (SUCCEEDED(hr)) {
+ // copy image into surface
+ for (DWORD i = 0; i < height; i++) {
+ BYTE* src = (BYTE*) (image + i * width);
+ BYTE* dst = (BYTE*) locked_rect.pBits + i * locked_rect.Pitch;
+
+ CopyMemory(dst, src, width * sizeof(DWORD));
+ }
+
+ surf->UnlockRect();
+
+ ID3DXBuffer* buffer = 0;
+ D3DXIMAGE_FILEFORMAT imgfmt = D3DXIFF_PNG;
+
+ if (strstr(filename, ".jpg") || strstr(filename, ".JPG"))
+ imgfmt = D3DXIFF_JPG;
+
+ else if (strstr(filename, ".bmp") || strstr(filename, ".BMP"))
+ imgfmt = D3DXIFF_BMP;
+
+ hr = D3DXSaveSurfaceToFileInMemory(&buffer, // destination
+ imgfmt, // type of file
+ surf, // image to save
+ NULL, // palette
+ NULL); // source rect (entire image)
+
+ if (SUCCEEDED(hr)) {
+ fwrite(buffer->GetBufferPointer(), buffer->GetBufferSize(), 1, f);
+ success = true;
+ }
+ }
+ }
+
+ surf->Release();
+ surf = 0;
+ fclose(f);
+ return success;
+}
+
diff --git a/nGenEx/D3DXImage.h b/nGenEx/D3DXImage.h
new file mode 100644
index 0000000..2150ab0
--- /dev/null
+++ b/nGenEx/D3DXImage.h
@@ -0,0 +1,43 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: D3DXImage.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ D3DX image file loader
+*/
+
+#ifndef D3DXImage_h
+#define D3DXImage_h
+
+#include "Types.h"
+
+// +--------------------------------------------------------------------+
+
+struct D3DXImage
+{
+ static const char* TYPENAME() { return "D3DXImage"; }
+
+ D3DXImage();
+ D3DXImage(WORD w, WORD h, DWORD* img);
+ ~D3DXImage();
+
+ bool Load(char *filename);
+ bool Save(char *filename);
+
+ bool LoadBuffer(BYTE* buf, int len);
+
+ DWORD* image;
+ DWORD width;
+ DWORD height;
+ DWORD format;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif D3DXImage_h
diff --git a/nGenEx/DataLoader.cpp b/nGenEx/DataLoader.cpp
new file mode 100644
index 0000000..354232b
--- /dev/null
+++ b/nGenEx/DataLoader.cpp
@@ -0,0 +1,1013 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: DataLoader.cpp
+ AUTHOR: John DiCamillo
+
+*/
+
+#include "MemDebug.h"
+#include "DataLoader.h"
+#include "Archive.h"
+#include "Color.h"
+#include "D3DXImage.h"
+#include "Bitmap.h"
+#include "Bmp.h"
+#include "PCX.h"
+#include "Sound.h"
+#include "Resource.h"
+#include "Video.h"
+#include "Wave.h"
+
+// +------------------------------------------------------------------+
+
+static DataLoader* def_loader = 0;
+ DataLoader* DataLoader::loader = 0;
+
+static List<DataArchive> archives;
+
+// +--------------------------------------------------------------------+
+
+DataLoader::DataLoader()
+ : datapath(""), video(0), use_file_system(true), enable_media(true)
+{ }
+
+// +--------------------------------------------------------------------+
+
+void
+DataLoader::UseVideo(Video* v)
+{
+ video = v;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+DataLoader::Initialize()
+{
+ def_loader = new(__FILE__,__LINE__) DataLoader;
+ loader = def_loader;
+
+ archives.destroy();
+}
+
+void
+DataLoader::Close()
+{
+ archives.destroy();
+ Bitmap::ClearCache();
+
+ delete def_loader;
+ def_loader = 0;
+ loader = 0;
+}
+
+void
+DataLoader::Reset()
+{
+ Close();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+DataLoader::UseFileSystem(bool use)
+{
+ use_file_system = use;
+}
+
+void
+DataLoader::EnableMedia(bool enable)
+{
+ enable_media = enable;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+DataLoader::EnableDatafile(const char* name)
+{
+ int status = DATAFILE_NOTEXIST;
+
+ FILE* f = fopen(name, "rb");
+ if (f) {
+ ::fclose(f);
+
+ DataArchive* a = new(__FILE__,__LINE__) DataArchive(name);
+
+ if (a && a->NumFiles() >= 1) {
+ status = DATAFILE_OK;
+
+ bool found = false;
+ ListIter<DataArchive> iter = archives;
+ while (++iter && !found) {
+ DataArchive* archive = iter.value();
+ if (!strcmp(archive->Name(), name)) {
+ found = true;
+ }
+ }
+
+ if (!found)
+ archives.append(a);
+ }
+ else {
+ Print(" WARNING: invalid data file '%s'\n", name);
+ status = DATAFILE_INVALID;
+
+ delete a;
+ }
+
+ loader = this;
+ }
+ else {
+ Print(" WARNING: could not open datafile '%s'\n", name);
+ status = DATAFILE_NOTEXIST;
+ }
+
+ return status;
+}
+
+int
+DataLoader::DisableDatafile(const char* name)
+{
+ ListIter<DataArchive> iter = archives;
+ while (++iter) {
+ DataArchive* a = iter.value();
+ if (!strcmp(a->Name(), name)) {
+ delete iter.removeItem();
+ return DATAFILE_OK;
+ }
+ }
+
+ return DATAFILE_NOTEXIST;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+DataLoader::SetDataPath(const char* path)
+{
+ if (path)
+ datapath = path;
+ else
+ datapath = "";
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+DataLoader::FindFile(const char* name)
+{
+ // assemble file name:
+ char filename[1024];
+ strcpy(filename, datapath);
+ strcat(filename, name);
+
+ // first check current directory:
+ if (use_file_system) {
+ FILE* f = ::fopen(filename, "rb");
+
+ if (f) {
+ ::fclose(f);
+ return true;
+ }
+ }
+
+ // then check datafiles, from last to first:
+ int narchives = archives.size();
+ for (int i = 0; i < narchives; i++) {
+ DataArchive* a = archives[narchives-1-i];
+ if (a->FindEntry(filename) > -1) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+DataLoader::ListFiles(const char* filter, List<Text>& list, bool recurse)
+{
+ list.destroy();
+
+ ListFileSystem(filter, list, datapath, recurse);
+
+ // then check datafile(s):
+ int narchives = archives.size();
+ for (int i = 0; i < narchives; i++) {
+ DataArchive* a = archives[narchives-1-i];
+ ListArchiveFiles(a->Name(), filter, list);
+ }
+
+ return list.size();
+}
+
+int
+DataLoader::ListArchiveFiles(const char* archive_name, const char* filter, List<Text> &list)
+{
+ int pathlen = datapath.length();
+ DataArchive* a = 0;
+
+ if (archive_name) {
+ int narchives = archives.size();
+ for (int i = 0; i < narchives && !a; i++) {
+ a = archives[narchives-1-i];
+
+ if (stricmp(a->Name(), archive_name))
+ a = 0;
+ }
+ }
+
+ if (!a) {
+ ListFileSystem(filter, list, datapath, true);
+ return list.size();
+ }
+
+ if (!strcmp(filter, "*.*")) {
+ int count = a->NumFiles();
+ for (int n = 0; n < count; n++) {
+ DataEntry* entry = a->GetFile(n);
+ Text entry_name = entry->name;
+ entry_name.setSensitive(false);
+
+ if (entry_name.contains(datapath)) {
+ Text fname = entry_name(pathlen, 1000);
+
+ if (!list.contains(&fname))
+ list.append(new Text(fname));
+ }
+ }
+ }
+ else {
+ char data_filter[256];
+ ZeroMemory(data_filter, 256);
+
+ const char* pf = filter;
+ char* pdf = data_filter;
+
+ while (*pf) {
+ if (*pf != '*')
+ *pdf++ = *pf;
+ pf++;
+ }
+
+ int count = a->NumFiles();
+ for (int n = 0; n < count; n++) {
+ DataEntry* entry = a->GetFile(n);
+ Text entry_name = entry->name;
+ entry_name.setSensitive(false);
+
+ if (entry_name.contains(datapath) && entry_name.contains(data_filter)) {
+ Text fname = entry_name(pathlen, 1000);
+
+ if (!list.contains(&fname))
+ list.append(new Text(fname));
+ }
+ }
+ }
+
+ return list.size();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+DataLoader::ListFileSystem(const char* filter, List<Text>& list, Text base_path, bool recurse)
+{
+ if (use_file_system) {
+ char data_filter[256];
+ ZeroMemory(data_filter, 256);
+
+ const char* pf = filter;
+ char* pdf = data_filter;
+
+ while (*pf) {
+ if (*pf != '*')
+ *pdf++ = *pf;
+ pf++;
+ }
+
+ int pathlen = base_path.length();
+
+ // assemble win32 find filter:
+ char win32_filter[1024];
+ strcpy(win32_filter, datapath);
+
+ if (recurse)
+ strcat(win32_filter, "*.*");
+ else
+ strcat(win32_filter, filter);
+
+ // first check current directory:
+ WIN32_FIND_DATA data;
+ HANDLE hFind = FindFirstFile(win32_filter, &data);
+
+ if (hFind != INVALID_HANDLE_VALUE) {
+ do {
+ if ((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
+ if (recurse && data.cFileName[0] != '.') {
+ Text old_datapath = datapath;
+
+ Text newpath = datapath;
+ newpath += data.cFileName;
+ newpath += "/";
+ datapath = newpath;
+
+ ListFileSystem(filter, list, base_path, recurse);
+
+ datapath = old_datapath;
+ }
+ }
+ else {
+ if (!strcmp(filter, "*.*") || strstr(data.cFileName, data_filter)) {
+ Text full_name = datapath;
+ full_name += data.cFileName;
+
+ list.append(new Text(full_name(pathlen, 1000)));
+ }
+ }
+ }
+ while (FindNextFile(hFind, &data));
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+int
+DataLoader::LoadBuffer(const char* name, BYTE*& buf, bool null_terminate, bool optional)
+{
+ buf = 0;
+
+ // assemble file name:
+ char filename[1024];
+ strcpy(filename, datapath);
+ strcat(filename, name);
+
+ if (use_file_system) {
+ // first check current directory:
+ FILE* f = ::fopen(filename, "rb");
+
+ if (f) {
+ ::fseek(f, 0, SEEK_END);
+ int len = ftell(f);
+ ::fseek(f, 0, SEEK_SET);
+
+ if (null_terminate) {
+ buf = new(__FILE__,__LINE__) BYTE[len+1];
+ if (buf)
+ buf[len] = 0;
+ }
+
+ else {
+ buf = new(__FILE__,__LINE__) BYTE[len];
+ }
+
+ if (buf)
+ ::fread(buf, len, 1, f);
+
+ ::fclose(f);
+
+ return len;
+ }
+ }
+
+ // then check datafile(s):
+ int narchives = archives.size();
+
+ // vox files are usually in their own archive,
+ // so check there first
+ if (narchives > 1 && strstr(filename, "Vox")) {
+ for (int i = 0; i < narchives; i++) {
+ DataArchive* a = archives[narchives-1-i];
+ if (strstr(a->Name(), "vox")) {
+ int index = a->FindEntry(filename);
+ if (index > -1)
+ return a->ExpandEntry(index, buf, null_terminate);
+ }
+ }
+ }
+
+ for (int i = 0; i < narchives; i++) {
+ DataArchive* a = archives[narchives-1-i];
+ int index = a->FindEntry(filename);
+ if (index > -1)
+ return a->ExpandEntry(index, buf, null_terminate);
+ }
+
+ if (!optional)
+ Print("WARNING - DataLoader could not load buffer '%s'\n", filename);
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+DataLoader::LoadPartialFile(const char* name, BYTE*& buf, int max_load, bool optional)
+{
+ buf = 0;
+
+ // assemble file name:
+ char filename[1024];
+ strcpy(filename, datapath);
+ strcat(filename, name);
+
+ // first check current directory:
+ FILE* f = ::fopen(filename, "rb");
+
+ if (f) {
+ ::fseek(f, 0, SEEK_END);
+ int len = ftell(f);
+ ::fseek(f, 0, SEEK_SET);
+
+ if (len > max_load) {
+ len = max_load;
+ }
+
+ buf = new(__FILE__,__LINE__) BYTE[len];
+
+ if (buf)
+ ::fread(buf, len, 1, f);
+
+ ::fclose(f);
+
+ return len;
+ }
+
+ if (!optional)
+ Print("WARNING - DataLoader could not load partial file '%s'\n", filename);
+ return 0;
+}
+
+int
+DataLoader::fread(void* buffer, size_t size, size_t count, BYTE*& stream)
+{
+ CopyMemory(buffer, stream, size*count);
+ stream += size*count;
+
+ return size*count;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+DataLoader::ReleaseBuffer(BYTE*& buf)
+{
+ delete [] buf;
+ buf = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+DataLoader::CacheBitmap(const char* name, Bitmap*& bitmap, int type, bool optional)
+{
+ int result = 0;
+
+ // search cache:
+ bitmap = Bitmap::CheckCache(name);
+ if (bitmap) return 1;
+
+ // not in cache yet:
+ bitmap = new(__FILE__,__LINE__) Bitmap;
+
+ if (bitmap)
+ result = LoadBitmap(name, *bitmap, type, optional);
+
+ if (result && bitmap)
+ Bitmap::AddToCache(bitmap);
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+DataLoader::LoadBitmap(const char* name, Bitmap& bitmap, int type, bool optional)
+{
+ if (!enable_media)
+ return 0;
+
+ int result = LoadIndexed(name, bitmap, type);
+
+ // check for a matching high color bitmap:
+ if (result == 1) {
+ int hi_result = LoadHiColor(name, bitmap, type);
+
+ if (hi_result == 2)
+ result = 3;
+ }
+
+ bitmap.SetFilename(name);
+
+ if (!result && !optional)
+ Print("WARNING - DataLoader could not load bitmap '%s%s'\n", datapath.data(), name);
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+DataLoader::LoadTexture(const char* name, Bitmap*& bitmap, int type, bool preload_cache, bool optional)
+{
+ if (!enable_media)
+ return 0;
+
+ int result = 0;
+
+ // assemble file name:
+ char filename[256];
+ strcpy(filename, datapath);
+ strcat(filename, name);
+
+ // search cache:
+ bitmap = Bitmap::CheckCache(filename);
+ if (bitmap) return 1;
+
+ // not in cache yet:
+ bitmap = new(__FILE__,__LINE__) Bitmap;
+
+ if (bitmap) {
+ result = LoadHiColor(name, *bitmap, type);
+
+ if (!result) {
+ result = LoadIndexed(name, *bitmap, type);
+ }
+
+ bitmap->SetFilename(filename);
+
+ if (result) {
+ bitmap->MakeTexture();
+ Bitmap::AddToCache(bitmap);
+ }
+ else {
+ delete bitmap;
+ bitmap = 0;
+
+ if (!optional)
+ Print("WARNING - DataLoader could not load texture '%s%s'\n", datapath.data(), name);
+ }
+ }
+ else if (!optional) {
+ Print("WARNING - DataLoader could not allocate texture '%s%s'\n", datapath.data(), name);
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+DataLoader::LoadIndexed(const char* name, Bitmap& bitmap, int type)
+{
+ if (!enable_media)
+ return 0;
+
+ int result = 0;
+ PcxImage pcx;
+ D3DXImage d3dx;
+ bool pcx_file = strstr(name, ".pcx") || strstr(name, ".PCX");
+
+ // assemble file name:
+ char filename[256];
+ strcpy(filename, datapath);
+ strcat(filename, name);
+
+ // first try to load from current directory:
+ bool loaded = false;
+
+ if (use_file_system) {
+ if (pcx_file)
+ loaded = pcx.Load(filename) == PCX_OK;
+
+ else
+ loaded = d3dx.Load(filename);
+ }
+
+ if (!loaded) {
+ // then check datafile:
+ int len = 0;
+ BYTE* tmp_buf = 0;
+
+ int narchives = archives.size();
+ for (int i = 0; i < narchives; i++) {
+ DataArchive* a = archives[narchives-1-i];
+ int index = a->FindEntry(filename);
+ if (index > -1) {
+ len = a->ExpandEntry(index, tmp_buf);
+
+ if (pcx_file)
+ pcx.LoadBuffer(tmp_buf, len);
+
+ else
+ d3dx.LoadBuffer(tmp_buf, len);
+
+ ReleaseBuffer(tmp_buf);
+ break;
+ }
+ }
+ }
+
+ // now copy the image into the bitmap:
+ if (pcx_file) {
+ if (pcx.bitmap) {
+ bitmap.CopyImage(pcx.width, pcx.height, pcx.bitmap, type);
+ result = 1;
+ }
+
+ else if (pcx.himap) {
+ bitmap.CopyHighColorImage(pcx.width, pcx.height, pcx.himap, type);
+ result = 2;
+ }
+
+ if (result == 2)
+ LoadAlpha(name, bitmap, type);
+ }
+
+ else {
+ if (d3dx.image) {
+ bitmap.CopyHighColorImage(d3dx.width, d3dx.height, d3dx.image, type);
+ result = 2;
+ }
+
+ if (result == 2) {
+ LoadAlpha(name, bitmap, type);
+ }
+ }
+
+ return result;
+}
+
+int
+DataLoader::LoadHiColor(const char* name, Bitmap& bitmap, int type)
+{
+ if (!enable_media)
+ return 0;
+
+ int result = 0;
+ PcxImage pcx;
+ D3DXImage d3dx;
+ bool pcx_file = strstr(name, ".pcx") || strstr(name, ".PCX");
+ bool bmp_file = strstr(name, ".bmp") || strstr(name, ".BMP");
+ bool png_file = strstr(name, ".png") || strstr(name, ".PNG");
+
+ // check for a matching high color bitmap:
+ char filename[256];
+ char name2[256];
+ strcpy(name2, name);
+
+ char* dot = strrchr(name2, '.');
+ if (dot && pcx_file)
+ strcpy(dot, "+.pcx");
+ else if (dot && bmp_file)
+ strcpy(dot, "+.bmp");
+ else if (dot && png_file)
+ strcpy(dot, "+.png");
+ else
+ return result;
+
+ strcpy(filename, datapath);
+ strcat(filename, name2);
+
+ // first try to load from current directory:
+ bool loaded = false;
+
+ if (use_file_system) {
+ if (pcx_file)
+ loaded = pcx.Load(filename) == PCX_OK;
+
+ else
+ loaded = d3dx.Load(filename);
+ }
+
+ if (!loaded) {
+ // then check datafile:
+ int len = 0;
+ BYTE* tmp_buf = 0;
+
+ int narchives = archives.size();
+ for (int i = 0; i < narchives; i++) {
+ DataArchive* a = archives[narchives-1-i];
+ int index = a->FindEntry(filename);
+ if (index > -1) {
+ len = a->ExpandEntry(index, tmp_buf);
+
+ if (pcx_file)
+ pcx.LoadBuffer(tmp_buf, len);
+ else
+ d3dx.LoadBuffer(tmp_buf, len);
+
+ ReleaseBuffer(tmp_buf);
+ break;
+ }
+ }
+ }
+
+ // now copy the image into the bitmap:
+ if (pcx_file && pcx.himap) {
+ bitmap.CopyHighColorImage(pcx.width, pcx.height, pcx.himap, type);
+ result = 2;
+ }
+
+ else if (d3dx.image) {
+ bitmap.CopyHighColorImage(d3dx.width, d3dx.height, d3dx.image, type);
+ result = 2;
+ }
+
+ if (result == 2)
+ LoadAlpha(name, bitmap, type);
+
+ return result;
+}
+
+int
+DataLoader::LoadAlpha(const char* name, Bitmap& bitmap, int type)
+{
+ if (!enable_media)
+ return 0;
+
+ PcxImage pcx;
+ D3DXImage d3dx;
+ bool pcx_file = strstr(name, ".pcx") || strstr(name, ".PCX");
+ bool bmp_file = strstr(name, ".bmp") || strstr(name, ".BMP");
+ bool png_file = strstr(name, ".png") || strstr(name, ".PNG");
+ bool tga_file = strstr(name, ".tga") || strstr(name, ".TGA");
+
+ // check for an associated alpha-only (grayscale) bitmap:
+ char filename[256];
+ char name2[256];
+ strcpy(name2, name);
+ char* dot = strrchr(name2, '.');
+ if (dot && pcx_file)
+ strcpy(dot, "@.pcx");
+ else if (dot && bmp_file)
+ strcpy(dot, "@.bmp");
+ else if (dot && png_file)
+ strcpy(dot, "@.png");
+ else if (dot && tga_file)
+ strcpy(dot, "@.tga");
+ else
+ return 0;
+
+ strcpy(filename, datapath);
+ strcat(filename, name2);
+
+ // first try to load from current directory:
+ bool loaded = false;
+
+ if (use_file_system) {
+ if (pcx_file)
+ loaded = pcx.Load(filename) == PCX_OK;
+
+ else
+ loaded = d3dx.Load(filename);
+ }
+
+ if (!loaded) {
+ // then check datafile:
+ int len = 0;
+ BYTE* tmp_buf = 0;
+
+ int narchives = archives.size();
+ for (int i = 0; i < narchives; i++) {
+ DataArchive* a = archives[narchives-1-i];
+ int index = a->FindEntry(filename);
+ if (index > -1) {
+ len = a->ExpandEntry(index, tmp_buf);
+
+ if (pcx_file)
+ pcx.LoadBuffer(tmp_buf, len);
+ else
+ d3dx.LoadBuffer(tmp_buf, len);
+
+ ReleaseBuffer(tmp_buf);
+ break;
+ }
+ }
+ }
+
+ // now copy the alpha values into the bitmap:
+ if (pcx_file && pcx.bitmap) {
+ bitmap.CopyAlphaImage(pcx.width, pcx.height, pcx.bitmap);
+ }
+ else if (pcx_file && pcx.himap) {
+ bitmap.CopyAlphaRedChannel(pcx.width, pcx.height, pcx.himap);
+ }
+ else if (d3dx.image) {
+ bitmap.CopyAlphaRedChannel(d3dx.width, d3dx.height, d3dx.image);
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+DataLoader::LoadSound(const char* name, Sound*& snd, DWORD flags, bool optional)
+{
+ if (!enable_media)
+ return 0;
+
+ if (strstr(name, ".ogg"))
+ return LoadStream(name, snd, optional);
+
+ int result = 0;
+
+ WAVE_HEADER head;
+ WAVE_FMT fmt;
+ WAVE_FACT fact;
+ WAVE_DATA data;
+ WAVEFORMATEX wfex;
+ LPBYTE wave;
+
+ LPBYTE buf;
+ LPBYTE p;
+ int len;
+
+ ZeroMemory(&head, sizeof(head));
+ ZeroMemory(&fmt, sizeof(fmt));
+ ZeroMemory(&fact, sizeof(fact));
+ ZeroMemory(&data, sizeof(data));
+
+ len = LoadBuffer(name, buf, false, optional);
+
+ if (len > sizeof(head)) {
+ CopyMemory(&head, buf, sizeof(head));
+
+ if (head.RIFF == MAKEFOURCC('R', 'I', 'F', 'F') &&
+ head.WAVE == MAKEFOURCC('W', 'A', 'V', 'E')) {
+
+ p = buf + sizeof(WAVE_HEADER);
+
+ do {
+ DWORD chunk_id = *((LPDWORD) p);
+
+ switch (chunk_id) {
+ case MAKEFOURCC('f', 'm', 't', ' '):
+ CopyMemory(&fmt, p, sizeof(fmt));
+ p += fmt.chunk_size + 8;
+ break;
+
+ case MAKEFOURCC('f', 'a', 'c', 't'):
+ CopyMemory(&fact, p, sizeof(fact));
+ p += fact.chunk_size + 8;
+ break;
+
+ case MAKEFOURCC('s', 'm', 'p', 'l'):
+ CopyMemory(&fact, p, sizeof(fact));
+ p += fact.chunk_size + 8;
+ break;
+
+ case MAKEFOURCC('d', 'a', 't', 'a'):
+ CopyMemory(&data, p, sizeof(data));
+ p += 8;
+ break;
+
+ default:
+ ReleaseBuffer(buf);
+ return result;
+ }
+ }
+ while (data.chunk_size == 0);
+
+ wfex.wFormatTag = fmt.wFormatTag;
+ wfex.nChannels = fmt.nChannels;
+ wfex.nSamplesPerSec = fmt.nSamplesPerSec;
+ wfex.nAvgBytesPerSec = fmt.nAvgBytesPerSec;
+ wfex.nBlockAlign = fmt.nBlockAlign;
+ wfex.wBitsPerSample = fmt.wBitsPerSample;
+ wfex.cbSize = 0;
+ wave = p;
+
+ snd = Sound::Create(flags, &wfex, data.chunk_size, wave);
+
+ if (snd)
+ result = data.chunk_size;
+ }
+ }
+
+ ReleaseBuffer(buf);
+ return result;
+}
+
+int
+DataLoader::LoadStream(const char* name, Sound*& snd, bool optional)
+{
+ if (!enable_media)
+ return 0;
+
+ if (!name)
+ return 0;
+
+ int namelen = strlen(name);
+
+ if (namelen < 5)
+ return 0;
+
+ if ((name[namelen-3] == 'o' || name[namelen-3] == 'O') &&
+ (name[namelen-2] == 'g' || name[namelen-2] == 'G') &&
+ (name[namelen-1] == 'g' || name[namelen-1] == 'G')) {
+
+ return LoadOggStream(name, snd);
+ }
+
+ int result = 0;
+
+ WAVE_HEADER head;
+ WAVE_FMT fmt;
+ WAVE_FACT fact;
+ WAVE_DATA data;
+ WAVEFORMATEX wfex;
+
+ LPBYTE buf;
+ LPBYTE p;
+ int len;
+
+ ZeroMemory(&head, sizeof(head));
+ ZeroMemory(&fmt, sizeof(fmt));
+ ZeroMemory(&fact, sizeof(fact));
+ ZeroMemory(&data, sizeof(data));
+
+ len = LoadPartialFile(name, buf, 4096, optional);
+
+ if (len > sizeof(head)) {
+ CopyMemory(&head, buf, sizeof(head));
+
+ if (head.RIFF == MAKEFOURCC('R', 'I', 'F', 'F') &&
+ head.WAVE == MAKEFOURCC('W', 'A', 'V', 'E')) {
+
+ p = buf + sizeof(WAVE_HEADER);
+
+ do {
+ DWORD chunk_id = *((LPDWORD) p);
+
+ switch (chunk_id) {
+ case MAKEFOURCC('f', 'm', 't', ' '):
+ CopyMemory(&fmt, p, sizeof(fmt));
+ p += fmt.chunk_size + 8;
+ break;
+
+ case MAKEFOURCC('f', 'a', 'c', 't'):
+ CopyMemory(&fact, p, sizeof(fact));
+ p += fact.chunk_size + 8;
+ break;
+
+ case MAKEFOURCC('s', 'm', 'p', 'l'):
+ CopyMemory(&fact, p, sizeof(fact));
+ p += fact.chunk_size + 8;
+ break;
+
+ case MAKEFOURCC('d', 'a', 't', 'a'):
+ CopyMemory(&data, p, sizeof(data));
+ p += 8;
+ break;
+
+ default:
+ ReleaseBuffer(buf);
+ return result;
+ }
+ }
+ while (data.chunk_size == 0);
+
+ wfex.wFormatTag = fmt.wFormatTag;
+ wfex.nChannels = fmt.nChannels;
+ wfex.nSamplesPerSec = fmt.nSamplesPerSec;
+ wfex.nAvgBytesPerSec = fmt.nAvgBytesPerSec;
+ wfex.nBlockAlign = fmt.nBlockAlign;
+ wfex.wBitsPerSample = fmt.wBitsPerSample;
+ wfex.cbSize = 0;
+
+ snd = Sound::Create(Sound::STREAMED, &wfex);
+
+ if (snd) {
+ // assemble file name:
+ char filename[1024];
+ strcpy(filename, datapath);
+ strcat(filename, name);
+
+ snd->StreamFile(filename, p - buf);
+
+ result = data.chunk_size;
+ }
+ }
+ }
+
+ ReleaseBuffer(buf);
+ return result;
+}
+
+int
+DataLoader::LoadOggStream(const char* name, Sound*& snd)
+{
+ if (!enable_media)
+ return 0;
+
+ snd = Sound::CreateOggStream(name);
+
+ return snd != 0;
+}
diff --git a/nGenEx/DataLoader.h b/nGenEx/DataLoader.h
new file mode 100644
index 0000000..87dbdbb
--- /dev/null
+++ b/nGenEx/DataLoader.h
@@ -0,0 +1,86 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: DataLoader.h
+ AUTHOR: John DiCamillo
+
+*/
+
+#ifndef DataLoader_h
+#define DataLoader_h
+
+#include "Types.h"
+#include "List.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class Bitmap;
+class Sound;
+class Video;
+
+// +--------------------------------------------------------------------+
+
+class DataLoader
+{
+public:
+ static const char* TYPENAME() { return "DataLoader"; }
+
+ enum { DATAFILE_OK, DATAFILE_INVALID, DATAFILE_NOTEXIST };
+
+ DataLoader();
+
+ static DataLoader* GetLoader() { return loader; }
+ static void Initialize();
+ static void Close();
+
+ void Reset();
+ void UseFileSystem(bool use=true);
+ void UseVideo(Video* v);
+ void EnableMedia(bool enable=true);
+
+ int EnableDatafile(const char* name);
+ int DisableDatafile(const char* name);
+
+ void SetDataPath(const char* path);
+ const char* GetDataPath() const { return datapath; }
+
+ bool IsFileSystemEnabled() const { return use_file_system; }
+ bool IsMediaLoadEnabled() const { return enable_media; }
+
+ bool FindFile(const char* fname);
+ int ListFiles(const char* filter, List<Text>& list, bool recurse=false);
+ int ListArchiveFiles(const char* archive, const char* filter, List<Text>& list);
+ int LoadBuffer(const char* name, BYTE*& buf, bool null_terminate=false, bool optional=false);
+ int LoadBitmap(const char* name, Bitmap& bmp, int type=0, bool optional=false);
+ int CacheBitmap(const char* name, Bitmap*& bmp, int type=0, bool optional=false);
+ int LoadTexture(const char* name, Bitmap*& bmp, int type=0, bool preload_cache=false, bool optional=false);
+ int LoadSound(const char* fname, Sound*& snd, DWORD flags=0, bool optional=false);
+ int LoadStream(const char* fname, Sound*& snd, bool optional=false);
+
+ void ReleaseBuffer(BYTE*& buf);
+ int fread(void* buffer, size_t size, size_t count, BYTE*& stream);
+
+private:
+ int LoadIndexed(const char* name, Bitmap& bmp, int type);
+ int LoadHiColor(const char* name, Bitmap& bmp, int type);
+ int LoadAlpha( const char* name, Bitmap& bmp, int type);
+
+ void ListFileSystem(const char* filter, List<Text>& list, Text base_path, bool recurse);
+ int LoadPartialFile(const char* fname, BYTE*& buf, int max_load, bool optional=false);
+ int LoadOggStream(const char* fname, Sound*& snd);
+
+ Text datapath;
+ Video* video;
+ bool use_file_system;
+ bool enable_media;
+
+ static DataLoader* loader;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif DataLoader_h
+
diff --git a/nGenEx/Director.h b/nGenEx/Director.h
new file mode 100644
index 0000000..7eda1b2
--- /dev/null
+++ b/nGenEx/Director.h
@@ -0,0 +1,44 @@
+/* Project nGen
+ John DiCamillo
+ Copyright © 1997-2002. All Rights Reserved.
+
+ SUBSYSTEM: nGen.lib
+ FILE: Director.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Abstract Director (AI or Human Input) for Physical Objects
+*/
+
+#ifndef Director_h
+#define Director_h
+
+#include "Types.h"
+#include "Geometry.h"
+
+// +--------------------------------------------------------------------+
+
+class Physical;
+
+// +--------------------------------------------------------------------+
+
+class Director
+{
+public:
+ Director() { }
+ virtual ~Director() { }
+
+ // accessors:
+ virtual int Type() const { return 0; }
+ virtual int Subframe() const { return 0; }
+
+ // operations
+ virtual void ExecFrame(double factor) { }
+};
+
+// +--------------------------------------------------------------------+
+
+#endif Director_h
+
diff --git a/nGenEx/EditBox.cpp b/nGenEx/EditBox.cpp
new file mode 100644
index 0000000..0cc780d
--- /dev/null
+++ b/nGenEx/EditBox.cpp
@@ -0,0 +1,452 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: EditBox.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ EditBox ActiveWindow class
+*/
+
+#include "MemDebug.h"
+#include "EditBox.h"
+#include "FormWindow.h"
+#include "Video.h"
+#include "Font.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+
+DWORD GetRealTime();
+
+// +--------------------------------------------------------------------+
+
+static int old_cursor;
+
+// +--------------------------------------------------------------------+
+
+EditBox::EditBox(ActiveWindow* p, int ax, int ay, int aw, int ah, DWORD aid)
+ : ScrollWindow(p->GetScreen(), ax, ay, aw, ah, aid, 0, p), caret_x(0), caret_y(0)
+{
+ sel_start = 0;
+ sel_length = 0;
+ h_offset = 0;
+ pass_char = 0;
+
+ selected_color = Color::Yellow;
+
+ char buf[32];
+ sprintf(buf, "EditBox %d", id);
+ desc = buf;
+}
+
+EditBox::EditBox(Screen* s, int ax, int ay, int aw, int ah, DWORD aid)
+ : ScrollWindow(s, ax, ay, aw, ah, aid), caret_x(0), caret_y(0)
+{
+ sel_start = 0;
+ sel_length = 0;
+ h_offset = 0;
+ pass_char = 0;
+
+ selected_color = Color::Yellow;
+
+ char buf[32];
+ sprintf(buf, "EditBox %d", id);
+ desc = buf;
+}
+
+EditBox::~EditBox()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+EditBox::DrawContent(const Rect& ctrl_rect)
+{
+ int h = rect.h;
+
+ if (line_height < 1)
+ line_height = GetFont()->Height();
+ page_size = h / (line_height + leading);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+EditBox::DrawTabbedText()
+{
+ if (font && text.length()) {
+ int border_size = 4;
+
+ if (style & WIN_RAISED_FRAME && style & WIN_SUNK_FRAME)
+ border_size = 8;
+
+ Rect label_rect = rect;
+
+ label_rect.x = border_size;
+ label_rect.y = border_size;
+ label_rect.w -= border_size * 2;
+ label_rect.h -= border_size * 2;
+
+ if (focus)
+ font->SetCaretIndex(sel_start);
+
+ // if displaying in password mode, create a display string
+ // containing the proper number of password chars:
+ Text s = text;
+ if (pass_char)
+ s = Text(pass_char, text.length());
+
+ // no tabs set:
+ if (tab[0] == 0) {
+ DWORD text_flags = DT_WORDBREAK | text_align;
+
+ if (single_line)
+ text_flags = text_flags | DT_SINGLELINE;
+
+ if (style & WIN_TEXT_SHADOW) {
+ label_rect.x++;
+ label_rect.y++;
+
+ font->SetColor(back_color);
+ DrawText(s.data() + h_offset, 0, label_rect, text_flags);
+
+ label_rect.x--;
+ label_rect.y--;
+ }
+
+ font->SetColor(fore_color);
+ DrawText(s.data() + h_offset, 0, label_rect, text_flags);
+ }
+
+ // use tabs:
+ else {
+ }
+
+ font->SetCaretIndex(-1);
+ }
+ else {
+ caret_x = 2;
+ caret_y = 3;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Color EditBox::GetSelectedColor()
+{
+ return selected_color;
+}
+
+void EditBox::SetSelectedColor(Color c)
+{
+ if (selected_color != c) {
+ selected_color = c;
+ }
+}
+
+Text EditBox::GetSelText()
+{
+ if (sel_start == 0 && sel_length == text.length())
+ return text;
+
+ Text selection;
+ char* buf = new(__FILE__,__LINE__) char[sel_length+1];
+
+ if (buf) {
+ for (int i = 0; i < sel_length; i++)
+ buf[i] = text[(int) (sel_start + i)];
+
+ buf[sel_length] = 0;
+
+ selection = buf;
+ delete [] buf;
+ }
+
+ return selection;
+}
+
+void EditBox::SetSelStart(int n)
+{
+ if (n >= 0 && n <= text.length())
+ sel_start = n;
+}
+
+void EditBox::SetSelLength(int n)
+{
+ if (n <= text.length() - sel_start)
+ sel_length = n;
+}
+
+void EditBox::EnsureCaretVisible()
+{
+ if (!single_line) {
+ h_offset = 0;
+ return;
+ }
+
+ if (sel_start < 0) {
+ sel_start = 0;
+ h_offset = 0;
+ }
+
+ else if (sel_start > h_offset) {
+ int x_caret;
+ bool moved;
+
+ do {
+ x_caret = 0;
+ moved = false;
+
+ if (pass_char) {
+ Text pass = Text(pass_char, sel_start-h_offset);
+ x_caret += font->StringWidth(pass.data(), pass.length());
+ }
+ else {
+ Text sub = text.substring(h_offset, sel_start-h_offset);
+ x_caret += font->StringWidth(sub.data(), sub.length());
+ }
+
+ if (x_caret >= Width()-4) {
+ if (h_offset < text.length()) {
+ h_offset++;
+ moved = true;
+ }
+ }
+ }
+ while (moved);
+ }
+
+ else {
+ h_offset = sel_start;
+ }
+}
+
+bool EditBox::CanScroll(int dir, int nlines)
+{
+ return false;
+}
+
+void EditBox::Scroll(int direction, int nlines)
+{
+ scrolling = SCROLL_NONE;
+}
+
+void EditBox::ScrollTo(int index)
+{
+}
+
+int EditBox::GetPageCount()
+{
+ return 1;
+}
+
+int EditBox::GetPageSize()
+{
+ return page_size;
+}
+
+// +--------------------------------------------------------------------+
+
+void EditBox::SetFocus()
+{
+ ActiveWindow::SetFocus();
+
+ sel_start = text.length();
+ sel_length = 0;
+}
+
+void EditBox::KillFocus()
+{
+ ActiveWindow::KillFocus();
+}
+
+// +--------------------------------------------------------------------+
+
+int EditBox::CaretFromPoint(int x, int y) const
+{
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+int EditBox::OnMouseMove(int x, int y)
+{
+ return ActiveWindow::OnMouseMove(x,y);
+}
+
+// +--------------------------------------------------------------------+
+
+int EditBox::OnLButtonDown(int x, int y)
+{
+ return ActiveWindow::OnLButtonDown(x,y);
+}
+
+// +--------------------------------------------------------------------+
+
+int EditBox::OnLButtonUp(int x, int y)
+{
+ return ActiveWindow::OnLButtonUp(x,y);
+}
+
+// +--------------------------------------------------------------------+
+
+int EditBox::OnClick()
+{
+ int fire_click = !scrolling;
+
+ if (scrolling == SCROLL_THUMB)
+ scrolling = SCROLL_NONE;
+
+ if (fire_click)
+ return ActiveWindow::OnClick();
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void EditBox::Insert(char c)
+{
+ if (single_line && c == '\n')
+ return;
+
+ if (sel_start < text.length()) {
+ if (sel_start == 0) {
+ text = Text(c) + text;
+ sel_start = 1;
+ }
+ else {
+ Text t1 = text.substring(0, sel_start);
+ Text t2 = text.substring(sel_start, text.length()-sel_start);
+ text = t1 + Text(c) + t2;
+ sel_start++;
+ }
+ }
+ else {
+ text += c;
+ sel_start = text.length();
+ }
+
+ EnsureCaretVisible();
+}
+
+void EditBox::Insert(const char* s)
+{
+}
+
+void EditBox::Delete()
+{
+ if (sel_start < text.length()) {
+ if (sel_start == 0) {
+ text = text.substring(1, text.length()-1);
+ }
+ else {
+ Text t1 = text.substring(0, sel_start);
+ Text t2 = text.substring(sel_start+1, text.length()-sel_start-1);
+ text = t1 + t2;
+ }
+ }
+
+ EnsureCaretVisible();
+}
+
+void EditBox::Backspace()
+{
+ if (sel_start > 0) {
+ if (sel_start == text.length()) {
+ text = text.substring(0, sel_start-1);
+ }
+ else {
+ Text t1 = text.substring(0, sel_start-1);
+ Text t2 = text.substring(sel_start, text.length()-sel_start);
+ text = t1 + t2;
+ }
+
+ sel_start--;
+ EnsureCaretVisible();
+ }
+}
+
+int EditBox::OnKeyDown(int vk, int flags)
+{
+ if (vk >= 'A' && vk <= 'Z') {
+ if (flags & 1)
+ Insert((char) vk);
+ else
+ Insert((char) tolower(vk));
+ }
+ else {
+ switch (vk) {
+ case VK_LEFT:
+ if (sel_start > 0) sel_start--;
+ EnsureCaretVisible();
+ break;
+
+ case VK_RIGHT:
+ if (sel_start < text.length()) sel_start++;
+ EnsureCaretVisible();
+ break;
+
+ case VK_HOME:
+ sel_start = 0;
+ EnsureCaretVisible();
+ break;
+
+ case VK_END:
+ sel_start = text.length();
+ EnsureCaretVisible();
+ break;
+
+ case VK_DELETE: Delete(); break;
+ case VK_BACK: Backspace(); break;
+
+ case VK_SPACE: Insert(' '); break;
+ case VK_RETURN: Insert('\n'); break;
+
+ case VK_NUMPAD0: Insert('0'); break;
+ case VK_NUMPAD1: Insert('1'); break;
+ case VK_NUMPAD2: Insert('2'); break;
+ case VK_NUMPAD3: Insert('3'); break;
+ case VK_NUMPAD4: Insert('4'); break;
+ case VK_NUMPAD5: Insert('5'); break;
+ case VK_NUMPAD6: Insert('6'); break;
+ case VK_NUMPAD7: Insert('7'); break;
+ case VK_NUMPAD8: Insert('8'); break;
+ case VK_NUMPAD9: Insert('9'); break;
+ case VK_DECIMAL: Insert('.'); break;
+ case VK_ADD: Insert('+'); break;
+ case VK_SUBTRACT: Insert('-'); break;
+ case VK_MULTIPLY: Insert('*'); break;
+ case VK_DIVIDE: Insert('/'); break;
+
+ case '0': if (flags & 1) Insert(')'); else Insert('0'); break;
+ case '1': if (flags & 1) Insert('!'); else Insert('1'); break;
+ case '2': if (flags & 1) Insert('@'); else Insert('2'); break;
+ case '3': if (flags & 1) Insert('#'); else Insert('3'); break;
+ case '4': if (flags & 1) Insert('$'); else Insert('4'); break;
+ case '5': if (flags & 1) Insert('%'); else Insert('5'); break;
+ case '6': if (flags & 1) Insert('^'); else Insert('6'); break;
+ case '7': if (flags & 1) Insert('&'); else Insert('7'); break;
+ case '8': if (flags & 1) Insert('*'); else Insert('8'); break;
+ case '9': if (flags & 1) Insert('('); else Insert('9'); break;
+ case 186: if (flags & 1) Insert(':'); else Insert(';'); break;
+ case 187: if (flags & 1) Insert('+'); else Insert('='); break;
+ case 188: if (flags & 1) Insert('<'); else Insert(','); break;
+ case 189: if (flags & 1) Insert('_'); else Insert('-'); break;
+ case 190: if (flags & 1) Insert('>'); else Insert('.'); break;
+ case 191: if (flags & 1) Insert('?'); else Insert('/'); break;
+ case 192: if (flags & 1) Insert('~'); else Insert('`'); break;
+ case 219: if (flags & 1) Insert('{'); else Insert('['); break;
+ case 221: if (flags & 1) Insert('}'); else Insert(']'); break;
+ case 220: if (flags & 1) Insert('|'); else Insert('\\'); break;
+ case 222: if (flags & 1) Insert('"'); else Insert('\''); break;
+ }
+ }
+
+ return ActiveWindow::OnKeyDown(vk, flags);
+}
diff --git a/nGenEx/EditBox.h b/nGenEx/EditBox.h
new file mode 100644
index 0000000..fba4fbf
--- /dev/null
+++ b/nGenEx/EditBox.h
@@ -0,0 +1,92 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: EditBox.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ EditBox ActiveWindow class
+*/
+
+#ifndef EditBox_h
+#define EditBox_h
+
+#include "Types.h"
+#include "ScrollWindow.h"
+
+// +--------------------------------------------------------------------+
+
+class EditBox : public ScrollWindow
+{
+public:
+ static const char* TYPENAME() { return "EditBox"; }
+
+ enum ALIGN { EDIT_ALIGN_LEFT = DT_LEFT,
+ EDIT_ALIGN_CENTER = DT_CENTER,
+ EDIT_ALIGN_RIGHT = DT_RIGHT
+ };
+
+ EditBox(ActiveWindow* p, int ax, int ay, int aw, int ah, DWORD aid);
+ EditBox(Screen* s, int ax, int ay, int aw, int ah, DWORD aid);
+ virtual ~EditBox();
+
+ // Operations:
+ virtual void DrawContent(const Rect& ctrl_rect);
+ virtual void DrawTabbedText();
+
+ // Event Target Interface:
+ virtual void SetFocus();
+ virtual void KillFocus();
+
+ virtual int OnMouseMove(int x, int y);
+ virtual int OnLButtonDown(int x, int y);
+ virtual int OnLButtonUp(int x, int y);
+ virtual int OnClick();
+
+ virtual int OnKeyDown(int vk, int flags);
+
+ // ScrollWindow Interface:
+ virtual bool CanScroll(int direction, int nlines=1);
+ virtual void Scroll(int direction, int nlines=1);
+ virtual void ScrollTo(int index);
+ virtual int GetPageCount();
+ virtual int GetPageSize();
+
+ Color GetSelectedColor();
+ void SetSelectedColor(Color c);
+
+ int GetSelStart() { return sel_start; }
+ int GetSelLength() { return sel_length; }
+ Text GetSelText();
+
+ void SetSelStart(int n);
+ void SetSelLength(int n);
+
+ char GetPasswordChar() { return pass_char; }
+ void SetPasswordChar(char c) { pass_char = c; }
+
+protected:
+ void Insert(char c);
+ void Insert(const char* s);
+ void Delete();
+ void Backspace();
+ int CaretFromPoint(int x, int y) const;
+ void EnsureCaretVisible();
+
+ int sel_start;
+ int sel_length;
+ int h_offset;
+ int caret_x;
+ int caret_y;
+
+ char pass_char;
+
+ Color selected_color;
+};
+
+#endif EditBox_h
+
diff --git a/nGenEx/Encrypt.cpp b/nGenEx/Encrypt.cpp
new file mode 100644
index 0000000..6fa79d2
--- /dev/null
+++ b/nGenEx/Encrypt.cpp
@@ -0,0 +1,171 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Encrypt.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Simple Encryption / Decryption class
+*/
+
+
+#include "MemDebug.h"
+#include "Encrypt.h"
+
+// +--------------------------------------------------------------------+
+
+void Print(const char* fmt, ...);
+
+static long k[4] = {
+ 0x3B398E26,
+ 0x40C29501,
+ 0x614D7630,
+ 0x7F59409A
+};
+
+static void encypher(long* v)
+{
+ DWORD y=v[0];
+ DWORD z=v[1];
+ DWORD sum=0;
+ DWORD delta=0x9e3779b9; // a key schedule constant
+ DWORD n=32; // num iterations
+
+ while (n-->0) { // basic cycle start
+ sum += delta;
+ y += (z<<4)+k[0] ^ z+sum ^ (z>>5)+k[1];
+ z += (y<<4)+k[2] ^ y+sum ^ (y>>5)+k[3];
+ }
+
+ v[0]=y;
+ v[1]=z;
+}
+
+static void decypher(long* v)
+{
+ DWORD y=v[0];
+ DWORD z=v[1];
+ DWORD sum=0;
+ DWORD delta=0x9e3779b9; // a key schedule constant
+ DWORD n=32; // num iterations
+
+ sum=delta<<5;
+
+ while (n-->0) {
+ z-= (y<<4)+k[2] ^ y+sum ^ (y>>5)+k[3];
+ y-= (z<<4)+k[0] ^ z+sum ^ (z>>5)+k[1];
+ sum-=delta;
+ }
+
+ v[0]=y;
+ v[1]=z;
+}
+
+// +-------------------------------------------------------------------+
+
+Text
+Encryption::Encrypt(Text block)
+{
+ int len = block.length();
+
+ if (len < 1)
+ return Text();
+
+ // pad to eight byte chunks
+ if (len & 0x7) {
+ len /= 8;
+ len *= 8;
+ len += 8;
+ }
+
+ BYTE* work = new(__FILE__,__LINE__) BYTE[len];
+ ZeroMemory(work, len);
+ CopyMemory(work, block.data(), block.length());
+
+ long* v = (long*) work;
+ for (int i = 0; i < len/8; i++) {
+ encypher(v);
+ v += 2;
+ }
+
+ Text cypher((const char*) work, len);
+ delete [] work;
+ return cypher;
+}
+
+// +-------------------------------------------------------------------+
+
+Text
+Encryption::Decrypt(Text block)
+{
+ int len = block.length();
+
+ if (len & 0x7) {
+ Print("WARNING: attempt to decrypt odd length block (len=%d)\n", len);
+ return Text();
+ }
+
+ BYTE* work = new(__FILE__,__LINE__) BYTE[len];
+ CopyMemory(work, block.data(), len);
+
+ long* v = (long*) work;
+ for (int i = 0; i < len/8; i++) {
+ decypher(v);
+ v += 2;
+ }
+
+ Text clear((const char*) work, len);
+ delete [] work;
+ return clear;
+}
+
+// +-------------------------------------------------------------------+
+
+static const char* codes = "abcdefghijklmnop";
+
+Text
+Encryption::Encode(Text block)
+{
+ int len = block.length() * 2;
+ char* work = new(__FILE__,__LINE__) char[len + 1];
+
+ for (int i = 0; i < block.length(); i++) {
+ BYTE b = (BYTE) (block.data()[i]);
+ work[2*i] = codes[b>>4 & 0xf];
+ work[2*i+1] = codes[b & 0xf];
+ }
+
+ work[len] = 0;
+
+ Text code(work, len);
+ delete [] work;
+ return code;
+}
+
+// +-------------------------------------------------------------------+
+
+Text
+Encryption::Decode(Text block)
+{
+ int len = block.length() / 2;
+ char* work = new(__FILE__,__LINE__) char[len + 1];
+
+ for (int i = 0; i < len; i++) {
+ char u = block[2*i];
+ char l = block[2*i + 1];
+
+ work[i] = (u - codes[0]) << 4 |
+ (l - codes[0]);
+ }
+
+ work[len] = 0;
+
+ Text clear(work, len);
+ delete [] work;
+ return clear;
+}
+
diff --git a/nGenEx/Encrypt.h b/nGenEx/Encrypt.h
new file mode 100644
index 0000000..9024740
--- /dev/null
+++ b/nGenEx/Encrypt.h
@@ -0,0 +1,38 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Encrypt.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Simple Encryption / Decryption class
+*/
+
+
+#ifndef Encrypt_h
+#define Encrypt_h
+
+#include "Types.h"
+#include "Text.h"
+
+// +-------------------------------------------------------------------+
+
+class Encryption
+{
+public:
+ // private key encryption / decryption of
+ // arbitrary blocks of data
+ static Text Encrypt(Text block);
+ static Text Decrypt(Text block);
+
+ // encode / decode binary blocks into
+ // ascii strings for use in text files
+ static Text Encode(Text block);
+ static Text Decode(Text block);
+};
+
+#endif Encrypt_h \ No newline at end of file
diff --git a/nGenEx/EventDispatch.cpp b/nGenEx/EventDispatch.cpp
new file mode 100644
index 0000000..e17713b
--- /dev/null
+++ b/nGenEx/EventDispatch.cpp
@@ -0,0 +1,275 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: EventDispatch.cpp
+ AUTHOR: John DiCamillo
+
+*/
+
+#include "MemDebug.h"
+#include "EventDispatch.h"
+#include "Mouse.h"
+#include "Keyboard.h"
+
+// +--------------------------------------------------------------------+
+
+int GetKeyPlus(int& key, int& shift);
+
+// +--------------------------------------------------------------------+
+
+EventDispatch* EventDispatch::dispatcher = 0;
+
+// +--------------------------------------------------------------------+
+
+EventDispatch::EventDispatch()
+ : capture(0), current(0), focus(0), click_tgt(0)
+ , mouse_x(0), mouse_y(0), mouse_l(0), mouse_r(0)
+{ }
+
+EventDispatch::~EventDispatch()
+{ }
+
+// +--------------------------------------------------------------------+
+
+void
+EventDispatch::Create()
+{
+ dispatcher = new(__FILE__,__LINE__) EventDispatch;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+EventDispatch::Close()
+{
+ delete dispatcher;
+ dispatcher = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+EventDispatch::Dispatch()
+{
+ int ml = Mouse::LButton();
+ int mr = Mouse::RButton();
+ int mx = Mouse::X();
+ int my = Mouse::Y();
+ int mw = Mouse::Wheel();
+
+ EventTarget* mouse_tgt = capture;
+ EventTarget* key_tgt = focus;
+ EventTarget* do_click = 0;
+
+ if (!mouse_tgt) {
+ ListIter<EventTarget> iter = clients;
+ while (++iter) {
+ EventTarget* test = iter.value();
+ if (test->IsFormActive()) {
+ if (test->TargetRect().Contains(mx,my))
+ mouse_tgt = test;
+
+ if (test->HasFocus())
+ key_tgt = test;
+ }
+ }
+ }
+
+ // Mouse Events:
+
+ if (mouse_tgt != current) {
+ if (current && current->IsEnabled() && current->IsVisible())
+ current->OnMouseExit(mx,my);
+
+ current = mouse_tgt;
+
+ if (current && current->IsEnabled() && current->IsVisible())
+ current->OnMouseEnter(mx,my);
+ }
+
+ if (mouse_tgt && mouse_tgt->IsEnabled()) {
+ if (mx != mouse_x || my != mouse_y)
+ mouse_tgt->OnMouseMove(mx,my);
+
+ if (mw != 0)
+ mouse_tgt->OnMouseWheel(mw);
+
+ if (ml != mouse_l) {
+ if (ml) {
+ mouse_tgt->OnLButtonDown(mx,my);
+ click_tgt = mouse_tgt;
+ }
+ else {
+ mouse_tgt->OnLButtonUp(mx,my);
+
+ if (click_tgt == mouse_tgt) {
+ if (click_tgt->TargetRect().Contains(mx,my))
+ do_click = click_tgt;
+ click_tgt = 0;
+ }
+ }
+ }
+
+ if (mr != mouse_r) {
+ if (mr)
+ mouse_tgt->OnRButtonDown(mx,my);
+ else
+ mouse_tgt->OnRButtonUp(mx,my);
+ }
+ }
+
+ mouse_l = ml;
+ mouse_r = mr;
+ mouse_x = mx;
+ mouse_y = my;
+
+ // Keyboard Events:
+
+ if (click_tgt && click_tgt != key_tgt) {
+ if (key_tgt) key_tgt->KillFocus();
+ key_tgt = click_tgt;
+
+ if (key_tgt != focus) {
+ if (focus) focus->KillFocus();
+
+ if (key_tgt && key_tgt->IsEnabled() && key_tgt->IsVisible())
+ focus = key_tgt;
+ else
+ key_tgt = 0;
+
+ if (focus) focus->SetFocus();
+ }
+ }
+
+ if (key_tgt && key_tgt->IsEnabled()) {
+ int key = 0;
+ int shift = 0;
+
+ while (GetKeyPlus(key, shift)) {
+ if (key == VK_ESCAPE) {
+ key_tgt->KillFocus();
+ focus = 0;
+ break;
+ }
+
+ else if (key == VK_TAB && focus) {
+ int key_index = clients.index(focus) + 1;
+
+ if (shift & 1) key_index -= 2;
+
+ if (key_index >= clients.size())
+ key_index = 0;
+ else if (key_index < 0)
+ key_index = clients.size()-1;
+
+ if (focus) focus->KillFocus();
+ focus = clients[key_index];
+ if (focus) focus->SetFocus();
+
+ break;
+ }
+
+ key_tgt->OnKeyDown(key, shift);
+ }
+ }
+
+ if (do_click)
+ do_click->OnClick();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+EventDispatch::MouseEnter(EventTarget* mouse_tgt)
+{
+ if (mouse_tgt != current) {
+ if (current) current->OnMouseExit(0,0);
+ current = mouse_tgt;
+ if (current) current->OnMouseEnter(0,0);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+EventDispatch::Register(EventTarget* tgt)
+{
+ if (!clients.contains(tgt))
+ clients.append(tgt);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+EventDispatch::Unregister(EventTarget* tgt)
+{
+ clients.remove(tgt);
+
+ if (capture == tgt) capture = 0;
+ if (current == tgt) current = 0;
+ if (focus == tgt) focus = 0;
+ if (click_tgt == tgt) click_tgt = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+EventTarget*
+EventDispatch::GetCapture()
+{
+ return capture;
+}
+
+int
+EventDispatch::CaptureMouse(EventTarget* tgt)
+{
+ if (tgt) {
+ capture = tgt;
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+EventDispatch::ReleaseMouse(EventTarget* tgt)
+{
+ if (capture == tgt) {
+ capture = 0;
+ return 1;
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+EventTarget*
+EventDispatch::GetFocus()
+{
+ return focus;
+}
+
+void
+EventDispatch::SetFocus(EventTarget* tgt)
+{
+ if (focus != tgt) {
+ if (focus)
+ focus->KillFocus();
+
+ focus = tgt;
+
+ if (focus && focus->IsEnabled() && focus->IsVisible())
+ focus->SetFocus();
+ }
+}
+
+void
+EventDispatch::KillFocus(EventTarget* tgt)
+{
+ if (focus && focus == tgt) {
+ focus = 0;
+ tgt->KillFocus();
+ }
+}
diff --git a/nGenEx/EventDispatch.h b/nGenEx/EventDispatch.h
new file mode 100644
index 0000000..d2e3150
--- /dev/null
+++ b/nGenEx/EventDispatch.h
@@ -0,0 +1,62 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: EventDispatch.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Event Dispatch class
+*/
+
+#ifndef EventDispatch_h
+#define EventDispatch_h
+
+#include "Types.h"
+#include "EventTarget.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class EventDispatch
+{
+public:
+ static const char* TYPENAME() { return "EventDispatch"; }
+
+ EventDispatch();
+ virtual ~EventDispatch();
+
+ static void Create();
+ static void Close();
+ static EventDispatch* GetInstance() { return dispatcher; }
+
+ virtual void Dispatch();
+ virtual void Register(EventTarget* tgt);
+ virtual void Unregister(EventTarget* tgt);
+
+ virtual EventTarget* GetCapture();
+ virtual int CaptureMouse(EventTarget* tgt);
+ virtual int ReleaseMouse(EventTarget* tgt);
+
+ virtual EventTarget* GetFocus();
+ virtual void SetFocus(EventTarget* tgt);
+ virtual void KillFocus(EventTarget* tgt);
+
+ virtual void MouseEnter(EventTarget* tgt);
+
+protected:
+ int mouse_x, mouse_y, mouse_l, mouse_r;
+ List<EventTarget> clients;
+ EventTarget* capture;
+ EventTarget* current;
+ EventTarget* focus;
+ EventTarget* click_tgt;
+
+ static EventDispatch* dispatcher;
+};
+
+#endif EventDispatch_h
+
diff --git a/nGenEx/EventTarget.h b/nGenEx/EventTarget.h
new file mode 100644
index 0000000..af24a58
--- /dev/null
+++ b/nGenEx/EventTarget.h
@@ -0,0 +1,59 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: EventTarget.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Event Target interface class
+*/
+
+#ifndef EventTarget_h
+#define EventTarget_h
+
+#include "Types.h"
+#include "Geometry.h"
+
+// +--------------------------------------------------------------------+
+
+class EventTarget
+{
+public:
+ static const char* TYPENAME() { return "EventTarget"; }
+
+ virtual ~EventTarget() { }
+
+ int operator == (const EventTarget& t) const { return this == &t; }
+
+ virtual int OnMouseMove(int x, int y) { return 0; }
+ virtual int OnLButtonDown(int x, int y) { return 0; }
+ virtual int OnLButtonUp(int x, int y) { return 0; }
+ virtual int OnClick() { return 0; }
+ virtual int OnSelect() { return 0; }
+ virtual int OnRButtonDown(int x, int y) { return 0; }
+ virtual int OnRButtonUp(int x, int y) { return 0; }
+ virtual int OnMouseEnter(int x, int y) { return 0; }
+ virtual int OnMouseExit(int x, int y) { return 0; }
+ virtual int OnMouseWheel(int wheel) { return 0; }
+
+ virtual int OnKeyDown(int vk, int flags) { return 0; }
+
+ virtual void SetFocus() { }
+ virtual void KillFocus() { }
+ virtual bool HasFocus() const { return false; }
+
+ virtual bool IsEnabled() const { return true; }
+ virtual bool IsVisible() const { return true; }
+ virtual bool IsFormActive() const { return true; }
+
+ virtual Rect TargetRect() const { return Rect(); }
+
+ virtual const char* GetDescription() const { return "EventTarget"; }
+};
+
+#endif EventTarget_h
+
diff --git a/nGenEx/FadeView.cpp b/nGenEx/FadeView.cpp
new file mode 100644
index 0000000..3ff62f7
--- /dev/null
+++ b/nGenEx/FadeView.cpp
@@ -0,0 +1,145 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: FadeView.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Fading Bitmap "billboard" Image View class
+*/
+
+#include "MemDebug.h"
+#include "FadeView.h"
+#include "Color.h"
+#include "Window.h"
+#include "Video.h"
+#include "Screen.h"
+#include "Game.h"
+
+// +--------------------------------------------------------------------+
+
+FadeView::FadeView(Window* c, double in, double out, double hold)
+ : View(c),
+ fade_in(in * 1000),
+ fade_out(out * 1000),
+ hold_time(hold * 1000),
+ step_time(0),
+ fast(1),
+ time(0)
+{
+ state = StateStart;
+}
+
+FadeView::~FadeView()
+{ }
+
+// +--------------------------------------------------------------------+
+
+void FadeView::FadeIn(double in) { fade_in = in * 1000; }
+void FadeView::FadeOut(double out) { fade_out = out * 1000; }
+void FadeView::FastFade(int fade_fast) { fast = fade_fast; }
+void FadeView::StopHold()
+{
+ //Print(" FadeView::StopHold()\n");
+ hold_time = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FadeView::Refresh()
+{
+ double msec = 0;
+
+ if (state == StateStart) {
+ time = Game::RealTime();
+ }
+ else if (state != StateDone) {
+ double new_time = Game::RealTime();
+ msec = new_time - time;
+ time = new_time;
+ }
+
+ switch (state) {
+ case StateStart:
+ if (fade_in) {
+ //Print(" * FadeView: %f, %f, %f\n", fade_in, fade_out, hold_time);
+ Color::SetFade(0);
+ //Print(" 1. FadeView SetFade to 0 (%6.1f)\n", time);
+ }
+
+ step_time = 0;
+ state = State2;
+ break;
+
+ case State2:
+ if (fade_in) {
+ Color::SetFade(0);
+ //Print(" 1. FadeView SetFade to 0 (%6.1f)\n", time);
+ }
+
+ step_time = 0;
+ state = StateIn;
+ break;
+
+ case StateIn:
+ if (step_time < fade_in) {
+ double fade = step_time / fade_in;
+ Color::SetFade(fade);
+ //Print(" 2. FadeView SetFade to %3d (%6.1f) %6.1f\n", (int) (fade * 100), time, step_time);
+ step_time += msec;
+ }
+ else {
+ Color::SetFade(1);
+ //Print(" 2. FadeView SetFade to %3d (%6.1f) %6.1f => HOLDING\n", 100, time, step_time);
+ step_time = 0;
+ state = StateHold;
+ }
+ break;
+
+ case StateHold:
+ if (step_time < hold_time) {
+ step_time += msec;
+ //Print(" 3. FadeView holding at %3d (%6.1f) %6.1f\n", 100, time, step_time);
+ }
+ else {
+ //Print(" 3. FadeView HOLD COMPLETE (%6.1f) %6.1f\n", time, step_time);
+ step_time = 0;
+ state = StateOut;
+ }
+ break;
+
+ case StateOut:
+ if (fade_out > 0) {
+ if (step_time < fade_out) {
+ double fade = 1 - step_time / fade_out;
+ Color::SetFade(fade);
+ //Print(" 4. FadeView SetFade to %3d (%6.1f) %6.1f\n", (int) (fade*100), time, step_time);
+ step_time += msec;
+ }
+ else {
+ Color::SetFade(0);
+ //Print(" 4. FadeView SetFade to %3d (%6.1f) %6.1f\n", 0, time, step_time);
+ step_time = 0;
+ state = StateDone;
+ }
+ }
+ else {
+ Color::SetFade(1);
+ //Print(" 4. FadeView SetFade to %3d (%6.1f) %6.1f\n", 0, time, step_time);
+ step_time = 0;
+ state = StateDone;
+ }
+ break;
+
+ default:
+ case StateDone:
+ //Print(" 5. FadeView done (%6.1f) %6.1f\n", time, step_time);
+ break;
+ }
+}
+
diff --git a/nGenEx/FadeView.h b/nGenEx/FadeView.h
new file mode 100644
index 0000000..ed7f15a
--- /dev/null
+++ b/nGenEx/FadeView.h
@@ -0,0 +1,56 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: FadeView.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Non-rendering view class that controls the fade level (fade-in/fade-out)
+*/
+
+#ifndef FadeView_h
+#define FadeView_h
+
+#include "Types.h"
+#include "View.h"
+
+// +--------------------------------------------------------------------+
+
+class FadeView : public View
+{
+public:
+ static const char* TYPENAME() { return "FadeView"; }
+
+ enum FadeState { StateStart, State2, StateIn, StateHold, StateOut, StateDone };
+
+ FadeView(Window* c, double fade_in=1, double fade_out=1, double hold_time=4);
+ virtual ~FadeView();
+
+ // Operations:
+ virtual void Refresh();
+ virtual bool Done() const { return state == StateDone; }
+ virtual bool Holding() const { return state == StateHold; }
+
+ // Control:
+ virtual void FastFade(int fade_fast);
+ virtual void FadeIn(double fade_in);
+ virtual void FadeOut(double fade_out);
+ virtual void StopHold();
+
+protected:
+ double fade_in;
+ double fade_out;
+ double hold_time;
+ double time;
+ double step_time;
+
+ int fast;
+ FadeState state;
+};
+
+#endif FadeView_h
+
diff --git a/nGenEx/Fix.cpp b/nGenEx/Fix.cpp
new file mode 100644
index 0000000..f6dd213
--- /dev/null
+++ b/nGenEx/Fix.cpp
@@ -0,0 +1,24 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Fix.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Fixed point number class with 16 bits of fractional precision
+*/
+
+#include "MemDebug.h"
+#include "Fix.h"
+
+// +--------------------------------------------------------------------+
+
+const fix fix::one = fix(1);
+const fix fix::two = fix(2);
+const fix fix::three = fix(3);
+const fix fix::five = fix(5);
+const fix fix::ten = fix(10);
diff --git a/nGenEx/Fix.h b/nGenEx/Fix.h
new file mode 100644
index 0000000..b67185e
--- /dev/null
+++ b/nGenEx/Fix.h
@@ -0,0 +1,180 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Fix.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Fixed point number class with 16 bits of fractional precision
+*/
+
+#ifndef Fix_h
+#define Fix_h
+
+// +--------------------------------------------------------------------+
+
+#include "Types.h"
+
+// +--------------------------------------------------------------------+
+
+const double fix_sixty_five=65536.0;
+
+inline int fast_f2i(double d)
+{
+ int i;
+
+ _asm {
+ fld d
+ fistp i
+ }
+
+ return i;
+}
+
+// +--------------------------------------------------------------------+
+
+class fix
+{
+public:
+ static const char* TYPENAME() { return "fix"; }
+
+ enum FixDef { Precision=16, IntMask=0xffff0000, FractMask=0x0000ffff };
+ static const fix one;
+ static const fix two;
+ static const fix three;
+ static const fix five;
+ static const fix ten;
+
+ fix() { }
+ fix(int n) : val(n<<Precision) { }
+ fix(double d)
+ {
+ long ival;
+ _asm {
+ fld fix_sixty_five
+ fmul d
+ fistp ival
+ }
+ val=ival;
+ }
+ fix(const fix& f) : val(f.val) { }
+
+ // conversion operators:
+ operator int () const { return (val>>Precision); }
+ operator float () const { return ((float) val) / ((float) fix_sixty_five); }
+ operator double() const { return ((double) val) / fix_sixty_five; }
+
+ // assignment operators:
+ fix& operator=(const fix& f) { val=f.val; return *this; }
+ fix& operator=(int n) { val=(n<<Precision); return *this; }
+ fix& operator=(double d) { long ival;
+ _asm { fld fix_sixty_five
+ fmul d
+ fistp ival }
+ val = ival;
+ return *this; }
+
+ // comparison operators:
+ int operator==(const fix& f) const { return val==f.val; }
+ int operator!=(const fix& f) const { return val!=f.val; }
+ int operator<=(const fix& f) const { return val<=f.val; }
+ int operator>=(const fix& f) const { return val>=f.val; }
+ int operator< (const fix& f) const { return val< f.val; }
+ int operator> (const fix& f) const { return val> f.val; }
+
+ // arithmetic operators:
+ fix operator+(const fix& f) const { fix r; r.val = val+f.val; return r; }
+ fix operator-(const fix& f) const { fix r; r.val = val-f.val; return r; }
+ fix operator*(const fix& f) const { long a=val; long b=f.val;
+ _asm {
+ mov eax, a
+ mov edx, b
+ imul edx
+ shrd eax, edx, 16
+ mov a, eax
+ }
+ fix r; r.val = a; return r; }
+ fix operator/(const fix& f) const { long a=val; long b=f.val;
+ _asm {
+ mov eax, a
+ mov ebx, b
+ mov edx, eax
+ sar edx, 16
+ shl eax, 16
+ idiv ebx
+ mov a, eax
+ }
+ fix r; r.val = a; return r; }
+ fix& operator+=(const fix& f) { val+=f.val; return *this; }
+ fix& operator-=(const fix& f) { val-=f.val; return *this; }
+ fix& operator*=(const fix& f) { long a=val; long b=f.val;
+ _asm {
+ mov eax, a
+ mov edx, b
+ imul edx
+ shrd eax, edx, 16
+ mov a, eax
+ }
+ val=a; return *this; }
+ fix& operator/=(const fix& f) { long a=val; long b=f.val;
+ _asm {
+ mov eax, a
+ mov ebx, b
+ mov edx, eax
+ sar edx, 16
+ shl eax, 16
+ idiv ebx
+ mov a, eax
+ }
+ val=a; return *this; }
+
+ fix operator+(int n) const { fix r; r.val = val+(n<<Precision); return r; }
+ fix operator-(int n) const { fix r; r.val = val-(n<<Precision); return r; }
+ fix operator*(int n) const { fix r; r.val = val*n; return r; }
+ fix operator/(int n) const { fix r; r.val = val/n; return r; }
+ fix& operator+=(int n) { val+=(n<<Precision); return *this; }
+ fix& operator-=(int n) { val-=(n<<Precision); return *this; }
+ fix& operator*=(int n) { val*=n; return *this; }
+ fix& operator/=(int n) { val/=n; return *this; }
+
+ fix operator+(double d) const { fix f(d); return (*this)+f; }
+ fix operator-(double d) const { fix f(d); return (*this)-f; }
+ fix operator*(double d) const { fix f(d); return (*this)*f; }
+ fix operator/(double d) const { fix f(d); return (*this)/f; }
+ fix& operator+=(double d) { fix f(d); val+=f.val; return *this; }
+ fix& operator-=(double d) { fix f(d); val-=f.val; return *this; }
+ fix& operator*=(double d) { int n; _asm { fld d
+ fistp n } val*=n; return *this; }
+ fix& operator/=(double d) { int n; _asm { fld d
+ fistp n } val/=n; return *this; }
+
+ // misc. functions:
+ fix truncate() const { fix r; r.val = val&IntMask; return r; }
+ fix fraction() const { fix r; r.val = val-truncate().val; return r; }
+ fix floor() const { fix r; r.val = val&IntMask; return r; }
+ fix ceil() const { fix r; r.val = (val+FractMask)&IntMask; return r; }
+ fix adjust_up() const { fix r; r.val = val+FractMask; return r; }
+ fix adjust_down() const { fix r; r.val = val-FractMask; return r; }
+
+ fix muldiv(const fix& num, const fix& den) const
+ { long a=val, b=num.val, c=den.val;
+ _asm {
+ mov eax, a
+ mov edx, b
+ mov ebx, c
+ imul edx
+ idiv ebx
+ mov a, eax
+ }
+ fix r; r.val = a; return r; }
+
+ // data:
+ long val;
+};
+
+#endif Fix_h
+
diff --git a/nGenEx/Font.cpp b/nGenEx/Font.cpp
new file mode 100644
index 0000000..e1c90dc
--- /dev/null
+++ b/nGenEx/Font.cpp
@@ -0,0 +1,1232 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Font.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Font Resource class implementation
+*/
+
+#include "MemDebug.h"
+#include "Font.h"
+#include "Polygon.h"
+#include "Bitmap.h"
+#include "DataLoader.h"
+#include "ParseUtil.h"
+#include "Video.h"
+
+DWORD GetRealTime();
+
+// +--------------------------------------------------------------------+
+
+Font::Font()
+ : flags(0), height(0), baseline(0), interspace(0), spacewidth(0),
+ imagewidth(0), image(0), expansion(0), alpha(1), blend(Video::BLEND_ALPHA),
+ scale(1), material(0), vset(0), polys(0), npolys(0),
+ caret_index(-1), caret_x(0), caret_y(0), tgt_bitmap(0)
+{
+ ZeroMemory(name, sizeof(name));
+ ZeroMemory(glyph, sizeof(glyph));
+ ZeroMemory(kern, sizeof(kern));
+}
+
+Font::Font(const char* n)
+ : flags(0), height(0), baseline(0), interspace(0), spacewidth(4),
+ imagewidth(0), image(0), expansion(0), alpha(1), blend(Video::BLEND_ALPHA),
+ scale(1), material(0), vset(0), polys(0), npolys(0),
+ caret_index(-1), caret_x(0), caret_y(0), tgt_bitmap(0)
+{
+ ZeroMemory(glyph, sizeof(glyph));
+ ZeroMemory(kern, sizeof(kern));
+ CopyMemory(name, n, sizeof(name));
+
+ if (!Load(name)) {
+ flags = 0;
+ height = 0;
+ baseline = 0;
+ interspace = 0;
+ spacewidth = 0;
+ imagewidth = 0;
+ image = 0;
+
+ ZeroMemory(glyph, sizeof(glyph));
+ ZeroMemory(kern, sizeof(kern));
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Font::~Font()
+{
+ if (image) delete [] image;
+ if (vset) delete vset;
+ if (polys) delete [] polys;
+ if (material) delete material;
+}
+
+// +--------------------------------------------------------------------+
+
+static char kern_tweak[256][256];
+
+bool
+Font::Load(const char* name)
+{
+ if (!name || !name[0])
+ return false;
+
+ char imgname[256];
+ char defname[256];
+ wsprintf(defname, "%s.def", name);
+ wsprintf(imgname, "%s.pcx", name);
+
+ DataLoader* loader = DataLoader::GetLoader();
+ if (!loader)
+ return false;
+
+ LoadDef(defname, imgname);
+
+ for (int i = 0; i < 256; i++) {
+ glyph[i].offset = GlyphOffset(i);
+ glyph[i].width = 0;
+ }
+
+ if (loader->LoadBitmap(imgname, bitmap)) {
+ if (!bitmap.Pixels() && !bitmap.HiPixels())
+ return false;
+
+ scale = bitmap.Width() / 256;
+ imagewidth = bitmap.Width();
+ if (height > bitmap.Height())
+ height = bitmap.Height();
+
+ int imgsize = bitmap.Width() * bitmap.Height();
+ image = new(__FILE__,__LINE__) BYTE[imgsize];
+
+ if (image) {
+ if (bitmap.Pixels()) {
+ CopyMemory(image, bitmap.Pixels(), imgsize);
+ }
+
+ else {
+ for (int i = 0; i < imgsize; i++)
+ image[i] = (BYTE) bitmap.HiPixels()[i].Alpha();
+ }
+ }
+
+ material = new(__FILE__,__LINE__) Material;
+ material->tex_diffuse = &bitmap;
+ }
+ else {
+ return false;
+ }
+
+ for (i = 0; i < 256; i++) {
+ glyph[i].width = CalcWidth(i);
+ }
+
+ color = Color::White;
+
+ if (!(flags & (FONT_FIXED_PITCH | FONT_NO_KERN)))
+ AutoKern();
+
+ for (i = 0; i < 256; i++) {
+ for (int j = 0; j < 256; j++) {
+ if (kern_tweak[i][j] < 100) {
+ kern[i][j] = kern_tweak[i][j];
+ }
+ }
+ }
+
+ return true;
+}
+
+void
+Font::LoadDef(char* defname, char* imgname)
+{
+ for (int i = 0; i < 256; i++)
+ for (int j = 0; j < 256; j++)
+ kern_tweak[i][j] = 111;
+
+ DataLoader* loader = DataLoader::GetLoader();
+ if (!loader)
+ return;
+
+ BYTE* block;
+ int blocklen = loader->LoadBuffer(defname, block, true);
+
+ if (!block || blocklen < 4)
+ return;
+
+ Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block, blocklen));
+ Term* term = parser.ParseTerm();
+
+ if (!term) {
+ Print("WARNING: could not parse '%s'\n", defname);
+ return;
+ }
+ else {
+ TermText* file_type = term->isText();
+ if (!file_type || file_type->value() != "FONT") {
+ Print("WARNING: invalid font def file '%s'\n", defname);
+ return;
+ }
+ }
+
+ do {
+ delete term;
+
+ term = parser.ParseTerm();
+
+ if (term) {
+ TermDef* def = term->isDef();
+ if (def) {
+ if (def->name()->value().indexOf("image") == 0) {
+ GetDefText(imgname, def, defname);
+ }
+
+ else if (def->name()->value() == "height") {
+ int h=0;
+ GetDefNumber(h, def, defname);
+
+ if (h >= 0 && h <= 32)
+ height = (BYTE) h;
+ }
+
+ else if (def->name()->value() == "baseline") {
+ int b=0;
+ GetDefNumber(b, def, defname);
+
+ if (b >= 0 && b <= 32)
+ baseline = (BYTE) b;
+ }
+
+ else if (def->name()->value() == "flags") {
+ if (def->term()->isText()) {
+ Text buf;
+ GetDefText(buf, def, defname);
+ buf.setSensitive(false);
+
+ flags = 0;
+
+ if (buf.contains("caps"))
+ flags = flags | FONT_ALL_CAPS;
+
+ if (buf.contains("kern"))
+ flags = flags | FONT_NO_KERN;
+
+ if (buf.contains("fixed"))
+ flags = flags | FONT_FIXED_PITCH;
+ }
+
+ else {
+ int f=0;
+ GetDefNumber(f, def, defname);
+ flags = (WORD) f;
+ }
+ }
+
+ else if (def->name()->value() == "interspace") {
+ int n=0;
+ GetDefNumber(n, def, defname);
+
+ if (n >= 0 && n <= 100)
+ interspace = (BYTE) n;
+ }
+
+ else if (def->name()->value() == "spacewidth") {
+ int n=0;
+ GetDefNumber(n, def, defname);
+
+ if (n >= 0 && n <= 100)
+ spacewidth = (BYTE) n;
+ }
+
+ else if (def->name()->value() == "expansion") {
+ GetDefNumber(expansion, def, defname);
+ }
+
+ else if (def->name()->value() == "kern") {
+ TermStruct* val = def->term()->isStruct();
+
+ char a[8], b[8];
+ int k=111;
+
+ a[0] = 0;
+ b[0] = 0;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ if (pdef->name()->value() == "left" || pdef->name()->value() == "a")
+ GetDefText(a, pdef, defname);
+
+ else if (pdef->name()->value() == "right" || pdef->name()->value() == "b")
+ GetDefText(b, pdef, defname);
+
+ else if (pdef->name()->value() == "kern" || pdef->name()->value() == "k")
+ GetDefNumber(k, pdef, defname);
+ }
+ }
+
+ if (k < 100)
+ kern_tweak[a[0]][b[0]] = k;
+ }
+
+ else {
+ Print("WARNING: unknown object '%s' in '%s'\n",
+ def->name()->value().data(), defname);
+ }
+ }
+ else {
+ Print("WARNING: term ignored in '%s'\n", defname);
+ term->print();
+ }
+ }
+ }
+ while (term);
+
+ loader->ReleaseBuffer(block);
+
+}
+
+// +--------------------------------------------------------------------+
+
+static const int pipe_width = 16;
+static const int char_width = 16;
+static const int char_height = 16;
+static const int row_items = 16;
+static const int row_width = row_items * char_width;
+static const int row_size = char_height * row_width;
+
+int
+Font::GlyphOffset(BYTE c) const
+{
+ if (flags & FONT_ALL_CAPS)
+ if (islower(c))
+ c = toupper(c);
+
+ return (c/row_items * row_size * scale * scale +
+ c%row_items * char_width * scale);
+}
+
+int
+Font::GlyphLocationX(BYTE c) const
+{
+ if (flags & FONT_ALL_CAPS)
+ if (islower(c))
+ c = toupper(c);
+
+ return c%row_items * char_width;
+}
+
+int
+Font::GlyphLocationY(BYTE c) const
+{
+ if (flags & FONT_ALL_CAPS)
+ if (islower(c))
+ c = toupper(c);
+
+ return c/row_items * char_height;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Font::CalcWidth(BYTE c) const
+{
+ if (c >= PIPE_NBSP && c <= ARROW_RIGHT)
+ return pipe_width;
+
+ if (c >= 128 || !image)
+ return 0;
+
+ // all digits should be same size:
+ if (isdigit(c))
+ c = '0';
+
+ int result = 0;
+ int w = 16 * scale;
+ int h = 16 * scale;
+
+ BYTE* src = image + GlyphOffset(c);
+
+ for (int y = 0; y < h; y++) {
+ BYTE* pleft = src;
+
+ for (int x = 0; x < w; x++) {
+ if (*pleft++ > 0 && x > result)
+ result = x;
+ }
+
+ src += imagewidth;
+ }
+
+ return result + 2;
+}
+
+// +--------------------------------------------------------------------+
+
+struct FontKernData
+{
+ double l[32];
+ double r[32];
+};
+
+void
+Font::FindEdges(BYTE c, double* l, double* r)
+{
+ if (!image)
+ return;
+
+ int w = glyph[c].width;
+ int h = height;
+
+ if (h > 32)
+ h = 32;
+
+ BYTE* src = image + GlyphOffset(c);
+
+ for (int y = 0; y < h; y++) {
+ BYTE* pleft = src;
+ BYTE* pright = src+w-1;
+
+ *l = -1;
+ *r = -1;
+
+ for (int x = 0; x < w; x++) {
+ if (*l == -1 && *pleft != 0)
+ *l = x + 1 - (double) *pleft/255.0;
+ if (*r == -1 && *pright != 0)
+ *r = x + 1 - (double) *pright/255.0;
+
+ pleft++;
+ pright--;
+ }
+
+ src += imagewidth;
+ l++;
+ r++;
+ }
+}
+
+static bool nokern(char c)
+{
+ if (c <= Font::ARROW_RIGHT || c >= 128)
+ return true;
+
+ const char* nokernchars = "0123456789+=<>-.,:;?'\"";
+
+ if (strchr(nokernchars, c))
+ return true;
+
+ return false;
+}
+
+void
+Font::AutoKern()
+{
+ FontKernData* data = new(__FILE__,__LINE__) FontKernData[256];
+
+ if (!data)
+ return;
+
+ int h = height;
+ if (h > 32) h = 32;
+
+ int i, j;
+
+ // first, compute row edges for each glyph:
+
+ for (i = 0; i < 256; i++) {
+ ZeroMemory(&data[i], sizeof(FontKernData));
+
+ char c = i;
+
+ if ((flags & FONT_ALL_CAPS) && islower(c))
+ c = toupper(c);
+
+ if (glyph[(BYTE) c].width > 0) {
+ FindEdges((BYTE) c, data[i].l, data[i].r);
+ }
+ }
+
+ // then, compute the appropriate kern for each pair.
+ // use a desired average distance of one pixel,
+ // with a desired minimum distance of more than half a pixel:
+
+ double desired_avg = 2.5 + expansion;
+ double desired_min = 1;
+
+ for (i = 0; i < 256; i++) {
+ for (j = 0; j < 256; j++) {
+ // no kerning between digits or dashes:
+ if (nokern(i) || nokern(j)) {
+ kern[i][j] = (char) 0;
+ }
+
+ else {
+ double delta = 0;
+ double avg = 0;
+ double min = 2500;
+ int n = 0;
+
+ for (int y = 0; y < h; y++) {
+ if (data[i].r[y] >= 0 && data[j].l[y] >= 0) {
+ delta = data[i].r[y] + data[j].l[y];
+ avg += delta;
+ if (delta < min)
+ min = delta;
+
+ n++;
+ }
+ }
+
+ if (n > 0) {
+ avg /= n;
+
+ delta = desired_avg - avg;
+
+ if (delta < desired_min - min) {
+ delta = ceil(desired_min - min);
+
+ if (i == 'T' && islower(j) && !(flags & FONT_ALL_CAPS))
+ delta += 1;
+ }
+ }
+ else {
+ delta = 0;
+ }
+
+ kern[i][j] = (char) delta;
+ }
+ }
+ }
+
+ delete [] data;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Font::CharWidth(char c) const
+{
+ if (flags & FONT_ALL_CAPS)
+ if (islower(c))
+ c = toupper(c);
+
+ int result = 0;
+
+ if (c >= PIPE_NBSP && c <= ARROW_RIGHT)
+ result = pipe_width;
+
+ else if (c < 0 || isspace(c))
+ result = spacewidth;
+
+ else
+ result = glyph[c].width + interspace;
+
+ return result;
+}
+
+int
+Font::SpaceWidth() const
+{
+ return spacewidth;
+}
+
+int
+Font::KernWidth(char a, char b) const
+{
+ if (flags & FONT_ALL_CAPS) {
+ if (islower(a)) a = toupper(a);
+ if (islower(b)) b = toupper(b);
+ }
+
+ return kern[a][b];
+}
+
+void
+Font::SetKern(char a, char b, int k)
+{
+ if (k < -100 || k > 100)
+ return;
+
+ if (flags & FONT_ALL_CAPS) {
+ if (islower(a)) a = toupper(a);
+ if (islower(b)) b = toupper(b);
+ }
+
+ kern[a][b] = (char) k;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Font::StringWidth(const char* str, int len) const
+{
+ int result = 0;
+
+ if (!str)
+ return result;
+
+ if (!len)
+ len = strlen(str);
+
+ const char* c = str;
+ for (int i = 0; i < len; i++) {
+ if (isspace(*c) && (*c < PIPE_NBSP || *c > ARROW_RIGHT))
+ result += spacewidth;
+ else {
+ int cc = *c;
+ if (flags & FONT_ALL_CAPS)
+ if (islower(cc))
+ cc = toupper(cc);
+
+ int k = 0;
+ if (i < len-1)
+ k = kern[cc][str[i+1]];
+
+ result += glyph[cc].width + interspace + k;
+ }
+ c++;
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Font::DrawText(const char* text, int count, Rect& text_rect, DWORD flags, Bitmap* tgt)
+{
+ Rect clip_rect = text_rect;
+
+ if (clip_rect.w < 1 || clip_rect.h < 1)
+ return;
+
+ tgt_bitmap = tgt;
+
+ if (text && text[0]) {
+ if (count < 1)
+ count = strlen(text);
+
+ // single line:
+ if (flags & DT_SINGLELINE) {
+ DrawTextSingle(text, count, text_rect, clip_rect, flags);
+ }
+
+ // multi-line with word wrap:
+ else if (flags & DT_WORDBREAK) {
+ DrawTextWrap(text, count, text_rect, clip_rect, flags);
+ }
+
+ // multi-line with clip:
+ else {
+ DrawTextMulti(text, count, text_rect, clip_rect, flags);
+ }
+ }
+ else {
+ caret_x = text_rect.x + 2;
+ caret_y = text_rect.y + 2;
+ }
+
+ // if calc only, update the rectangle:
+ if (flags & DT_CALCRECT) {
+ text_rect.h = clip_rect.h;
+ text_rect.w = clip_rect.w;
+ }
+
+ // otherwise, draw caret if requested:
+ else if (caret_index >= 0 && caret_y >= text_rect.y && caret_y <= text_rect.y + text_rect.h) {//caret_y + height < text_rect.y + text_rect.h) {
+ Video* video = Video::GetInstance();
+
+ if (video && (GetRealTime() / 500) & 1) {
+ float v[4];
+ v[0] = (float) (caret_x + 1);
+ v[1] = (float) (caret_y);
+ v[2] = (float) (caret_x + 1);
+ v[3] = (float) (caret_y + height);
+
+ video->DrawScreenLines(1, v, color, blend);
+ }
+
+ caret_index = -1;
+ }
+
+ tgt_bitmap = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+static int find_next_word_start(const char* text, int index)
+{
+ // step through intra-word space:
+ while (text[index] && isspace(text[index]) && text[index] != '\n')
+ index++;
+
+ return index;
+}
+
+static int find_next_word_end(const char* text, int index)
+{
+ if (index < 0)
+ return index;
+
+ // check for leading newline:
+ if (text[index] == '\n')
+ return index;
+
+ // step through intra-word space:
+ while (text[index] && isspace(text[index]))
+ index++;
+
+ // step through word:
+ while (text[index] && !isspace(text[index]))
+ index++;
+
+ return index-1;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Font::DrawTextSingle(const char* text, int count, const Rect& text_rect, Rect& clip_rect, DWORD flags)
+{
+ // parse the format flags:
+ bool nodraw = (flags & DT_CALCRECT) ?true:false;
+
+ int align = DT_LEFT;
+ if (flags & DT_RIGHT)
+ align = DT_RIGHT;
+ else if (flags & DT_CENTER)
+ align = DT_CENTER;
+
+ int max_width = 0;
+
+ int valign = DT_TOP;
+ if (flags & DT_BOTTOM) valign = DT_BOTTOM;
+ else if (flags & DT_VCENTER) valign = DT_VCENTER;
+
+ int xoffset = 0;
+ int yoffset = 0;
+
+ int length = StringWidth(text, count);
+ if (length < text_rect.w) {
+ switch (align) {
+ default:
+ case DT_LEFT: break;
+ case DT_RIGHT: xoffset = text_rect.w - length; break;
+ case DT_CENTER: xoffset = (text_rect.w - length)/2; break;
+ }
+ }
+
+ if (Height() < text_rect.h) {
+ switch (valign) {
+ default:
+ case DT_TOP: break;
+ case DT_BOTTOM: yoffset = text_rect.h - Height(); break;
+ case DT_VCENTER: yoffset = (text_rect.h - Height())/2; break;
+ }
+ }
+
+ max_width = length;
+
+ // if calc only, update the rectangle:
+ if (nodraw) {
+ clip_rect.h = Height();
+ clip_rect.w = max_width;
+ }
+
+ // otherwise, draw the string now:
+ else {
+ int x1 = text_rect.x + xoffset;
+ int y1 = text_rect.y + yoffset;
+
+ DrawString(text, count, x1, y1, text_rect);
+ }
+
+ if (caret_index >= 0 && caret_index <= count) {
+ caret_x = text_rect.x + xoffset;
+ caret_y = text_rect.y + yoffset;
+
+ if (caret_index > 0)
+ caret_x += StringWidth(text, caret_index);
+ }
+
+ else {
+ caret_x = text_rect.x + 0;
+ caret_y = text_rect.y + 0;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Font::DrawTextWrap(const char* text, int count, const Rect& text_rect, Rect& clip_rect, DWORD flags)
+{
+ // parse the format flags:
+ bool nodraw = (flags & DT_CALCRECT) ?true:false;
+
+ int align = DT_LEFT;
+ if (flags & DT_RIGHT)
+ align = DT_RIGHT;
+ else if (flags & DT_CENTER)
+ align = DT_CENTER;
+
+ int nlines = 0;
+ int max_width = 0;
+
+ int line_start = 0;
+ int line_count = 0;
+ int count_remaining = count;
+ int curr_word_end = -1;
+ int next_word_end = 0;
+ int eol_index = 0;
+
+ int xoffset = 0;
+ int yoffset = 0;
+
+ caret_x = -1;
+ caret_y = -1;
+
+ // repeat for each line of text:
+ while (count_remaining > 0) {
+ int length = 0;
+
+ // find the end of the last whole word that fits on the line:
+ for (;;) {
+ next_word_end = find_next_word_end(text, curr_word_end+1);
+
+ if (next_word_end < 0 || next_word_end == curr_word_end)
+ break;
+
+ if (text[next_word_end] == '\n') {
+ eol_index = curr_word_end = next_word_end;
+ break;
+ }
+
+ int word_len = next_word_end - line_start + 1;
+
+ length = StringWidth(text+line_start, word_len);
+
+ if (length < text_rect.w) {
+ curr_word_end = next_word_end;
+
+ // check for a newline in the next block of white space:
+ eol_index = 0;
+ const char* eol = &text[curr_word_end+1];
+ while (*eol && isspace(*eol) && *eol != '\n')
+ eol++;
+
+ if (*eol == '\n') {
+ eol_index = eol - text;
+ break;
+ }
+ }
+ else
+ break;
+ }
+
+ line_count = curr_word_end - line_start + 1;
+
+ if (line_count > 0) {
+ length = StringWidth(text+line_start, line_count);
+ }
+
+ // there was a single word longer than the entire line:
+ else {
+ line_count = next_word_end - line_start + 1;
+ length = StringWidth(text+line_start, line_count);
+ curr_word_end = next_word_end;
+ }
+
+ xoffset = 0;
+ if (length < text_rect.w) {
+ switch (align) {
+ default:
+ case DT_LEFT: break;
+ case DT_RIGHT: xoffset = text_rect.w - length; break;
+ case DT_CENTER: xoffset = (text_rect.w - length)/2; break;
+ }
+ }
+
+ if (length > max_width) max_width = length;
+
+ if (eol_index > 0)
+ curr_word_end = eol_index;
+
+ int next_line_start = find_next_word_start(text, curr_word_end+1);
+
+ if (length > 0 && !nodraw) {
+ int x1 = text_rect.x + xoffset;
+ int y1 = text_rect.y + yoffset;
+
+ DrawString(text+line_start, line_count, x1, y1, text_rect);
+
+ if (caret_index == line_start) {
+ caret_x = x1 - 2;
+ caret_y = y1;
+ }
+ else if (caret_index > line_start && caret_index < next_line_start) {
+ caret_x = text_rect.x + xoffset + StringWidth(text+line_start, caret_index-line_start) - 2;
+ caret_y = text_rect.y + yoffset;
+ }
+ else if (caret_index == count) {
+ if (text[count-1] == '\n') {
+ caret_x = x1 - 2;
+ caret_y = y1 + height;
+ }
+ else {
+ caret_x = text_rect.x + xoffset + StringWidth(text+line_start, caret_index-line_start) - 2;
+ caret_y = text_rect.y + yoffset;
+ }
+ }
+ }
+
+ nlines++;
+ yoffset += Height();
+ if (eol_index > 0)
+ curr_word_end = eol_index;
+ line_start = find_next_word_start(text, curr_word_end+1);
+ count_remaining = count - line_start;
+ }
+
+ // if calc only, update the rectangle:
+ if (nodraw) {
+ clip_rect.h = nlines * Height();
+ clip_rect.w = max_width;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Font::DrawTextMulti(const char* text, int count, const Rect& text_rect, Rect& clip_rect, DWORD flags)
+{
+ // parse the format flags:
+ bool nodraw = (flags & DT_CALCRECT) ?true:false;
+
+ int align = DT_LEFT;
+ if (flags & DT_RIGHT)
+ align = DT_RIGHT;
+ else if (flags & DT_CENTER)
+ align = DT_CENTER;
+
+ int nlines = 1;
+ int max_width = 0;
+ int line_start = 0;
+ int count_remaining = count;
+
+ int xoffset = 0;
+ int yoffset = 0;
+ nlines = 0;
+
+ // repeat for each line of text:
+ while (count_remaining > 0) {
+ int length = 0;
+ int line_count = 0;
+
+ // find the end of line:
+ while (line_count < count_remaining) {
+ char c = text[line_start+line_count];
+ if (!c || c == '\n')
+ break;
+
+ line_count++;
+ }
+
+ if (line_count > 0) {
+ length = StringWidth(text+line_start, line_count);
+ }
+
+ xoffset = 0;
+ if (length < text_rect.w) {
+ switch (align) {
+ default:
+ case DT_LEFT: break;
+ case DT_RIGHT: xoffset = text_rect.w - length; break;
+ case DT_CENTER: xoffset = (text_rect.w - length)/2; break;
+ }
+ }
+
+ if (length > max_width) max_width = length;
+
+ if (length && !nodraw) {
+ int x1 = text_rect.x + xoffset;
+ int y1 = text_rect.y + yoffset;
+
+ DrawString(text+line_start, line_count, x1, y1, text_rect);
+ }
+
+ nlines++;
+ yoffset += Height();
+
+ if (line_start+line_count+1 < count) {
+ line_start = find_next_word_start(text, line_start+line_count+1);
+ count_remaining = count - line_start;
+ }
+ else {
+ count_remaining = 0;
+ }
+ }
+
+ // if calc only, update the rectangle:
+ if (nodraw) {
+ clip_rect.h = nlines * Height();
+ clip_rect.w = max_width;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Font::DrawString(const char* str, int len, int x1, int y1, const Rect& clip, Bitmap* tgt)
+{
+ Video* video = Video::GetInstance();
+ int count = 0;
+ int maxw = clip.w;
+ int maxh = clip.h;
+
+ if (len < 1 || !video)
+ return count;
+
+ // vertical clip
+ if ((y1 < clip.y) || (y1 > clip.y + clip.h))
+ return count;
+
+ // RENDER TO BITMAP
+
+ if (!tgt)
+ tgt = tgt_bitmap;
+
+ if (tgt) {
+ for (int i = 0; i < len; i++) {
+ char c = str[i];
+
+ if ((flags & FONT_ALL_CAPS) && islower(c))
+ c = toupper(c);
+
+ int cw = glyph[c].width + interspace;
+ int ch = height;
+ int k = 0;
+
+ if (i < len-1)
+ k = kern[c][str[i+1]];
+
+ // horizontal clip:
+ if (x1 < clip.x) {
+ if (isspace(c) && (c < PIPE_NBSP || c > ARROW_RIGHT)) {
+ x1 += spacewidth;
+ maxw -= spacewidth;
+ }
+ else {
+ x1 += cw+k;
+ maxw -= cw+k;
+ }
+ }
+ else if (x1+cw > clip.x+clip.w) {
+ return count;
+ }
+ else {
+ if (isspace(c) && (c < PIPE_NBSP || c > ARROW_RIGHT)) {
+ x1 += spacewidth;
+ maxw -= spacewidth;
+ }
+ else {
+ int sx = GlyphLocationX(c);
+ int sy = GlyphLocationY(c);
+
+ Color* srcpix = bitmap.HiPixels();
+ Color* dstpix = tgt->HiPixels();
+ if (srcpix && dstpix) {
+ int spitch = bitmap.Width();
+ int dpitch = tgt->Width();
+
+ Color* dst = dstpix + (y1*dpitch) + x1;
+ Color* src = srcpix + (sy*spitch) + sx;
+
+ for (int i = 0; i < ch; i++) {
+ Color* ps = src;
+ Color* pd = dst;
+
+ for (int n = 0; n < cw; n++) {
+ DWORD alpha = ps->Alpha();
+ if (alpha) {
+ *pd = color.dim(alpha / 240.0);
+ }
+ ps++;
+ pd++;
+ }
+
+ dst += dpitch;
+ src += spitch;
+ }
+ }
+ else {
+ // this probably won't work...
+ tgt->BitBlt(x1, y1, bitmap, sx, sy, cw, ch, true);
+ }
+
+ x1 += cw + k;
+ maxw -= cw + k;
+ }
+
+ count++;
+ }
+ }
+ return count;
+ }
+
+ // RENDER TO VIDEO
+
+ // allocate verts, if necessary
+ int nverts = 4*len;
+ if (!vset) {
+ vset = new(__FILE__,__LINE__) VertexSet(nverts);
+
+ if (!vset)
+ return false;
+
+ vset->space = VertexSet::SCREEN_SPACE;
+
+ for (int v = 0; v < vset->nverts; v++) {
+ vset->s_loc[v].z = 0.0f;
+ vset->rw[v] = 1.0f;
+ }
+ }
+ else if (vset->nverts < nverts) {
+ vset->Resize(nverts);
+
+ for (int v = 0; v < vset->nverts; v++) {
+ vset->s_loc[v].z = 0.0f;
+ vset->rw[v] = 1.0f;
+ }
+ }
+
+ if (vset->nverts < nverts)
+ return count;
+
+ if (alpha < 1)
+ color.SetAlpha((BYTE) (alpha * 255.0f));
+ else
+ color.SetAlpha(255);
+
+ for (int i = 0; i < len; i++) {
+ char c = str[i];
+
+ if ((flags & FONT_ALL_CAPS) && islower(c))
+ c = toupper(c);
+
+ int cw = glyph[c].width + interspace;
+ int k = 0;
+
+ if (i < len-1)
+ k = kern[c][str[i+1]];
+
+ // horizontal clip:
+ if (x1 < clip.x) {
+ if (isspace(c) && (c < PIPE_NBSP || c > ARROW_RIGHT)) {
+ x1 += spacewidth;
+ maxw -= spacewidth;
+ }
+ else {
+ x1 += cw+k;
+ maxw -= cw+k;
+ }
+ }
+ else if (x1+cw > clip.x+clip.w) {
+ break;
+ }
+ else {
+ if (isspace(c) && (c < PIPE_NBSP || c > ARROW_RIGHT)) {
+ x1 += spacewidth;
+ maxw -= spacewidth;
+ }
+ else {
+ // create four verts for this character:
+ int v = count*4;
+ double char_x = GlyphLocationX(c);
+ double char_y = GlyphLocationY(c);
+ double char_w = glyph[c].width;
+ double char_h = height;
+
+ if (y1 + char_h > clip.y + clip.h) {
+ char_h = clip.y + clip.h - y1;
+ }
+
+ vset->s_loc[v+0].x = (float) (x1 - 0.5);
+ vset->s_loc[v+0].y = (float) (y1 - 0.5);
+ vset->tu[v+0] = (float) (char_x / 256);
+ vset->tv[v+0] = (float) (char_y / 256);
+ vset->diffuse[v+0] = color.Value();
+
+ vset->s_loc[v+1].x = (float) (x1 + char_w - 0.5);
+ vset->s_loc[v+1].y = (float) (y1 - 0.5);
+ vset->tu[v+1] = (float) (char_x / 256 + char_w / 256);
+ vset->tv[v+1] = (float) (char_y / 256);
+ vset->diffuse[v+1] = color.Value();
+
+ vset->s_loc[v+2].x = (float) (x1 + char_w - 0.5);
+ vset->s_loc[v+2].y = (float) (y1 + char_h - 0.5);
+ vset->tu[v+2] = (float) (char_x / 256 + char_w / 256);
+ vset->tv[v+2] = (float) (char_y / 256 + char_h / 256);
+ vset->diffuse[v+2] = color.Value();
+
+ vset->s_loc[v+3].x = (float) (x1 - 0.5);
+ vset->s_loc[v+3].y = (float) (y1 + char_h - 0.5);
+ vset->tu[v+3] = (float) (char_x / 256);
+ vset->tv[v+3] = (float) (char_y / 256 + char_h / 256);
+ vset->diffuse[v+3] = color.Value();
+
+ x1 += cw + k;
+ maxw -= cw + k;
+
+ count++;
+ }
+ }
+ }
+
+ if (count) {
+ // this small hack is an optimization to reduce the
+ // size of vertex buffer needed for font rendering:
+
+ int old_nverts = vset->nverts;
+ vset->nverts = 4 * count;
+
+ // create a larger poly array, if necessary:
+ if (count > npolys) {
+ if (polys)
+ delete [] polys;
+
+ npolys = count;
+ polys = new(__FILE__,__LINE__) Poly[npolys];
+ Poly* p = polys;
+ int index = 0;
+
+ for (int i = 0; i < npolys; i++) {
+ p->nverts = 4;
+ p->vertex_set = vset;
+ p->material = material;
+ p->verts[0] = index++;
+ p->verts[1] = index++;
+ p->verts[2] = index++;
+ p->verts[3] = index++;
+
+ p++;
+ }
+ }
+
+ video->DrawScreenPolys(count, polys, blend);
+
+ // remember to restore the proper size of the vertex set:
+ vset->nverts = old_nverts;
+ }
+
+ return count;
+}
+
diff --git a/nGenEx/Font.h b/nGenEx/Font.h
new file mode 100644
index 0000000..bea3c33
--- /dev/null
+++ b/nGenEx/Font.h
@@ -0,0 +1,143 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Font.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Font Resource class
+*/
+
+#ifndef Font_h
+#define Font_h
+
+#include "Types.h"
+#include "Bitmap.h"
+#include "Color.h"
+#include "Geometry.h"
+
+// +--------------------------------------------------------------------+
+
+struct Poly;
+struct Material;
+struct VertexSet;
+class Video;
+
+// +--------------------------------------------------------------------+
+
+struct FontChar
+{
+ short offset;
+ short width;
+};
+
+// +--------------------------------------------------------------------+
+
+class Font
+{
+public:
+ static const char* TYPENAME() { return "Font"; }
+
+ enum FLAGS { FONT_FIXED_PITCH = 1,
+ FONT_ALL_CAPS = 2,
+ FONT_NO_KERN = 4
+ };
+
+ enum CHARS { PIPE_NBSP = 16,
+ PIPE_VERT = 17,
+ PIPE_LT = 18,
+ PIPE_TEE = 19,
+ PIPE_UL = 20,
+ PIPE_LL = 21,
+ PIPE_HORZ = 22,
+ PIPE_PLUS = 23,
+ PIPE_MINUS = 24,
+ ARROW_UP = 25,
+ ARROW_DOWN = 26,
+ ARROW_LEFT = 27,
+ ARROW_RIGHT = 28
+ };
+
+ // default constructor:
+ Font();
+ Font(const char* name);
+ ~Font();
+
+ bool Load(const char* name);
+
+ int CharWidth(char c) const;
+ int SpaceWidth() const;
+ int KernWidth(char left, char right) const;
+ int StringWidth(const char* str, int len=0) const;
+
+ void DrawText(const char* txt, int count, Rect& txt_rect, DWORD flags, Bitmap* tgt_bitmap=0);
+ int DrawString( const char* txt, int len, int x1, int y1, const Rect& clip, Bitmap* tgt_bitmap=0);
+
+ int Height() const { return height; }
+ int Baseline() const { return baseline; }
+ WORD GetFlags() const { return flags; }
+ void SetFlags(WORD s) { flags = s; }
+ Color GetColor() const { return color; }
+ void SetColor(const Color& c) { color = c; }
+ double GetExpansion() const { return expansion; }
+ void SetExpansion(double e) { expansion = (float) e; }
+ double GetAlpha() const { return alpha; }
+ void SetAlpha(double a) { alpha = (float) a; }
+ int GetBlend() const { return blend; }
+ void SetBlend(int b) { blend = b; }
+
+ void SetKern(char left, char right, int k=0);
+
+ int GetCaretIndex() const { return caret_index; }
+ void SetCaretIndex(int n) { caret_index = n; }
+
+private:
+ void AutoKern();
+ void FindEdges(BYTE c, double* l, double* r);
+ int CalcWidth(BYTE c) const;
+ int GlyphOffset(BYTE c) const;
+ int GlyphLocationX(BYTE c) const;
+ int GlyphLocationY(BYTE c) const;
+
+ void DrawTextSingle(const char* txt, int count, const Rect& txt_rect, Rect& clip_rect, DWORD flags);
+ void DrawTextWrap(const char* txt, int count, const Rect& txt_rect, Rect& clip_rect, DWORD flags);
+ void DrawTextMulti(const char* txt, int count, const Rect& txt_rect, Rect& clip_rect, DWORD flags);
+
+ void LoadDef(char* defname, char* imgname);
+
+ char name[64];
+ WORD flags;
+ BYTE height;
+ BYTE baseline;
+ BYTE interspace;
+ BYTE spacewidth;
+ float expansion;
+ float alpha;
+ int blend;
+ int scale;
+
+ int caret_index;
+ int caret_x;
+ int caret_y;
+
+ int imagewidth;
+ BYTE* image;
+ Bitmap bitmap;
+ Bitmap* tgt_bitmap;
+ Material* material;
+ VertexSet* vset;
+ Poly* polys;
+ int npolys;
+
+ FontChar glyph[256];
+ Color color;
+
+ char kern[256][256];
+};
+
+#endif Font_h
+
diff --git a/nGenEx/FontMgr.cpp b/nGenEx/FontMgr.cpp
new file mode 100644
index 0000000..e37b2b9
--- /dev/null
+++ b/nGenEx/FontMgr.cpp
@@ -0,0 +1,58 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: FontMgr.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Font Resource Manager class implementation
+*/
+
+#include "MemDebug.h"
+#include "FontMgr.h"
+
+// +--------------------------------------------------------------------+
+
+List<FontItem> FontMgr::fonts;
+
+// +--------------------------------------------------------------------+
+
+void
+FontMgr::Close()
+{
+ fonts.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FontMgr::Register(const char* name, Font* font)
+{
+ FontItem* item = new(__FILE__,__LINE__) FontItem;
+
+ if (item) {
+ item->name = name;
+ item->size = 0;
+ item->font = font;
+
+ fonts.append(item);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Font*
+FontMgr::Find(const char* name)
+{
+ ListIter<FontItem> item = fonts;
+ while (++item) {
+ if (item->name == name)
+ return item->font;
+ }
+
+ return 0;
+}
diff --git a/nGenEx/FontMgr.h b/nGenEx/FontMgr.h
new file mode 100644
index 0000000..5c99118
--- /dev/null
+++ b/nGenEx/FontMgr.h
@@ -0,0 +1,50 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: FontMgr.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Font Resource Manager class
+*/
+
+#ifndef FontMgr_h
+#define FontMgr_h
+
+#include "Types.h"
+#include "Color.h"
+#include "List.h"
+#include "Text.h"
+
+class Font;
+
+// +--------------------------------------------------------------------+
+
+struct FontItem
+{
+ static const char* TYPENAME() { return "FontItem"; }
+
+ Text name;
+ int size;
+ Font* font;
+};
+
+class FontMgr
+{
+public:
+ static const char* TYPENAME() { return "FontMgr"; }
+
+ static void Close();
+ static void Register(const char* name, Font* font);
+ static Font* Find(const char* name);
+
+private:
+ static List<FontItem> fonts;
+};
+
+#endif FontMgr_h
+
diff --git a/nGenEx/FormDef.cpp b/nGenEx/FormDef.cpp
new file mode 100644
index 0000000..9cef31f
--- /dev/null
+++ b/nGenEx/FormDef.cpp
@@ -0,0 +1,1269 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: FormDef.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Form and Control Definition Resources
+*/
+
+#include "MemDebug.h"
+#include "FormDef.h"
+#include "ParseUtil.h"
+#include "DataLoader.h"
+#include "Bitmap.h"
+#include "Game.h"
+
+// +----------------------------------------------------------------------+
+
+ColumnDef::ColumnDef()
+ : width(10), align(0), sort(0), color(Color::White), use_color(false)
+{ }
+
+ColumnDef::ColumnDef(const char* t, int w, int a, int s)
+ : title(t), width(w), align(a), sort(s),
+ color(Color::White), use_color(false)
+{ }
+
+// +----------------------------------------------------------------------+
+
+WinDef::WinDef(DWORD a_id, DWORD a_type, const char* a_text, DWORD a_style)
+ : id(a_id), pid(0), type(a_type), text(a_text), style(a_style)
+{
+ rect = Rect(0,0,0,0);
+ text_align = 0;
+ single_line = false;
+ enabled = true;
+ transparent = false;
+ hide_partial = true;
+ back_color = Color::Gray;
+ base_color = Color::Gray;
+ fore_color = Color::Black;
+ fixed_width = 0;
+ fixed_height = 0;
+}
+
+void WinDef::SetID(DWORD i) { id = i; }
+void WinDef::SetParentID(DWORD i) { pid = i; }
+void WinDef::SetType(DWORD t) { type = t; }
+void WinDef::SetRect(const Rect& r) { rect = r; }
+void WinDef::SetEnabled(bool e) { enabled = e; }
+void WinDef::SetStyle(DWORD s) { style = s; }
+void WinDef::SetFont(const char* t) { font = t; }
+void WinDef::SetText(const char* t) { text = t; }
+void WinDef::SetAltText(const char* t) { alt_text = t; }
+void WinDef::SetTexture(const char* t) { texture = t; }
+void WinDef::SetBackColor(Color c) { back_color = c; }
+void WinDef::SetBaseColor(Color c) { base_color = c; }
+void WinDef::SetForeColor(Color c) { fore_color = c; }
+void WinDef::SetTextAlign(DWORD a) { text_align = a; }
+void WinDef::SetSingleLine(bool a) { single_line = a; }
+void WinDef::SetTransparent(bool t) { transparent = t; }
+void WinDef::SetHidePartial(bool a) { hide_partial = a; }
+
+void WinDef::SetMargins(const Insets& m) { margins = m; }
+void WinDef::SetTextInsets(const Insets& t) { text_insets = t; }
+void WinDef::SetCellInsets(const Insets& c) { cell_insets = c; }
+void WinDef::SetCells(const Rect& r) { cells = r; }
+
+// +----------------------------------------------------------------------+
+
+#define CTRL_DEF_ANIMATED 0x0001
+#define CTRL_DEF_BORDER 0x0002
+#define CTRL_DEF_DROP_SHADOW 0x0004
+#define CTRL_DEF_INDENT 0x0008
+#define CTRL_DEF_INVERT_LABEL 0x0010
+#define CTRL_DEF_GLOW 0x0020
+#define CTRL_DEF_SIMPLE 0x0040
+#define CTRL_DEF_STICKY 0x0080
+
+CtrlDef::CtrlDef(DWORD a_id, DWORD a_type, const char* a_text, DWORD a_style)
+ : WinDef(a_id, a_type, a_text, a_style)
+{
+ ctrl_flags = CTRL_DEF_ANIMATED | CTRL_DEF_BORDER | CTRL_DEF_INDENT;
+ bevel_width = 5;
+ picture_loc = 1; // North
+ picture_type = Bitmap::BMP_SOLID;
+
+ active = false;
+ show_headings = false;
+
+ leading = 0;
+ line_height = 0;
+ multiselect = 0;
+ dragdrop = 0;
+ orientation = 0;
+ scroll_bar = 1;
+ num_leds = 1;
+
+ smooth_scroll = false;
+
+ item_style = 0;
+ selected_style = 0;
+ pass_char = 0;
+
+ items.destroy();
+
+ ZeroMemory(tabs, sizeof(tabs));
+ ntabs = 0;
+}
+
+CtrlDef::~CtrlDef()
+{
+ items.destroy();
+ columns.destroy();
+}
+
+CtrlDef& CtrlDef::operator=(const CtrlDef& ctrl)
+{
+ WinDef::operator=(ctrl);
+
+ ctrl_flags = ctrl.ctrl_flags;
+ bevel_width = ctrl.bevel_width;
+ picture_loc = ctrl.picture_loc;
+ picture_type = ctrl.picture_type;
+
+ active = ctrl.active;
+ show_headings = ctrl.show_headings;
+
+ leading = ctrl.leading;
+ line_height = ctrl.line_height;
+ multiselect = ctrl.multiselect;
+ dragdrop = ctrl.dragdrop;
+ orientation = ctrl.orientation;
+ scroll_bar = ctrl.scroll_bar;
+ pass_char = ctrl.pass_char;
+ active_color = ctrl.active_color;
+ border_color = ctrl.border_color;
+
+ smooth_scroll = ctrl.smooth_scroll;
+
+ item_style = ctrl.item_style;
+ selected_style = ctrl.selected_style;
+ pass_char = ctrl.pass_char;
+
+ standard_image = ctrl.standard_image;
+ activated_image = ctrl.activated_image;
+ transition_image = ctrl.transition_image;
+
+ return *this;
+}
+
+int CtrlDef::GetOrientation() const
+{
+ return orientation;
+}
+
+void CtrlDef::SetOrientation(int o)
+{
+ orientation = o;
+}
+
+bool CtrlDef::GetActive() const
+{
+ return active;
+}
+
+void CtrlDef::SetActive(bool c)
+{
+ active = c;
+}
+
+Color CtrlDef::GetActiveColor() const
+{
+ return active_color;
+}
+
+void CtrlDef::SetActiveColor(Color c)
+{
+ active_color = c;
+}
+
+bool CtrlDef::GetAnimated() const
+{
+ return ctrl_flags & CTRL_DEF_ANIMATED;
+}
+
+void CtrlDef::SetAnimated(bool bNewValue)
+{
+ if (bNewValue)
+ ctrl_flags |= CTRL_DEF_ANIMATED;
+ else
+ ctrl_flags &= ~CTRL_DEF_ANIMATED;
+}
+
+short CtrlDef::GetBevelWidth() const
+{
+ return bevel_width;
+}
+
+void CtrlDef::SetBevelWidth(short nNewValue)
+{
+ bevel_width = nNewValue;
+}
+
+bool CtrlDef::GetBorder() const
+{
+ return (ctrl_flags & CTRL_DEF_BORDER)?true:false;
+}
+
+void CtrlDef::SetBorder(bool bNewValue)
+{
+ if (bNewValue)
+ ctrl_flags |= CTRL_DEF_BORDER;
+ else
+ ctrl_flags &= ~CTRL_DEF_BORDER;
+}
+
+Color CtrlDef::GetBorderColor() const
+{
+ return border_color;
+}
+
+void CtrlDef::SetBorderColor(Color c)
+{
+ border_color = c;
+}
+
+bool CtrlDef::GetDropShadow() const
+{
+ return (ctrl_flags & CTRL_DEF_DROP_SHADOW)?true:false;
+}
+
+void CtrlDef::SetDropShadow(bool bNewValue)
+{
+ if (bNewValue)
+ ctrl_flags |= CTRL_DEF_DROP_SHADOW;
+ else
+ ctrl_flags &= ~CTRL_DEF_DROP_SHADOW;
+}
+
+bool CtrlDef::GetIndent() const
+{
+ return (ctrl_flags & CTRL_DEF_INDENT)?true:false;
+}
+
+void CtrlDef::SetIndent(bool bNewValue)
+{
+ if (bNewValue)
+ ctrl_flags |= CTRL_DEF_INDENT;
+ else
+ ctrl_flags &= ~CTRL_DEF_INDENT;
+}
+
+bool CtrlDef::GetInvertLabel() const
+{
+ return (ctrl_flags & CTRL_DEF_INVERT_LABEL)?true:false;
+}
+
+void CtrlDef::SetInvertLabel(bool bNewValue)
+{
+ if (bNewValue)
+ ctrl_flags |= CTRL_DEF_INVERT_LABEL;
+ else
+ ctrl_flags &= ~CTRL_DEF_INVERT_LABEL;
+}
+
+Text CtrlDef::GetPicture() const
+{
+ return picture;
+}
+
+void CtrlDef::SetPicture(const Text& img_name)
+{
+ picture = img_name;
+}
+
+short CtrlDef::GetPictureLocation() const
+{
+ return picture_loc;
+}
+
+void CtrlDef::SetPictureLocation(short nNewValue)
+{
+ picture_loc = nNewValue;
+}
+
+short CtrlDef::GetPictureType() const
+{
+ return picture_type;
+}
+
+void CtrlDef::SetPictureType(short nNewValue)
+{
+ picture_type = nNewValue;
+}
+
+bool CtrlDef::GetSticky() const
+{
+ return (ctrl_flags & CTRL_DEF_STICKY)?true:false;
+}
+
+void CtrlDef::SetSticky(bool bNewValue)
+{
+ if (bNewValue)
+ ctrl_flags |= CTRL_DEF_STICKY;
+ else
+ ctrl_flags &= ~CTRL_DEF_STICKY;
+}
+
+int CtrlDef::GetNumLeds() const
+{
+ return num_leds;
+}
+
+void CtrlDef::SetNumLeds(int n)
+{
+ if (n > 0)
+ num_leds = n;
+}
+
+int CtrlDef::NumItems() const
+{
+ return items.size();
+}
+
+Text CtrlDef::GetItem(int i) const
+{
+ Text result;
+
+ if (i >= 0 && i < items.size())
+ result = *(items[i]);
+
+ return result;
+}
+
+void CtrlDef::AddItem(const char* t)
+{
+ items.append(new(__FILE__,__LINE__) Text(t));
+}
+
+int CtrlDef::NumColumns() const
+{
+ return columns.size();
+}
+
+ColumnDef* CtrlDef::GetColumn(int i) const
+{
+ ColumnDef* result = 0;
+
+ if (i >= 0 && i < columns.size())
+ result = columns[i];
+
+ return result;
+}
+
+void CtrlDef::AddColumn(const char* t, int w, int a, int s)
+{
+ columns.append(new(__FILE__,__LINE__) ColumnDef(t,w,a,s));
+}
+
+int CtrlDef::NumTabs() const
+{
+ return ntabs;
+}
+
+int CtrlDef::GetTab(int i) const
+{
+ if (i >= 0 && i < ntabs)
+ return tabs[i];
+ return 0;
+}
+
+void CtrlDef::SetTab(int i, int t)
+{
+ if (i >= 0 && i < 10) {
+ tabs[i] = t;
+ if (i >= ntabs)
+ ntabs = i+1;
+ }
+}
+
+void CtrlDef::AddTab(int i)
+{
+ if (ntabs < 10)
+ tabs[ntabs++] = i;
+}
+
+bool CtrlDef::GetShowHeadings() const
+{
+ return show_headings;
+}
+
+void CtrlDef::SetShowHeadings(bool bNewValue)
+{
+ show_headings = bNewValue;
+}
+
+int CtrlDef::GetLeading() const
+{
+ return leading;
+}
+
+void CtrlDef::SetLeading(int nNewValue)
+{
+ leading = nNewValue;
+}
+
+int CtrlDef::GetLineHeight() const
+{
+ return line_height;
+}
+
+void CtrlDef::SetLineHeight(int nNewValue)
+{
+ line_height = nNewValue;
+}
+
+int CtrlDef::GetMultiSelect() const
+{
+ return multiselect;
+}
+
+void CtrlDef::SetMultiSelect(int nNewValue)
+{
+ multiselect = nNewValue;
+}
+
+int CtrlDef::GetDragDrop() const
+{
+ return dragdrop;
+}
+
+void CtrlDef::SetDragDrop(int nNewValue)
+{
+ dragdrop = nNewValue;
+}
+
+int CtrlDef::GetScrollBarVisible() const
+{
+ return scroll_bar;
+}
+
+void CtrlDef::SetScrollBarVisible(int nNewValue)
+{
+ scroll_bar = nNewValue;
+}
+
+bool CtrlDef::GetSmoothScroll() const
+{
+ return smooth_scroll;
+}
+
+void CtrlDef::SetSmoothScroll(bool bNewValue)
+{
+ smooth_scroll = bNewValue;
+}
+
+short CtrlDef::GetItemStyle() const
+{
+ return item_style;
+}
+
+void CtrlDef::SetItemStyle(short nNewValue)
+{
+ item_style = nNewValue;
+}
+
+short CtrlDef::GetSelectedStyle() const
+{
+ return selected_style;
+}
+
+void CtrlDef::SetSelectedStyle(short nNewValue)
+{
+ selected_style = nNewValue;
+}
+
+char CtrlDef::GetPasswordChar() const
+{
+ return pass_char;
+}
+
+void CtrlDef::SetPasswordChar(char nNewValue)
+{
+ pass_char = nNewValue;
+}
+
+Text CtrlDef::GetStandardImage() const
+{
+ return standard_image;
+}
+
+void CtrlDef::SetStandardImage(const Text& img_name)
+{
+ standard_image = img_name;
+}
+
+Text CtrlDef::GetActivatedImage() const
+{
+ return activated_image;
+}
+
+void CtrlDef::SetActivatedImage(const Text& img_name)
+{
+ activated_image = img_name;
+}
+
+Text CtrlDef::GetTransitionImage() const
+{
+ return transition_image;
+}
+
+void CtrlDef::SetTransitionImage(const Text& img_name)
+{
+ transition_image = img_name;
+}
+
+
+// +----------------------------------------------------------------------+
+
+FormDef::FormDef(const char* a_text, DWORD a_style)
+ : WinDef(0, WIN_DEF_FORM, a_text, a_style)
+{
+}
+
+FormDef::~FormDef()
+{
+ controls.destroy();
+}
+
+void FormDef::AddCtrl(CtrlDef* def)
+{
+ if (def)
+ controls.append(def);
+}
+
+CtrlDef* FormDef::FindCtrl(BYTE ctrl_id)
+{
+ if (ctrl_id > 0) {
+ CtrlDef test(ctrl_id, 0);
+ return controls.find(&test);
+ }
+
+ return 0;
+}
+
+ListIter<CtrlDef>
+FormDef::GetControls() const
+{
+ // cast away const
+ FormDef* f = (FormDef*) this;
+ return f->controls;
+}
+
+// +----------------------------------------------------------------------+
+
+static char filename[64];
+static char path_name[64];
+
+void
+FormDef::Load(const char* fname)
+{
+ sprintf(filename, "%s.frm", fname);
+
+ Print("Loading Form '%s'\n", fname);
+
+ sprintf(path_name, "Screens/");
+
+ // Load Design File:
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath(path_name);
+
+ BYTE* block;
+ int blocklen = loader->LoadBuffer(filename, block, true);
+
+ if (!block || blocklen < 4)
+ return;
+
+ Parser parser(new(__FILE__,__LINE__) BlockReader((const char*) block, blocklen));
+ Term* term = parser.ParseTerm();
+
+ if (!term) {
+ Print("ERROR: could not parse '%s'\n", filename);
+ return;
+ }
+ else {
+ TermText* file_type = term->isText();
+ if (!file_type || file_type->value() != "FORM") {
+ Print("ERROR: invalid form file '%s'\n", filename);
+ return;
+ }
+ }
+
+ do {
+ delete term;
+
+ term = parser.ParseTerm();
+
+ if (term) {
+ TermDef* def = term->isDef();
+ if (def) {
+ if (def->name()->value() == "form") {
+
+ if (!def->term() || !def->term()->isStruct()) {
+ Print("WARNING: form structure missing in '%s'\n", filename);
+ }
+ else {
+ FormDef* form = this;
+ TermStruct* val = def->term()->isStruct();
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ char buf[256];
+
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ if (pdef->name()->value() == "text" ||
+ pdef->name()->value() == "caption") {
+
+ GetDefText(buf, pdef, filename);
+ form->SetText(Game::GetText(buf));
+ }
+
+ else if (pdef->name()->value() == "id") {
+ DWORD id;
+ GetDefNumber(id, pdef, filename);
+ form->SetID(id);
+ }
+
+ else if (pdef->name()->value() == "pid") {
+ DWORD id;
+ GetDefNumber(id, pdef, filename);
+ form->SetParentID(id);
+ }
+
+ else if (pdef->name()->value() == "rect") {
+ Rect r;
+ GetDefRect(r, pdef, filename);
+ form->SetRect(r);
+ }
+
+ else if (pdef->name()->value() == "font") {
+ GetDefText(buf, pdef, filename);
+ form->SetFont(buf);
+ }
+
+ else if (pdef->name()->value() == "back_color") {
+ Color c;
+ GetDefColor(c, pdef, filename);
+ form->SetBackColor(c);
+ }
+
+ else if (pdef->name()->value() == "base_color") {
+ Color c;
+ GetDefColor(c, pdef, filename);
+ form->SetBaseColor(c);
+ }
+
+ else if (pdef->name()->value() == "fore_color") {
+ Color c;
+ GetDefColor(c, pdef, filename);
+ form->SetForeColor(c);
+ }
+
+ else if (pdef->name()->value() == "margins") {
+ GetDefInsets(form->margins, pdef, filename);
+ }
+
+ else if (pdef->name()->value() == "text_insets") {
+ GetDefInsets(form->text_insets, pdef, filename);
+ }
+
+ else if (pdef->name()->value() == "cell_insets") {
+ GetDefInsets(form->cell_insets, pdef, filename);
+ }
+
+ else if (pdef->name()->value() == "cells") {
+ GetDefRect(form->cells, pdef, filename);
+ }
+
+ else if (pdef->name()->value() == "texture") {
+ GetDefText(buf, pdef, filename);
+
+ if (*buf && !strchr(buf, '.'))
+ strcat(buf, ".pcx");
+
+ form->SetTexture(buf);
+ }
+
+ else if (pdef->name()->value() == "transparent") {
+ bool b;
+ GetDefBool(b, pdef, filename);
+ form->SetTransparent(b);
+ }
+
+ else if (pdef->name()->value() == "style") {
+ DWORD s;
+ GetDefNumber(s, pdef, filename);
+ form->SetStyle(s);
+ }
+
+ else if (pdef->name()->value() == "align" ||
+ pdef->name()->value() == "text_align") {
+ DWORD a = DT_LEFT;
+
+ if (GetDefText(buf, pdef, filename)) {
+ if (!stricmp(buf, "left"))
+ a = DT_LEFT;
+ else if (!stricmp(buf, "right"))
+ a = DT_RIGHT;
+ else if (!stricmp(buf, "center"))
+ a = DT_CENTER;
+ }
+
+ else {
+ GetDefNumber(a, pdef, filename);
+ }
+
+ form->SetTextAlign(a);
+ }
+
+ // layout constraints:
+
+ else if (pdef->name()->value() == "layout") {
+
+ if (!pdef->term() || !pdef->term()->isStruct()) {
+ Print("WARNING: layout structure missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = pdef->term()->isStruct();
+ ParseLayoutDef(&form->layout, val);
+ }
+ }
+
+ // controls:
+
+ else if (pdef->name()->value() == "defctrl") {
+
+ if (!pdef->term() || !pdef->term()->isStruct()) {
+ Print("WARNING: defctrl structure missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = pdef->term()->isStruct();
+ ParseCtrlDef(&form->defctrl, val);
+ }
+ }
+
+ else if (pdef->name()->value() == "ctrl") {
+
+ if (!pdef->term() || !pdef->term()->isStruct()) {
+ Print("WARNING: ctrl structure missing in '%s'\n", filename);
+ }
+ else {
+ CtrlDef* ctrl = new(__FILE__,__LINE__) CtrlDef;
+ TermStruct* val = pdef->term()->isStruct();
+
+ form->AddCtrl(ctrl);
+ *ctrl = form->defctrl; // copy default params
+
+ ParseCtrlDef(ctrl, val);
+ }
+ }
+
+ // end of controls.
+ }
+ } // end form params
+ } // end form struct
+ } // end form
+
+ else
+ Print("WARNING: unknown object '%s' in '%s'\n",
+ def->name()->value().data(), filename);
+ }
+ else {
+ Print("WARNING: term ignored in '%s'\n", filename);
+ term->print();
+ }
+ }
+ }
+ while (term);
+
+ loader->ReleaseBuffer(block);
+ loader->SetDataPath(0);
+}
+
+void FormDef::ParseCtrlDef(CtrlDef* ctrl, TermStruct* val)
+{
+ Text buf;
+
+ ctrl->SetText("");
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ if (pdef->name()->value() == "text" ||
+ pdef->name()->value() == "caption") {
+ GetDefText(buf, pdef, filename);
+ ctrl->SetText(Game::GetText(buf));
+ }
+
+ else if (pdef->name()->value() == "id") {
+ DWORD id;
+ GetDefNumber(id, pdef, filename);
+ ctrl->SetID(id);
+ }
+
+ else if (pdef->name()->value() == "pid") {
+ DWORD id;
+ GetDefNumber(id, pdef, filename);
+ ctrl->SetParentID(id);
+ }
+
+ else if (pdef->name()->value() == "alt") {
+ GetDefText(buf, pdef, filename);
+ ctrl->SetAltText(Game::GetText(buf));
+ }
+
+ else if (pdef->name()->value() == "type") {
+ DWORD type = WIN_DEF_LABEL;
+
+ GetDefText(buf, pdef, filename);
+ Text type_name(buf);
+
+ if (type_name == "button")
+ type = WIN_DEF_BUTTON;
+
+ else if (type_name == "combo")
+ type = WIN_DEF_COMBO;
+
+ else if (type_name == "edit")
+ type = WIN_DEF_EDIT;
+
+ else if (type_name == "image")
+ type = WIN_DEF_IMAGE;
+
+ else if (type_name == "slider")
+ type = WIN_DEF_SLIDER;
+
+ else if (type_name == "list")
+ type = WIN_DEF_LIST;
+
+ else if (type_name == "rich" || type_name == "text" || type_name == "rich_text")
+ type = WIN_DEF_RICH;
+
+ ctrl->SetType(type);
+ }
+
+ else if (pdef->name()->value() == "rect") {
+ Rect r;
+ GetDefRect(r, pdef, filename);
+ ctrl->SetRect(r);
+ }
+
+ else if (pdef->name()->value() == "font") {
+ GetDefText(buf, pdef, filename);
+ ctrl->SetFont(buf);
+ }
+
+ else if (pdef->name()->value() == "active_color") {
+ Color c;
+ GetDefColor(c, pdef, filename);
+ ctrl->SetActiveColor(c);
+ }
+
+ else if (pdef->name()->value() == "back_color") {
+ Color c;
+ GetDefColor(c, pdef, filename);
+ ctrl->SetBackColor(c);
+ }
+
+ else if (pdef->name()->value() == "base_color") {
+ Color c;
+ GetDefColor(c, pdef, filename);
+ ctrl->SetBaseColor(c);
+ }
+
+ else if (pdef->name()->value() == "border_color") {
+ Color c;
+ GetDefColor(c, pdef, filename);
+ ctrl->SetBorderColor(c);
+ }
+
+ else if (pdef->name()->value() == "fore_color") {
+ Color c;
+ GetDefColor(c, pdef, filename);
+ ctrl->SetForeColor(c);
+ }
+
+ else if (pdef->name()->value() == "texture") {
+ GetDefText(buf, pdef, filename);
+
+ if (buf.length() > 0 && !buf.contains('.'))
+ buf.append(".pcx");
+
+ ctrl->SetTexture(buf);
+ }
+
+ else if (pdef->name()->value() == "margins") {
+ GetDefInsets(ctrl->margins, pdef, filename);
+ }
+
+ else if (pdef->name()->value() == "text_insets") {
+ GetDefInsets(ctrl->text_insets, pdef, filename);
+ }
+
+ else if (pdef->name()->value() == "cell_insets") {
+ GetDefInsets(ctrl->cell_insets, pdef, filename);
+ }
+
+ else if (pdef->name()->value() == "cells") {
+ GetDefRect(ctrl->cells, pdef, filename);
+ }
+
+ else if (pdef->name()->value() == "fixed_width") {
+ GetDefNumber(ctrl->fixed_width, pdef, filename);
+ }
+
+ else if (pdef->name()->value() == "fixed_height") {
+ GetDefNumber(ctrl->fixed_height, pdef, filename);
+ }
+
+ else if (pdef->name()->value() == "standard_image") {
+ GetDefText(buf, pdef, filename);
+
+ if (buf.length() > 0 && !buf.contains('.'))
+ buf.append(".pcx");
+
+ ctrl->SetStandardImage(buf);
+ }
+
+ else if (pdef->name()->value() == "activated_image") {
+ GetDefText(buf, pdef, filename);
+
+ if (buf.length() > 0 && !buf.contains('.'))
+ buf.append(".pcx");
+
+ ctrl->SetActivatedImage(buf);
+ }
+
+ else if (pdef->name()->value() == "transition_image") {
+ GetDefText(buf, pdef, filename);
+
+ if (buf.length() > 0 && !buf.contains('.'))
+ buf.append(".pcx");
+
+ ctrl->SetTransitionImage(buf);
+ }
+
+ else if (pdef->name()->value() == "picture") {
+ GetDefText(buf, pdef, filename);
+
+ if (buf.length() > 0 && !buf.contains('.'))
+ buf.append(".pcx");
+
+ ctrl->SetPicture(buf);
+ }
+
+ else if (pdef->name()->value() == "enabled") {
+ bool e;
+ GetDefBool(e, pdef, filename);
+ ctrl->SetEnabled(e);
+ }
+
+ else if (pdef->name()->value() == "item") {
+ GetDefText(buf, pdef, filename);
+ ctrl->AddItem(Game::GetText(buf));
+ }
+
+ else if (pdef->name()->value() == "tab") {
+ int tab = 0;
+ GetDefNumber(tab, pdef, filename);
+ ctrl->AddTab(tab);
+ }
+
+ else if (pdef->name()->value() == "column") {
+
+ if (!pdef->term() || !pdef->term()->isStruct()) {
+ Print("WARNING: column structure missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = pdef->term()->isStruct();
+ ParseColumnDef(ctrl, val);
+ }
+ }
+
+ else if (pdef->name()->value() == "orientation") {
+ int n;
+ GetDefNumber(n, pdef, filename);
+ ctrl->SetOrientation(n);
+ }
+
+ else if (pdef->name()->value() == "leading") {
+ int n;
+ GetDefNumber(n, pdef, filename);
+ ctrl->SetLeading(n);
+ }
+
+ else if (pdef->name()->value() == "line_height") {
+ int n;
+ GetDefNumber(n, pdef, filename);
+ ctrl->SetLineHeight(n);
+ }
+
+ else if (pdef->name()->value() == "multiselect") {
+ int n;
+ GetDefNumber(n, pdef, filename);
+ ctrl->SetMultiSelect(n);
+ }
+
+ else if (pdef->name()->value() == "dragdrop") {
+ int n;
+ GetDefNumber(n, pdef, filename);
+ ctrl->SetDragDrop(n);
+ }
+
+ else if (pdef->name()->value() == "scroll_bar") {
+ int n;
+ GetDefNumber(n, pdef, filename);
+ ctrl->SetScrollBarVisible(n);
+ }
+
+ else if (pdef->name()->value() == "smooth_scroll") {
+ bool b;
+ GetDefBool(b, pdef, filename);
+ ctrl->SetSmoothScroll(b);
+ }
+
+ else if (pdef->name()->value() == "picture_loc") {
+ int n;
+ GetDefNumber(n, pdef, filename);
+ ctrl->SetPictureLocation((short) n);
+ }
+
+ else if (pdef->name()->value() == "picture_type") {
+ int n;
+ GetDefNumber(n, pdef, filename);
+ ctrl->SetPictureType((short) n);
+ }
+
+ else if (pdef->name()->value() == "style") {
+ DWORD s;
+ GetDefNumber(s, pdef, filename);
+ ctrl->SetStyle(s);
+ }
+
+ else if (pdef->name()->value() == "align" ||
+ pdef->name()->value() == "text_align") {
+ DWORD a = DT_LEFT;
+
+ if (GetDefText(buf, pdef, filename)) {
+ if (!stricmp(buf, "left"))
+ a = DT_LEFT;
+ else if (!stricmp(buf, "right"))
+ a = DT_RIGHT;
+ else if (!stricmp(buf, "center"))
+ a = DT_CENTER;
+ }
+
+ else {
+ GetDefNumber(a, pdef, filename);
+ }
+
+ ctrl->SetTextAlign(a);
+ }
+
+ else if (pdef->name()->value() == "single_line") {
+ bool single = false;
+ GetDefBool(single, pdef, filename);
+ ctrl->SetSingleLine(single);
+ }
+
+ else if (pdef->name()->value() == "bevel_width") {
+ DWORD s;
+ GetDefNumber(s, pdef, filename);
+ ctrl->SetBevelWidth((short) s);
+ }
+
+ else if (pdef->name()->value() == "active") {
+ bool b;
+ GetDefBool(b, pdef, filename);
+ ctrl->SetActive(b);
+ }
+
+ else if (pdef->name()->value() == "animated") {
+ bool b;
+ GetDefBool(b, pdef, filename);
+ ctrl->SetAnimated(b);
+ }
+
+ else if (pdef->name()->value() == "border") {
+ bool b;
+ GetDefBool(b, pdef, filename);
+ ctrl->SetBorder(b);
+ }
+
+ else if (pdef->name()->value() == "drop_shadow") {
+ bool b;
+ GetDefBool(b, pdef, filename);
+ ctrl->SetDropShadow(b);
+ }
+
+ else if (pdef->name()->value() == "show_headings") {
+ bool b;
+ GetDefBool(b, pdef, filename);
+ ctrl->SetShowHeadings(b);
+ }
+
+ else if (pdef->name()->value() == "sticky") {
+ bool b;
+ GetDefBool(b, pdef, filename);
+ ctrl->SetSticky(b);
+ }
+
+ else if (pdef->name()->value() == "transparent") {
+ bool b;
+ GetDefBool(b, pdef, filename);
+ ctrl->SetTransparent(b);
+ }
+
+ else if (pdef->name()->value() == "hide_partial") {
+ bool b;
+ GetDefBool(b, pdef, filename);
+ ctrl->SetHidePartial(b);
+ }
+
+ else if (pdef->name()->value() == "num_leds") {
+ int n;
+ GetDefNumber(n, pdef, filename);
+ ctrl->SetNumLeds(n);
+ }
+
+ else if (pdef->name()->value() == "item_style") {
+ int n;
+ GetDefNumber(n, pdef, filename);
+ ctrl->SetItemStyle((short) n);
+ }
+
+ else if (pdef->name()->value() == "selected_style") {
+ int n;
+ GetDefNumber(n, pdef, filename);
+ ctrl->SetSelectedStyle((short) n);
+ }
+
+ else if (pdef->name()->value() == "password") {
+ Text password;
+ GetDefText(password, pdef, filename);
+ ctrl->SetPasswordChar((char) password[0]);
+ }
+
+ // layout constraints:
+
+ else if (pdef->name()->value() == "layout") {
+
+ if (!pdef->term() || !pdef->term()->isStruct()) {
+ Print("WARNING: layout structure missing in '%s'\n", filename);
+ }
+ else {
+ TermStruct* val = pdef->term()->isStruct();
+ ParseLayoutDef(&ctrl->layout, val);
+ }
+ }
+ }
+ }
+}
+
+void FormDef::ParseColumnDef(CtrlDef* ctrl, TermStruct* val)
+{
+ Text text;
+ char buf[256];
+ int width = 0;
+ int align = 0;
+ int sort = 0;
+ Color c;
+ bool use_color = false;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ if (pdef->name()->value() == "text" ||
+ pdef->name()->value() == "title") {
+ GetDefText(buf, pdef, filename);
+ text = Game::GetText(buf);
+ }
+
+ else if (pdef->name()->value() == "width") {
+ GetDefNumber(width, pdef, filename);
+ }
+
+ else if (pdef->name()->value() == "align") {
+ align = DT_LEFT;
+
+ if (GetDefText(buf, pdef, filename)) {
+ if (!stricmp(buf, "left"))
+ align = DT_LEFT;
+ else if (!stricmp(buf, "right"))
+ align = DT_RIGHT;
+ else if (!stricmp(buf, "center"))
+ align = DT_CENTER;
+ }
+
+ else {
+ GetDefNumber(align, pdef, filename);
+ }
+ }
+
+ else if (pdef->name()->value() == "sort") {
+ GetDefNumber(sort, pdef, filename);
+ }
+
+ else if (pdef->name()->value() == "color") {
+ GetDefColor(c, pdef, filename);
+ use_color = true;
+ }
+ }
+ }
+
+ ctrl->AddColumn(text, width, align, sort);
+
+ if (use_color) {
+ int index = ctrl->NumColumns()-1;
+ ColumnDef* column = ctrl->GetColumn(index);
+
+ if (column) {
+ column->color = c;
+ column->use_color = true;
+ }
+ }
+}
+
+void FormDef::ParseLayoutDef(LayoutDef* def, TermStruct* val)
+{
+ if (!def || !val)
+ return;
+
+ for (int i = 0; i < val->elements()->size(); i++) {
+ TermDef* pdef = val->elements()->at(i)->isDef();
+ if (pdef) {
+ if (pdef->name()->value() == "x_mins" ||
+ pdef->name()->value() == "cols") {
+ GetDefArray(def->x_mins, pdef, filename);
+ }
+
+ else
+ if (pdef->name()->value() == "y_mins" ||
+ pdef->name()->value() == "rows") {
+ GetDefArray(def->y_mins, pdef, filename);
+ }
+
+ else
+ if (pdef->name()->value() == "x_weights" ||
+ pdef->name()->value() == "col_wts") {
+ GetDefArray(def->x_weights, pdef, filename);
+ }
+
+ else
+ if (pdef->name()->value() == "y_weights" ||
+ pdef->name()->value() == "row_wts") {
+ GetDefArray(def->y_weights, pdef, filename);
+ }
+ }
+ }
+
+}
+
+
diff --git a/nGenEx/FormDef.h b/nGenEx/FormDef.h
new file mode 100644
index 0000000..131d3f4
--- /dev/null
+++ b/nGenEx/FormDef.h
@@ -0,0 +1,332 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: FormDef.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Form and Control Definition Resources
+*/
+
+#ifndef FormDef_h
+#define FormDef_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "Color.h"
+#include "Text.h"
+#include "ArrayList.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class FormDef; // values defining form style and control placement
+class CtrlDef; // values defining control style
+class WinDef; // base class for FormDef and CtrlDef
+class TermStruct; // used for parsing
+
+enum WinType {
+ WIN_DEF_FORM,
+ WIN_DEF_LABEL,
+ WIN_DEF_BUTTON,
+ WIN_DEF_COMBO,
+ WIN_DEF_EDIT,
+ WIN_DEF_IMAGE,
+ WIN_DEF_SLIDER,
+ WIN_DEF_LIST,
+ WIN_DEF_RICH
+};
+
+// +--------------------------------------------------------------------+
+
+class ColumnDef
+{
+public:
+ static const char* TYPENAME() { return "ColumnDef"; }
+
+ ColumnDef();
+ ColumnDef(const char* title, int width, int align, int sort);
+
+ Text title;
+ int width;
+ int align;
+ int sort;
+ Color color;
+ bool use_color;
+};
+
+// +--------------------------------------------------------------------+
+
+class LayoutDef
+{
+public:
+ static const char* TYPENAME() { return "LayoutDef"; }
+
+ ArrayList x_mins;
+ ArrayList y_mins;
+ FloatList x_weights;
+ FloatList y_weights;
+};
+
+// +--------------------------------------------------------------------+
+
+class WinDef
+{
+ friend class FormDef;
+
+public:
+ static const char* TYPENAME() { return "WinDef"; }
+
+ WinDef(DWORD id, DWORD type, const char* text=0, DWORD style=0);
+ virtual ~WinDef() { }
+
+ int operator == (const WinDef& w) const { return id == w.id; }
+
+ DWORD GetID() const { return id; }
+ void SetID(DWORD id);
+ DWORD GetParentID() const { return pid; }
+ void SetParentID(DWORD id);
+ DWORD GetType() const { return type; }
+ void SetType(DWORD type);
+
+ void SetRect(const Rect& r);
+ Rect GetRect() const { return rect; }
+ int GetX() const { return rect.x; }
+ int GetY() const { return rect.y; }
+ int GetW() const { return rect.w; }
+ int GetH() const { return rect.h; }
+
+ void SetEnabled(bool enable=true);
+ bool IsEnabled() const { return enabled; }
+
+ void SetStyle(DWORD s);
+ DWORD GetStyle() const { return style; }
+
+ void SetFont(const char* t);
+ const Text& GetFont() const { return font; }
+ void SetText(const char* t);
+ const Text& GetText() const { return text; }
+ void SetAltText(const char* t);
+ const Text& GetAltText() const { return alt_text; }
+ void SetTexture(const char* t);
+ const Text& GetTexture() const { return texture; }
+
+ void SetBackColor(Color c);
+ Color GetBackColor() const { return back_color; }
+ void SetBaseColor(Color c);
+ Color GetBaseColor() const { return base_color; }
+ void SetForeColor(Color c);
+ Color GetForeColor() const { return fore_color; }
+ void SetSingleLine(bool a);
+ bool GetSingleLine() const { return single_line; }
+ void SetTextAlign(DWORD a);
+ DWORD GetTextAlign() const { return text_align; }
+ void SetTransparent(bool t);
+ bool GetTransparent() const { return transparent; }
+ void SetHidePartial(bool a);
+ bool GetHidePartial() const { return hide_partial;}
+
+ void SetMargins(const Insets& m);
+ const Insets& GetMargins() const { return margins; }
+ void SetTextInsets(const Insets& t);
+ const Insets& GetTextInsets() const { return text_insets; }
+ void SetCellInsets(const Insets& t);
+ const Insets& GetCellInsets() const { return cell_insets; }
+ void SetCells(const Rect& r);
+ const Rect& GetCells() const { return cells; }
+
+ void SetFixedWidth(int w) { fixed_width = w; }
+ int GetFixedWidth() const { return fixed_width; }
+ void SetFixedHeight(int h) { fixed_height = h; }
+ int GetFixedHeight() const { return fixed_height;}
+
+ const LayoutDef& GetLayout() const { return layout; }
+
+protected:
+ DWORD id;
+ DWORD pid;
+ DWORD type;
+ Rect rect;
+ Text font;
+ Text text;
+ Text alt_text;
+ Text texture;
+ Text picture;
+ DWORD style;
+ DWORD text_align;
+ bool single_line;
+ bool enabled;
+ bool transparent;
+ bool hide_partial;
+ Color back_color;
+ Color base_color;
+ Color fore_color;
+
+ Insets margins;
+ Insets text_insets;
+ Insets cell_insets;
+ Rect cells;
+ int fixed_width;
+ int fixed_height;
+
+ LayoutDef layout;
+};
+
+// +--------------------------------------------------------------------+
+
+class CtrlDef : public WinDef
+{
+public:
+ static const char* TYPENAME() { return "CtrlDef"; }
+
+ CtrlDef(DWORD id=0, DWORD type=WIN_DEF_LABEL, const char* text=0, DWORD style=0);
+ virtual ~CtrlDef();
+
+ virtual CtrlDef& operator=(const CtrlDef& ctrl);
+
+ bool GetActive() const;
+ void SetActive(bool c);
+ Color GetActiveColor() const;
+ void SetActiveColor(Color c);
+ bool GetAnimated() const;
+ void SetAnimated(bool bNewValue);
+ short GetBevelWidth() const;
+ void SetBevelWidth(short nNewValue);
+ bool GetBorder() const;
+ void SetBorder(bool bNewValue);
+ Color GetBorderColor() const;
+ void SetBorderColor(Color c);
+ bool GetDropShadow() const;
+ void SetDropShadow(bool bNewValue);
+ bool GetIndent() const;
+ void SetIndent(bool bNewValue);
+ bool GetInvertLabel() const;
+ void SetInvertLabel(bool bNewValue);
+ int GetOrientation() const;
+ void SetOrientation(int o);
+ Text GetPicture() const;
+ void SetPicture(const Text& img_name);
+ short GetPictureLocation() const;
+ void SetPictureLocation(short nNewValue);
+ short GetPictureType() const;
+ void SetPictureType(short nNewValue);
+ bool GetSticky() const;
+ void SetSticky(bool bNewValue);
+ int GetNumLeds() const;
+ void SetNumLeds(int nNewValue);
+
+ int NumItems() const;
+ Text GetItem(int i) const;
+ void AddItem(const char* t);
+
+ int NumColumns() const;
+ ColumnDef* GetColumn(int i) const;
+ void AddColumn(const char* t, int w, int a, int s);
+
+ int NumTabs() const;
+ int GetTab(int i) const;
+ void SetTab(int i, int t);
+ void AddTab(int i);
+
+ bool GetShowHeadings() const;
+ void SetShowHeadings(bool bNewValue);
+ int GetLeading() const;
+ void SetLeading(int nNewValue);
+ int GetLineHeight() const;
+ void SetLineHeight(int nNewValue);
+ int GetMultiSelect() const;
+ void SetMultiSelect(int nNewValue);
+ int GetDragDrop() const;
+ void SetDragDrop(int nNewValue);
+ int GetScrollBarVisible() const;
+ void SetScrollBarVisible(int nNewValue);
+ bool GetSmoothScroll() const;
+ void SetSmoothScroll(bool bNewValue);
+
+ short GetItemStyle() const;
+ void SetItemStyle(short nNewValue);
+ short GetSelectedStyle() const;
+ void SetSelectedStyle(short nNewValue);
+
+ char GetPasswordChar() const;
+ void SetPasswordChar(char c);
+
+ Text GetStandardImage() const;
+ void SetStandardImage(const Text& img_name);
+ Text GetActivatedImage() const;
+ void SetActivatedImage(const Text& img_name);
+ Text GetTransitionImage() const;
+ void SetTransitionImage(const Text& img_name);
+
+protected:
+ WORD ctrl_flags;
+ short bevel_width;
+
+ Color active_color;
+ Color border_color;
+
+ Text picture;
+ short picture_loc;
+ short picture_type;
+
+ Text standard_image;
+ Text activated_image;
+ Text transition_image;
+
+ bool active;
+ bool show_headings;
+ int leading;
+ int line_height;
+ int multiselect;
+ int dragdrop;
+ int scroll_bar;
+ int orientation;
+ int num_leds;
+
+ short item_style;
+ short selected_style;
+
+ bool smooth_scroll;
+
+ List<Text> items;
+ List<ColumnDef> columns;
+
+ int ntabs;
+ int tabs[10];
+ char pass_char;
+};
+
+// +--------------------------------------------------------------------+
+
+class FormDef : public WinDef
+{
+public:
+ static const char* TYPENAME() { return "FormDef"; }
+
+ FormDef(const char* text=0, DWORD style=0);
+ virtual ~FormDef();
+
+ void Load(const char* filename);
+
+ void AddCtrl(CtrlDef* def);
+ CtrlDef* FindCtrl(BYTE ctrl_id);
+
+ ListIter<CtrlDef> GetControls() const;
+
+protected:
+ void ParseCtrlDef(CtrlDef* ctrl, TermStruct* val);
+ void ParseColumnDef(CtrlDef* ctrl, TermStruct* val);
+ void ParseLayoutDef(LayoutDef* def, TermStruct* val);
+
+ CtrlDef defctrl;
+ List<CtrlDef> controls;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif FormDef_h
+
diff --git a/nGenEx/FormWindow.cpp b/nGenEx/FormWindow.cpp
new file mode 100644
index 0000000..1613491
--- /dev/null
+++ b/nGenEx/FormWindow.cpp
@@ -0,0 +1,809 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Form.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Form Window class
+*/
+
+#include "MemDebug.h"
+#include "FormWindow.h"
+#include "Screen.h"
+#include "DataLoader.h"
+#include "Font.h"
+#include "FontMgr.h"
+
+#include "Button.h"
+#include "ComboBox.h"
+#include "EditBox.h"
+#include "ImageBox.h"
+#include "ListBox.h"
+#include "RichTextBox.h"
+#include "Slider.h"
+
+// +--------------------------------------------------------------------+
+
+FormWindow::FormWindow(Screen* screen, int ax, int ay, int aw, int ah,
+ DWORD aid, DWORD s, ActiveWindow* pParent)
+ : ActiveWindow(screen, ax, ay, aw, ah, aid, s, pParent)
+{
+ char buf[32];
+ sprintf(buf, "Form %d", id);
+ desc = buf;
+}
+
+// +--------------------------------------------------------------------+
+
+FormWindow::~FormWindow()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FormWindow::Init()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FormWindow::Destroy()
+{
+ Hide();
+ children.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FormWindow::AddControl(ActiveWindow* ctrl)
+{
+ if (ctrl) {
+ if (!children.contains(ctrl))
+ children.append(ctrl);
+
+ ctrl->SetForm(this);
+
+ if (!shown)
+ ctrl->Hide();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Button*
+FormWindow::CreateButton(const char* btn_text, int ax, int ay, int aw, int ah, DWORD aid, DWORD pid)
+{
+ Button* button = 0;
+ ActiveWindow* parent = this;
+
+ if (pid)
+ parent = FindControl(pid);
+
+ button = new(__FILE__,__LINE__) Button(parent, ax, ay, aw, ah, aid);
+
+ if (button) {
+ button->SetForm(this);
+ button->SetText(btn_text);
+
+ if (!shown)
+ button->Hide();
+ }
+
+ return button;
+}
+
+// +--------------------------------------------------------------------+
+
+ImageBox*
+FormWindow::CreateImageBox(const char* lbl_text, int ax, int ay, int aw, int ah, DWORD aid, DWORD pid)
+{
+ ImageBox* image = 0;
+ ActiveWindow* parent = this;
+
+ if (pid)
+ parent = FindControl(pid);
+
+ image = new(__FILE__,__LINE__) ImageBox(parent, ax, ay, aw, ah, aid);
+
+ if (image) {
+ image->SetForm(this);
+ image->SetText(lbl_text);
+
+ if (!shown)
+ image->Hide();
+ }
+
+ return image;
+}
+
+// +--------------------------------------------------------------------+
+
+ActiveWindow*
+FormWindow::CreateLabel(const char* label_text, int ax, int ay, int aw, int ah, DWORD aid, DWORD pid, DWORD astyle)
+{
+ ActiveWindow* label = 0;
+ ActiveWindow* parent = this;
+
+ if (pid)
+ parent = FindControl(pid);
+
+ label = new(__FILE__,__LINE__) ActiveWindow(screen, ax, ay, aw, ah, aid, astyle, parent);
+
+ if (label) {
+ label->SetForm(this);
+ label->SetText(label_text);
+
+ if (!shown)
+ label->Hide();
+ }
+
+ return label;
+}
+
+// +--------------------------------------------------------------------+
+
+ListBox*
+FormWindow::CreateListBox(const char* lbl_text, int ax, int ay, int aw, int ah, DWORD aid, DWORD pid)
+{
+ ListBox* list = 0;
+ ActiveWindow* parent = this;
+
+ if (pid)
+ parent = FindControl(pid);
+
+ list = new(__FILE__,__LINE__) ListBox(parent, ax, ay, aw, ah, aid);
+
+ if (list) {
+ list->SetForm(this);
+
+ if (!shown)
+ list->Hide();
+ }
+
+ return list;
+}
+
+// +--------------------------------------------------------------------+
+
+ComboBox*
+FormWindow::CreateComboBox(const char* lbl_text, int ax, int ay, int aw, int ah, DWORD aid, DWORD pid)
+{
+ ComboBox* combo = 0;
+ ActiveWindow* parent = this;
+
+ if (pid)
+ parent = FindControl(pid);
+
+ combo = new(__FILE__,__LINE__) ComboBox(parent, ax, ay, aw, ah, aid);
+
+ if (combo) {
+ combo->SetForm(this);
+ combo->SetLabel(lbl_text);
+
+ if (!shown)
+ combo->Hide();
+ }
+
+ return combo;
+}
+
+// +--------------------------------------------------------------------+
+
+EditBox*
+FormWindow::CreateEditBox(const char* lbl_text, int ax, int ay, int aw, int ah, DWORD aid, DWORD pid)
+{
+ EditBox* edit = 0;
+ ActiveWindow* parent = this;
+
+ if (pid)
+ parent = FindControl(pid);
+
+ edit = new(__FILE__,__LINE__) EditBox(parent, ax, ay, aw, ah, aid);
+
+ if (edit) {
+ edit->SetForm(this);
+ edit->SetText(lbl_text);
+
+ if (!shown)
+ edit->Hide();
+ }
+
+ return edit;
+}
+
+// +--------------------------------------------------------------------+
+
+RichTextBox*
+FormWindow::CreateRichTextBox(const char* label_text, int ax, int ay, int aw, int ah, DWORD aid, DWORD pid, DWORD astyle)
+{
+ RichTextBox* rtb = 0;
+ ActiveWindow* parent = this;
+
+ if (pid)
+ parent = FindControl(pid);
+
+ rtb = new(__FILE__,__LINE__) RichTextBox(parent, ax, ay, aw, ah, aid, astyle);
+
+ if (rtb) {
+ rtb->SetForm(this);
+ rtb->SetText(label_text);
+
+ if (!shown)
+ rtb->Hide();
+ }
+
+ return rtb;
+}
+
+// +--------------------------------------------------------------------+
+
+Slider*
+FormWindow::CreateSlider(const char* label_text, int ax, int ay, int aw, int ah, DWORD aid, DWORD pid, DWORD astyle)
+{
+ Slider* slider = 0;
+ ActiveWindow* parent = this;
+
+ if (pid)
+ parent = FindControl(pid);
+
+ slider = new(__FILE__,__LINE__) Slider(parent, ax, ay, aw, ah, aid);
+
+ if (slider) {
+ slider->SetForm(this);
+
+ if (!shown)
+ slider->Hide();
+ }
+
+ return slider;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FormWindow::Init(const FormDef& def)
+{
+ if (def.GetRect().w > 0 && def.GetRect().h > 0) {
+ // if form size is specified in def, and it is
+ // smaller than the current screen size,
+ // center the form on the display:
+
+ Rect r = def.GetRect();
+
+ if (r.w < screen->Width()) {
+ r.x = (screen->Width() - r.w) / 2;
+ }
+ else {
+ r.x = 0;
+ r.w = screen->Width();
+ }
+
+ if (r.h < screen->Height()) {
+ r.y = (screen->Height() - r.h) / 2;
+ }
+ else {
+ r.y = 0;
+ r.h = screen->Height();
+ }
+
+ MoveTo(r);
+ }
+
+ SetMargins(def.GetMargins());
+ SetTextInsets(def.GetTextInsets());
+ SetCellInsets(def.GetCellInsets());
+ SetCells(def.GetCells());
+ SetFixedWidth(def.GetFixedWidth());
+ SetFixedHeight(def.GetFixedHeight());
+
+ UseLayout(def.GetLayout().x_mins,
+ def.GetLayout().y_mins,
+ def.GetLayout().x_weights,
+ def.GetLayout().y_weights);
+
+ if (def.GetTexture().length() > 0) {
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath("Screens/");
+ loader->LoadTexture(def.GetTexture(), texture);
+ loader->SetDataPath("");
+ }
+
+ SetBackColor(def.GetBackColor());
+ SetForeColor(def.GetForeColor());
+
+ Font* f = FontMgr::Find(def.GetFont());
+ if (f) SetFont(f);
+
+ SetTransparent(def.GetTransparent());
+
+ ListIter<CtrlDef> ctrl = def.GetControls();
+ while (++ctrl) {
+ switch (ctrl->GetType()) {
+ case WIN_DEF_FORM:
+ case WIN_DEF_LABEL:
+ default:
+ CreateDefLabel(*ctrl);
+ break;
+
+ case WIN_DEF_BUTTON:
+ CreateDefButton(*ctrl);
+ break;
+
+ case WIN_DEF_COMBO:
+ CreateDefCombo(*ctrl);
+ break;
+
+ case WIN_DEF_IMAGE:
+ CreateDefImage(*ctrl);
+ break;
+
+ case WIN_DEF_EDIT:
+ CreateDefEdit(*ctrl);
+ break;
+
+ case WIN_DEF_LIST:
+ CreateDefList(*ctrl);
+ break;
+
+ case WIN_DEF_SLIDER:
+ CreateDefSlider(*ctrl);
+ break;
+
+ case WIN_DEF_RICH:
+ CreateDefRichText(*ctrl);
+ break;
+ }
+ }
+
+ RegisterControls();
+ DoLayout();
+ CalcGrid();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FormWindow::CreateDefLabel(CtrlDef& def)
+{
+ ActiveWindow* ctrl = CreateLabel(def.GetText(),
+ def.GetX(),
+ def.GetY(),
+ def.GetW(),
+ def.GetH(),
+ def.GetID(),
+ def.GetParentID(),
+ def.GetStyle());
+
+ ctrl->SetAltText(def.GetAltText());
+ ctrl->SetBackColor(def.GetBackColor());
+ ctrl->SetForeColor(def.GetForeColor());
+ ctrl->SetTextAlign(def.GetTextAlign());
+ ctrl->SetSingleLine(def.GetSingleLine());
+ ctrl->SetTransparent(def.GetTransparent());
+ ctrl->SetHidePartial(def.GetHidePartial());
+
+ ctrl->SetMargins(def.GetMargins());
+ ctrl->SetTextInsets(def.GetTextInsets());
+ ctrl->SetCellInsets(def.GetCellInsets());
+ ctrl->SetCells(def.GetCells());
+ ctrl->SetFixedWidth(def.GetFixedWidth());
+ ctrl->SetFixedHeight(def.GetFixedHeight());
+
+ ctrl->UseLayout(def.GetLayout().x_mins,
+ def.GetLayout().y_mins,
+ def.GetLayout().x_weights,
+ def.GetLayout().y_weights);
+
+ if (def.GetTexture().length() > 0) {
+ Bitmap* ctrl_tex = 0;
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath("Screens/");
+ loader->LoadTexture(def.GetTexture(), ctrl_tex);
+ loader->SetDataPath("");
+
+ ctrl->SetTexture(ctrl_tex);
+ }
+
+ Font* f = FontMgr::Find(def.GetFont());
+ if (f) ctrl->SetFont(f);
+}
+
+void
+FormWindow::CreateDefButton(CtrlDef& def)
+{
+ Button* ctrl = CreateButton(def.GetText(),
+ def.GetX(),
+ def.GetY(),
+ def.GetW(),
+ def.GetH(),
+ def.GetID(),
+ def.GetParentID());
+
+ if (def.GetStandardImage().length()) {
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath("Screens/");
+
+ Bitmap* bmp = 0;
+ loader->LoadTexture(def.GetStandardImage(), bmp);
+ ctrl->SetStandardImage(bmp);
+
+ if (def.GetActivatedImage().length()) {
+ loader->LoadTexture(def.GetActivatedImage(), bmp);
+ ctrl->SetActivatedImage(bmp);
+ }
+
+ if (def.GetTransitionImage().length()) {
+ loader->LoadTexture(def.GetTransitionImage(), bmp);
+ ctrl->SetTransitionImage(bmp);
+ }
+
+ loader->SetDataPath("");
+ }
+
+ ctrl->SetAltText(def.GetAltText());
+ ctrl->SetEnabled(def.IsEnabled());
+ ctrl->SetBackColor(def.GetBackColor());
+ ctrl->SetForeColor(def.GetForeColor());
+ ctrl->SetTextAlign(def.GetTextAlign());
+ ctrl->SetSingleLine(def.GetSingleLine());
+
+ ctrl->SetBevelWidth(def.GetBevelWidth());
+ ctrl->SetDropShadow(def.GetDropShadow());
+ ctrl->SetSticky(def.GetSticky());
+ ctrl->SetTransparent(def.GetTransparent());
+ ctrl->SetHidePartial(def.GetHidePartial());
+ ctrl->SetPictureLocation(def.GetPictureLocation());
+
+ ctrl->SetMargins(def.GetMargins());
+ ctrl->SetTextInsets(def.GetTextInsets());
+ ctrl->SetCellInsets(def.GetCellInsets());
+ ctrl->SetCells(def.GetCells());
+ ctrl->SetFixedWidth(def.GetFixedWidth());
+ ctrl->SetFixedHeight(def.GetFixedHeight());
+
+ if (def.GetPicture().length() > 0) {
+ Bitmap pict;
+ int type = def.GetPictureType();
+
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath("Screens/");
+ loader->LoadBitmap(def.GetPicture(), pict, type);
+ loader->SetDataPath("");
+
+ ctrl->SetPicture(pict);
+ }
+
+ Font* f = FontMgr::Find(def.GetFont());
+ if (f) ctrl->SetFont(f);
+}
+
+void
+FormWindow::CreateDefImage(CtrlDef& def)
+{
+ ImageBox* ctrl = CreateImageBox(def.GetText(),
+ def.GetX(),
+ def.GetY(),
+ def.GetW(),
+ def.GetH(),
+ def.GetID(),
+ def.GetParentID());
+
+ ctrl->SetAltText(def.GetAltText());
+ ctrl->SetBackColor(def.GetBackColor());
+ ctrl->SetForeColor(def.GetForeColor());
+ ctrl->SetStyle(def.GetStyle());
+ ctrl->SetTextAlign(def.GetTextAlign());
+ ctrl->SetSingleLine(def.GetSingleLine());
+ ctrl->SetTransparent(def.GetTransparent());
+ ctrl->SetHidePartial(def.GetHidePartial());
+
+ ctrl->SetMargins(def.GetMargins());
+ ctrl->SetTextInsets(def.GetTextInsets());
+ ctrl->SetCellInsets(def.GetCellInsets());
+ ctrl->SetCells(def.GetCells());
+ ctrl->SetFixedWidth(def.GetFixedWidth());
+ ctrl->SetFixedHeight(def.GetFixedHeight());
+
+ if (def.GetPicture().length() > 0) {
+ Bitmap picture;
+
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath("Screens/");
+ loader->LoadBitmap(def.GetPicture(), picture);
+ loader->SetDataPath("");
+
+ ctrl->SetPicture(picture);
+ }
+
+ Font* f = FontMgr::Find(def.GetFont());
+ if (f) ctrl->SetFont(f);
+}
+
+void
+FormWindow::CreateDefList(CtrlDef& def)
+{
+ ListBox* ctrl = CreateListBox(def.GetText(),
+ def.GetX(),
+ def.GetY(),
+ def.GetW(),
+ def.GetH(),
+ def.GetID(),
+ def.GetParentID());
+
+ ctrl->SetAltText(def.GetAltText());
+ ctrl->SetEnabled(def.IsEnabled());
+ ctrl->SetBackColor(def.GetBackColor());
+ ctrl->SetForeColor(def.GetForeColor());
+ ctrl->SetStyle(def.GetStyle());
+ ctrl->SetTextAlign(def.GetTextAlign());
+ ctrl->SetTransparent(def.GetTransparent());
+ ctrl->SetHidePartial(def.GetHidePartial());
+
+ ctrl->SetLineHeight(def.GetLineHeight());
+ ctrl->SetShowHeadings(def.GetShowHeadings());
+ ctrl->SetLeading(def.GetLeading());
+ ctrl->SetMultiSelect(def.GetMultiSelect());
+ ctrl->SetDragDrop(def.GetDragDrop());
+ ctrl->SetScrollBarVisible(def.GetScrollBarVisible());
+ ctrl->SetSmoothScroll(def.GetSmoothScroll());
+ ctrl->SetItemStyle(def.GetItemStyle());
+ ctrl->SetSelectedStyle(def.GetSelectedStyle());
+
+ ctrl->SetMargins(def.GetMargins());
+ ctrl->SetTextInsets(def.GetTextInsets());
+ ctrl->SetCellInsets(def.GetCellInsets());
+ ctrl->SetCells(def.GetCells());
+ ctrl->SetFixedWidth(def.GetFixedWidth());
+ ctrl->SetFixedHeight(def.GetFixedHeight());
+
+ if (def.GetTexture().length() > 0) {
+ Bitmap* ctrl_tex = 0;
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath("Screens/");
+ loader->LoadTexture(def.GetTexture(), ctrl_tex);
+ loader->SetDataPath("");
+
+ ctrl->SetTexture(ctrl_tex);
+ }
+
+ int ncols = def.NumColumns();
+ for (int i = 0; i < ncols; i++) {
+ ColumnDef* col = def.GetColumn(i);
+ ctrl->AddColumn(col->title, col->width, col->align, col->sort);
+
+ if (col->use_color)
+ ctrl->SetColumnColor(i, col->color);
+ }
+
+ int nitems = def.NumItems();
+ for (i = 0; i < nitems; i++)
+ ctrl->AddItem(def.GetItem(i));
+
+ Font* f = FontMgr::Find(def.GetFont());
+ if (f) ctrl->SetFont(f);
+}
+
+void
+FormWindow::CreateDefCombo(CtrlDef& def)
+{
+ ComboBox* ctrl = CreateComboBox(def.GetText(),
+ def.GetX(),
+ def.GetY(),
+ def.GetW(),
+ def.GetH(),
+ def.GetID(),
+ def.GetParentID());
+
+ ctrl->SetAltText(def.GetAltText());
+ ctrl->SetEnabled(def.IsEnabled());
+ ctrl->SetBackColor(def.GetBackColor());
+ ctrl->SetForeColor(def.GetForeColor());
+ ctrl->SetTextAlign(def.GetTextAlign());
+
+ ctrl->SetActiveColor(def.GetActiveColor());
+ ctrl->SetBorderColor(def.GetBorderColor());
+ ctrl->SetBorder(def.GetBorder());
+ ctrl->SetBevelWidth(def.GetBevelWidth());
+ ctrl->SetTransparent(def.GetTransparent());
+ ctrl->SetHidePartial(def.GetHidePartial());
+
+ ctrl->SetMargins(def.GetMargins());
+ ctrl->SetTextInsets(def.GetTextInsets());
+ ctrl->SetCellInsets(def.GetCellInsets());
+ ctrl->SetCells(def.GetCells());
+ ctrl->SetFixedWidth(def.GetFixedWidth());
+ ctrl->SetFixedHeight(def.GetFixedHeight());
+
+ int nitems = def.NumItems();
+ for (int i = 0; i < nitems; i++)
+ ctrl->AddItem(def.GetItem(i));
+
+ Font* f = FontMgr::Find(def.GetFont());
+ if (f) ctrl->SetFont(f);
+}
+
+void
+FormWindow::CreateDefEdit(CtrlDef& def)
+{
+ EditBox* ctrl = CreateEditBox(def.GetText(),
+ def.GetX(),
+ def.GetY(),
+ def.GetW(),
+ def.GetH(),
+ def.GetID(),
+ def.GetParentID());
+
+ ctrl->SetAltText(def.GetAltText());
+ ctrl->SetEnabled(def.IsEnabled());
+ ctrl->SetBackColor(def.GetBackColor());
+ ctrl->SetForeColor(def.GetForeColor());
+ ctrl->SetStyle(def.GetStyle());
+ ctrl->SetSingleLine(def.GetSingleLine());
+ ctrl->SetTextAlign(def.GetTextAlign());
+ ctrl->SetTransparent(def.GetTransparent());
+ ctrl->SetHidePartial(def.GetHidePartial());
+ ctrl->SetPasswordChar(def.GetPasswordChar());
+
+ ctrl->SetMargins(def.GetMargins());
+ ctrl->SetTextInsets(def.GetTextInsets());
+ ctrl->SetCellInsets(def.GetCellInsets());
+ ctrl->SetCells(def.GetCells());
+ ctrl->SetFixedWidth(def.GetFixedWidth());
+ ctrl->SetFixedHeight(def.GetFixedHeight());
+
+ ctrl->SetLineHeight(def.GetLineHeight());
+ ctrl->SetScrollBarVisible(def.GetScrollBarVisible());
+ ctrl->SetSmoothScroll(def.GetSmoothScroll());
+
+ if (def.GetTexture().length() > 0) {
+ Bitmap* ctrl_tex = 0;
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath("Screens/");
+ loader->LoadTexture(def.GetTexture(), ctrl_tex);
+ loader->SetDataPath("");
+
+ ctrl->SetTexture(ctrl_tex);
+ }
+
+ Font* f = FontMgr::Find(def.GetFont());
+ if (f) ctrl->SetFont(f);
+}
+
+void
+FormWindow::CreateDefSlider(CtrlDef& def)
+{
+ Slider* ctrl = CreateSlider(def.GetText(),
+ def.GetX(),
+ def.GetY(),
+ def.GetW(),
+ def.GetH(),
+ def.GetID(),
+ def.GetParentID());
+
+
+ ctrl->SetAltText(def.GetAltText());
+ ctrl->SetEnabled(def.IsEnabled());
+ ctrl->SetBackColor(def.GetBackColor());
+ ctrl->SetForeColor(def.GetForeColor());
+
+ ctrl->SetActive(def.GetActive());
+ ctrl->SetOrientation(def.GetOrientation());
+ ctrl->SetFillColor(def.GetActiveColor());
+ ctrl->SetBorderColor(def.GetBorderColor());
+ ctrl->SetBorder(def.GetBorder());
+ ctrl->SetStyle(def.GetStyle());
+ ctrl->SetTransparent(def.GetTransparent());
+ ctrl->SetHidePartial(def.GetHidePartial());
+ ctrl->SetNumLeds(def.GetNumLeds());
+
+ ctrl->SetMargins(def.GetMargins());
+ ctrl->SetTextInsets(def.GetTextInsets());
+ ctrl->SetCellInsets(def.GetCellInsets());
+ ctrl->SetCells(def.GetCells());
+ ctrl->SetFixedWidth(def.GetFixedWidth());
+ ctrl->SetFixedHeight(def.GetFixedHeight());
+
+ Font* f = FontMgr::Find(def.GetFont());
+ if (f) ctrl->SetFont(f);
+}
+
+void
+FormWindow::CreateDefRichText(CtrlDef& def)
+{
+ RichTextBox* ctrl = CreateRichTextBox(def.GetText(),
+ def.GetX(),
+ def.GetY(),
+ def.GetW(),
+ def.GetH(),
+ def.GetID(),
+ def.GetParentID(),
+ def.GetStyle());
+
+ ctrl->SetAltText(def.GetAltText());
+ ctrl->SetBackColor(def.GetBackColor());
+ ctrl->SetForeColor(def.GetForeColor());
+ ctrl->SetLineHeight(def.GetLineHeight());
+ ctrl->SetLeading(def.GetLeading());
+ ctrl->SetScrollBarVisible(def.GetScrollBarVisible());
+ ctrl->SetSmoothScroll(def.GetSmoothScroll());
+ ctrl->SetTextAlign(def.GetTextAlign());
+ ctrl->SetTransparent(def.GetTransparent());
+ ctrl->SetHidePartial(def.GetHidePartial());
+
+ ctrl->SetMargins(def.GetMargins());
+ ctrl->SetTextInsets(def.GetTextInsets());
+ ctrl->SetCellInsets(def.GetCellInsets());
+ ctrl->SetCells(def.GetCells());
+ ctrl->SetFixedWidth(def.GetFixedWidth());
+ ctrl->SetFixedHeight(def.GetFixedHeight());
+
+ if (def.GetTexture().length() > 0) {
+ Bitmap* ctrl_tex = 0;
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath("Screens/");
+ loader->LoadTexture(def.GetTexture(), ctrl_tex);
+ loader->SetDataPath("");
+
+ ctrl->SetTexture(ctrl_tex);
+ }
+
+ Font* f = FontMgr::Find(def.GetFont());
+ if (f) ctrl->SetFont(f);
+
+ for (int i = 0; i < def.NumTabs(); i++)
+ ctrl->SetTabStop(i, def.GetTab(i));
+}
+
+// +--------------------------------------------------------------------+
+
+void
+FormWindow::AdoptFormDef(const FormDef& def)
+{
+ Destroy();
+ Init(def);
+}
+
+// +--------------------------------------------------------------------+
+
+ActiveWindow*
+FormWindow::FindControl(DWORD id)
+{
+ return FindChild(id);
+}
+
+
+// +--------------------------------------------------------------------+
+
+ActiveWindow*
+FormWindow::FindControl(int x, int y)
+{
+ ActiveWindow* mouse_tgt = 0;
+
+ ListIter<ActiveWindow> iter = children;
+ while (++iter) {
+ ActiveWindow* test = iter.value();
+ if (test->TargetRect().Contains(x,y)) {
+ mouse_tgt = test;
+
+ while (test) {
+ test = test->FindChild(x,y);
+
+ if (test)
+ mouse_tgt = test;
+ }
+ }
+ }
+
+ return mouse_tgt;
+}
+
+
+
diff --git a/nGenEx/FormWindow.h b/nGenEx/FormWindow.h
new file mode 100644
index 0000000..b94dded
--- /dev/null
+++ b/nGenEx/FormWindow.h
@@ -0,0 +1,77 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: FormWindow.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Form Window class (a window that manages controls)
+*/
+
+#ifndef FormWindow_h
+#define FormWindow_h
+
+#include "Types.h"
+#include "ActiveWindow.h"
+#include "FormDef.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class Button;
+class ComboBox;
+class EditBox;
+class ImageBox;
+class ListBox;
+class RichTextBox;
+class Slider;
+
+// +--------------------------------------------------------------------+
+
+class FormWindow : public ActiveWindow
+{
+public:
+ FormWindow(Screen* s, int ax, int ay, int aw, int ah,
+ DWORD aid=0, DWORD style=0, ActiveWindow* parent=0);
+ virtual ~FormWindow();
+
+ // operations:
+ virtual void Init();
+ virtual void Init(const FormDef& def);
+ virtual void Destroy();
+ virtual ActiveWindow* FindControl(DWORD id);
+ virtual ActiveWindow* FindControl(int x, int y);
+ virtual void RegisterControls() { }
+
+ virtual void AdoptFormDef(const FormDef& def);
+ virtual void AddControl(ActiveWindow* ctrl);
+
+ virtual ActiveWindow* CreateLabel( const char* text, int x, int y, int w, int h, DWORD id=0, DWORD pid=0, DWORD style=0);
+ virtual Button* CreateButton( const char* text, int x, int y, int w, int h, DWORD id=0, DWORD pid=0);
+ virtual ImageBox* CreateImageBox( const char* text, int x, int y, int w, int h, DWORD id=0, DWORD pid=0);
+ virtual ListBox* CreateListBox( const char* text, int x, int y, int w, int h, DWORD id=0, DWORD pid=0);
+ virtual ComboBox* CreateComboBox( const char* text, int x, int y, int w, int h, DWORD id=0, DWORD pid=0);
+ virtual EditBox* CreateEditBox( const char* text, int x, int y, int w, int h, DWORD id=0, DWORD pid=0);
+ virtual RichTextBox* CreateRichTextBox(const char* text, int x, int y, int w, int h, DWORD id=0, DWORD pid=0, DWORD style=0);
+ virtual Slider* CreateSlider( const char* text, int x, int y, int w, int h, DWORD id=0, DWORD pid=0, DWORD style=0);
+
+ // property accessors:
+ ListIter<ActiveWindow> Controls() { return children; }
+
+protected:
+ virtual void CreateDefLabel(CtrlDef& def);
+ virtual void CreateDefButton(CtrlDef& def);
+ virtual void CreateDefImage(CtrlDef& def);
+ virtual void CreateDefList(CtrlDef& def);
+ virtual void CreateDefCombo(CtrlDef& def);
+ virtual void CreateDefEdit(CtrlDef& def);
+ virtual void CreateDefSlider(CtrlDef& def);
+ virtual void CreateDefRichText(CtrlDef& def);
+};
+
+#endif FormWindow_h
+
diff --git a/nGenEx/FormatUtil.cpp b/nGenEx/FormatUtil.cpp
new file mode 100644
index 0000000..3e86f07
--- /dev/null
+++ b/nGenEx/FormatUtil.cpp
@@ -0,0 +1,337 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: FormatUtil.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+*/
+
+#include "MemDebug.h"
+#include "FormatUtil.h"
+
+// +--------------------------------------------------------------------+
+
+void FormatNumber(char* txt, double n)
+{
+ double a = fabs(n);
+
+ if (a < 1e3)
+ sprintf(txt, "%d", (int) (n));
+
+ else if (a < 1e6)
+ sprintf(txt, "%.1f K", (n/1e3));
+
+ else if (a < 1e9)
+ sprintf(txt, "%.1f M", (n/1e6));
+
+ else if (a < 1e12)
+ sprintf(txt, "%.1f G", (n/1e9));
+
+ else if (a < 1e15)
+ sprintf(txt, "%.1f T", (n/1e12));
+
+ else
+ sprintf(txt, "%.1e", n);
+}
+
+// +--------------------------------------------------------------------+
+
+void FormatNumberExp(char* txt, double n)
+{
+ double a = fabs(n);
+
+ if (a < 100e3)
+ sprintf(txt, "%d", (int) (n));
+
+ else
+ sprintf(txt, "%.1e", n);
+}
+
+// +--------------------------------------------------------------------+
+
+const int MINUTE = 60;
+const int HOUR = 60 * MINUTE;
+const int DAY = 24 * HOUR;
+
+void FormatTime(char* txt, double time)
+{
+ int t = (int) time;
+
+ int h = (t / HOUR);
+ int m = ((t - h*HOUR) / MINUTE);
+ int s = ((t - h*HOUR - m*MINUTE));
+
+ if (h > 0)
+ sprintf(txt, "%02d:%02d:%02d", h,m,s);
+ else
+ sprintf(txt, "%02d:%02d", m,s);
+}
+
+// +--------------------------------------------------------------------+
+
+void FormatTimeOfDay(char* txt, double time)
+{
+ int t = (int) time;
+
+ if (t >= DAY) {
+ int d = t / DAY;
+ t -= d * DAY;
+ }
+
+ int h = (t / HOUR);
+ int m = ((t - h*HOUR) / MINUTE);
+ int s = ((t - h*HOUR - m*MINUTE));
+
+ sprintf(txt, "%02d:%02d:%02d", h,m,s);
+}
+
+// +--------------------------------------------------------------------+
+
+void FormatDayTime(char* txt, double time, bool short_format)
+{
+ int t = (int) time;
+ int d = 1, h = 0, m = 0, s = 0;
+
+ if (t >= DAY) {
+ d = t / DAY;
+ t -= d * DAY;
+ d++;
+ }
+
+ if (t >= HOUR) {
+ h = t / HOUR;
+ t -= h * HOUR;
+ }
+
+ if (t >= MINUTE) {
+ m = t / MINUTE;
+ t -= m * MINUTE;
+ }
+
+ s = t;
+
+ if (short_format)
+ sprintf(txt, "%02d/%02d:%02d:%02d", d, h, m, s);
+ else
+ sprintf(txt, "Day %02d %02d:%02d:%02d", d, h, m, s);
+}
+
+// +--------------------------------------------------------------------+
+
+void FormatDay(char* txt, double time)
+{
+ int t = (int) time;
+ int d = 1, h = 0, m = 0, s = 0;
+
+ if (t >= DAY) {
+ d = t / DAY;
+ t -= d * DAY;
+ d++;
+ }
+
+ sprintf(txt, "Day %02d", d);
+}
+
+// +--------------------------------------------------------------------+
+
+void FormatPoint(char* txt, const Point& p)
+{
+ char x[16];
+ char y[16];
+ char z[16];
+
+ FormatNumber(x, p.x);
+ FormatNumber(y, p.y);
+ FormatNumber(z, p.z);
+
+ sprintf(txt, "(%s, %s, %s)", x, y, z);
+}
+
+// +--------------------------------------------------------------------+
+
+Text FormatTimeString(int utc)
+{
+ static const char* month[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+ static const char* meridian[2] = { "AM", "PM" };
+
+ if (utc < 1)
+ utc = (int) time(0);
+
+ time_t aclock = utc; // Get time in seconds
+ struct tm *t = localtime(&aclock); // Convert time to struct tm form
+
+ char buffer[256];
+ sprintf(buffer, "%d %s %d, %2d:%02d:%02d %s",
+ t->tm_mday, month[t->tm_mon], 1900 + t->tm_year,
+ t->tm_hour > 12 ? t->tm_hour-12 : t->tm_hour,
+ t->tm_min, t->tm_sec, meridian[t->tm_hour > 12]);
+
+ return buffer;
+}
+
+// +--------------------------------------------------------------------+
+
+static char safe_str[2048];
+
+const char* SafeString(const char* s)
+{
+ ZeroMemory(safe_str, sizeof(safe_str));
+
+ if (s && *s) {
+ int len = strlen(s);
+ int n = 0;
+
+ for (int i = 0; i < len; i++) {
+ char c = s[i];
+
+ if (c == '\n') {
+ safe_str[n++] = '\\';
+ safe_str[n++] = 'n';
+ }
+
+ else if (c == '\t') {
+ safe_str[n++] = '\\';
+ safe_str[n++] = 't';
+ }
+
+ else if (c == '"') {
+ safe_str[n++] = '\'';
+ }
+
+ else if (c == '\\' && i < len-1) {
+ safe_str[n++] = s[i++];
+ safe_str[n++] = s[i++];
+ }
+
+ else if (c < 32 || c > 126) {
+ // non printing characters
+ }
+
+ else {
+ safe_str[n++] = c;
+ }
+
+ if (n > 2040)
+ break;
+ }
+ }
+
+ return safe_str;
+}
+
+// +--------------------------------------------------------------------+
+
+const char* SafeQuotes(const char* msg)
+{
+ int dst = 0;
+
+ if (msg) {
+ while (*msg && dst < 254) {
+ if (*msg == '"') {
+ safe_str[dst++] = '\'';
+ msg++;
+ }
+ else if (isspace(*msg)) {
+ safe_str[dst++] = ' ';
+ msg++;
+ }
+ else {
+ safe_str[dst++] = *msg++;
+ }
+ }
+ }
+
+ safe_str[dst] = 0;
+ return safe_str;
+}
+
+// +--------------------------------------------------------------------+
+
+Text FormatTextReplace(const char* msg, const char* tgt, const char* val)
+{
+ if (!msg || !tgt || !val)
+ return "";
+
+ if (!strchr(msg, *tgt))
+ return msg;
+
+ Text result;
+ char* buffer = new char[strlen(msg) + 1];
+ const char* p = msg;
+ char* q = buffer;
+ int tgtlen = strlen(tgt);
+
+ while (*p) {
+ if (!strncmp(p, tgt, tgtlen)) {
+ p += tgtlen;
+ *q = 0;
+ q = buffer;
+
+ result += buffer;
+ result += val;
+ }
+
+ else {
+ *q++ = *p++;
+ }
+ }
+
+ if (q != buffer) {
+ *q = 0;
+ result += buffer;
+ }
+
+ delete [] buffer;
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+Text FormatTextEscape(const char* msg)
+{
+ if (!msg)
+ return "";
+
+ if (!strchr(msg, '\\'))
+ return msg;
+
+ Text result;
+ char* buffer = new char[strlen(msg) + 1];
+ const char* p = msg;
+ char* q = buffer;
+
+ while (*p) {
+ if (*p == '\\') {
+ p++;
+
+ if (*p == 'n') {
+ *q++ = '\n';
+ p++;
+ }
+
+ else if (*p == 't') {
+ *q++ = '\t';
+ p++;
+ }
+
+ else {
+ *q++ = *p++;
+ }
+ }
+
+ else {
+ *q++ = *p++;
+ }
+ }
+
+ *q = 0;
+ result = buffer;
+ delete [] buffer;
+ return result;
+}
diff --git a/nGenEx/FormatUtil.h b/nGenEx/FormatUtil.h
new file mode 100644
index 0000000..5119543
--- /dev/null
+++ b/nGenEx/FormatUtil.h
@@ -0,0 +1,47 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: FormatUtil.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Text formatting utilities
+*/
+
+#ifndef FormatUtil_h
+#define FormatUtil_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+void FormatNumber(char* txt, double n);
+void FormatNumberExp(char* txt, double n);
+void FormatTime(char* txt, double seconds);
+void FormatTimeOfDay(char* txt, double seconds);
+void FormatDayTime(char* txt, double seconds, bool short_format=false);
+void FormatDay(char* txt, double seconds);
+void FormatPoint(char* txt, const Point& p);
+Text FormatTimeString(int utc=0);
+
+const char* SafeString(const char* s);
+const char* SafeQuotes(const char* s);
+
+// scan msg and replace all occurrences of tgt with val
+// return new result, leave msg unmodified
+Text FormatTextReplace(const char* msg, const char* tgt, const char* val);
+
+// scan msg and replace all C-style \x escape sequences
+// with their single-character values, leave orig unmodified
+Text FormatTextEscape(const char* msg);
+
+// +--------------------------------------------------------------------+
+
+#endif FormatUtil_h
+
diff --git a/nGenEx/Game.cpp b/nGenEx/Game.cpp
new file mode 100644
index 0000000..f297955
--- /dev/null
+++ b/nGenEx/Game.cpp
@@ -0,0 +1,1574 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Game.cpp
+ AUTHOR: John DiCamillo
+
+*/
+
+#include "MemDebug.h"
+#include "Game.h"
+#include "Mouse.h"
+#include "Universe.h"
+#include "Screen.h"
+#include "Window.h"
+#include "EventDispatch.h"
+#include "Color.h"
+#include "DataLoader.h"
+#include "Keyboard.h"
+#include "Pcx.h"
+#include "Resource.h"
+#include "Bitmap.h"
+#include "MachineInfo.h"
+#include "Video.h"
+#include "VideoFactory.h"
+#include "VideoSettings.h"
+#include "AviFile.h"
+#include "ContentBundle.h"
+
+// +--------------------------------------------------------------------+
+
+FILE* ErrLog = 0;
+int ErrLine = 0;
+char ErrBuf[256];
+
+Game* game = 0;
+
+bool Game::active = false;
+bool Game::paused = false;
+bool Game::server = false;
+bool Game::show_mouse = false;
+DWORD Game::base_game_time = 0;
+DWORD Game::real_time = 0;
+DWORD Game::game_time = 0;
+DWORD Game::time_comp = 1;
+DWORD Game::frame_number = 0;
+
+const int VIDEO_FPS = 30;
+const double MAX_FRAME_TIME_VIDEO = 1.0 / (double) VIDEO_FPS;
+const double MAX_FRAME_TIME_NORMAL = 1.0 / 5.0;
+const double MIN_FRAME_TIME_NORMAL = 1.0 / 60.0;
+
+double Game::max_frame_length = MAX_FRAME_TIME_NORMAL;
+double Game::min_frame_length = MIN_FRAME_TIME_NORMAL;
+
+char Game::panicbuf[256];
+
+static LARGE_INTEGER perf_freq;
+static LARGE_INTEGER perf_cnt1;
+static LARGE_INTEGER perf_cnt2;
+
+// +--------------------------------------------------------------------+
+
+Game::Game()
+ : world(0), video_factory(0), video(0), video_settings(0), soundcard(0),
+ gamma(128), max_tex_size(2048), screen(0), totaltime(0),
+ hInst(0), hwnd(0), frame_rate(0), frame_count(0), frame_count0(0),
+ frame_time(0), frame_time0(0), gui_seconds(0), content(0),
+ status(Game::OK), exit_code(0), window_style(0), avi_file(0)
+{
+ if (!game) {
+ panicbuf[0] = 0;
+ game = this;
+ ZeroMemory(ErrBuf, 256);
+
+ video_settings = new(__FILE__,__LINE__) VideoSettings;
+
+ is_windowed = false;
+ is_active = false;
+ is_device_lost = false;
+ is_minimized = false;
+ is_maximized = false;
+ ignore_size_change = false;
+ is_device_initialized = false;
+ is_device_restored = false;
+ }
+ else
+ status = TOO_MANY;
+}
+
+Game::~Game()
+{
+ if (game == this)
+ game = 0;
+
+ delete content;
+ delete world;
+ delete screen;
+ delete video_factory;
+ delete video;
+ delete soundcard;
+ delete video_settings;
+ delete avi_file;
+
+ if (status == EXIT)
+ ShowStats();
+}
+
+// +--------------------------------------------------------------------+
+
+HINSTANCE Game::GetHINST()
+{
+ if (game)
+ return game->hInst;
+
+ return 0;
+}
+
+HWND Game::GetHWND()
+{
+ if (game)
+ return game->hwnd;
+
+ return 0;
+}
+
+bool Game::IsWindowed()
+{
+ if (game)
+ return game->is_windowed;
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+Text
+Game::GetText(const char* key)
+{
+ if (game && game->content && game->content->IsLoaded())
+ return game->content->GetText(key);
+
+ return key;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Game::GammaLevel()
+{
+ if (game)
+ return game->gamma;
+
+ return 0;
+}
+
+void
+Game::SetGammaLevel(int g)
+{
+ if (game) {
+ game->gamma = g;
+
+ if (game->video)
+ game->video->SetGammaLevel(g);
+ }
+}
+
+int
+Game::MaxTexSize()
+{
+ if (game && game->video) {
+ int max_vid_size = game->video->MaxTexSize();
+ return max_vid_size < game->max_tex_size ?
+ max_vid_size : game->max_tex_size;
+ }
+ else if (Video::GetInstance()) {
+ return Video::GetInstance()->MaxTexSize();
+ }
+
+ return 256;
+}
+
+int
+Game::MaxTexAspect()
+{
+ if (game && game->video) {
+ return game->video->MaxTexAspect();
+ }
+ else if (Video::GetInstance()) {
+ return Video::GetInstance()->MaxTexAspect();
+ }
+
+ return 1;
+}
+
+void
+Game::SetMaxTexSize(int n)
+{
+ if (game && n >= 64 && n <= 4096)
+ game->max_tex_size = n;
+}
+
+bool
+Game::DisplayModeSupported(int w, int h, int bpp)
+{
+ return game && game->video && game->video->IsModeSupported(w,h,bpp);
+}
+
+double
+Game::FrameRate()
+{
+ if (game)
+ return game->frame_rate;
+
+ return 0;
+}
+
+double
+Game::FrameTime()
+{
+ if (game)
+ return game->seconds;
+
+ return 0;
+}
+
+double
+Game::GUITime()
+{
+ if (game)
+ return game->gui_seconds;
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Game::Init(HINSTANCE hi, HINSTANCE hpi, LPSTR cmdline, int nCmdShow)
+{
+ status = OK;
+ hInst = hi;
+
+ Print(" Initializing Game\n");
+
+ stats.Clear();
+
+ if (!InitApplication(hInst)) { // Initialize shared things
+ Panic("Could not initialize the application.");
+ status = INIT_FAILED;
+ }
+
+ if (status == OK && !video_settings) {
+ Panic("No video settings specified");
+ status = INIT_FAILED;
+ }
+
+ if (status == OK) {
+ if (MachineInfo::GetPlatform() == MachineInfo::OS_WINNT) {
+ Panic(" D3D not available under WinNT 4");
+ status = INIT_FAILED;
+ }
+
+ else if (MachineInfo::GetDirectXVersion() < MachineInfo::DX_9) {
+ Panic(" Insufficient DirectX detected (Dx9 IS REQUIRED)");
+ status = INIT_FAILED;
+ }
+
+ Print(" Gamma Level = %d\n", gamma);
+ }
+
+ if (status == OK) {
+ Print("\n Initializing instance...\n");
+ // Perform initializations that apply to a specific instance
+ if (!InitInstance(hInst, nCmdShow)) {
+ Panic("Could not initialize the instance.");
+ status = INIT_FAILED;
+ }
+ }
+
+ if (status == OK) {
+ Print(" Initializing content...\n");
+ InitContent();
+ }
+
+ if (status == OK) {
+ Print(" Initializing game...\n");
+ if (!InitGame()) {
+ if (!panicbuf[0])
+ Panic("Could not initialize the game.");
+ status = INIT_FAILED;
+ }
+ }
+
+ return status == OK;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Game::InitApplication(HINSTANCE hInstance)
+{
+ WNDCLASS wc;
+ LOGBRUSH brush = { BS_SOLID, RGB(0,0,0), 0 };
+
+ if (server)
+ brush.lbColor = RGB(255,255,255);
+
+ // Fill in window class structure with parameters that
+ // describe the main window.
+ wc.style = CS_HREDRAW | CS_VREDRAW;
+ wc.lpfnWndProc = (WNDPROC) WndProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = hInstance;
+ wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(100));
+ wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+
+ wc.hbrBackground = CreateBrushIndirect(&brush);
+ wc.lpszMenuName = app_name;
+ wc.lpszClassName = app_name;
+
+ // Register the window class and return success/failure code.
+ if (RegisterClass(&wc) == 0) {
+ DWORD err = GetLastError();
+
+ if (err == 1410) // class already exists, this is OK
+ return true;
+
+ else
+ Print("WARNING: Register Window Class: %08x\n", err);
+ }
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Game::InitInstance(HINSTANCE hInstance, int nCmdShow)
+{
+ hInst = hInstance;
+
+ // initialize the game timer:
+ base_game_time = 0;
+ QueryPerformanceFrequency(&perf_freq);
+ QueryPerformanceCounter(&perf_cnt1);
+
+ // center window on display:
+ int screenx = GetSystemMetrics(SM_CXSCREEN);
+ int screeny = GetSystemMetrics(SM_CYSCREEN);
+ int x_offset = 0;
+ int y_offset = 0;
+ int s_width = 800;
+ int s_height = 600;
+
+ if (server) {
+ s_width = 320;
+ s_height = 200;
+ }
+
+ else if (video_settings) {
+ s_width = video_settings->window_width;
+ s_height = video_settings->window_height;
+ }
+
+ if (s_width < screenx)
+ x_offset = (screenx - s_width) / 2;
+
+ if (s_height < screeny)
+ y_offset = (screeny - s_height) / 2;
+
+ // Create a main window for this application instance
+ RECT rctmp;
+ rctmp.left = x_offset;
+ rctmp.top = y_offset;
+ rctmp.right = x_offset + s_width;
+ rctmp.bottom = y_offset + s_height;
+
+ window_style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME |
+ WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_VISIBLE;
+
+ AdjustWindowRect(&rctmp, window_style, 1);
+
+ hwnd = CreateWindow(
+ app_name, // Class name
+ app_name, // Caption
+
+ window_style,
+
+ x_offset, // Position
+ y_offset,
+
+ rctmp.right - rctmp.left, // Size
+ rctmp.bottom - rctmp.top,
+
+ 0, // Parent window (no parent)
+ 0, // use class menu
+ hInst, // handle to window instance
+ 0); // no params to pass on
+
+ // If window could not be created, return "failure"
+ if (!hwnd) {
+ Panic("Could not create window\n");
+ return false;
+ }
+
+ Print(" Window created.\n");
+
+ // Make the window visible and draw it
+ ShowWindow(hwnd, nCmdShow); // Show the window
+ UpdateWindow(hwnd); // Sends WM_PAINT message
+
+ // Save window properties
+ window_style = GetWindowLong(hwnd, GWL_STYLE);
+ GetWindowRect(hwnd, &bounds_rect);
+ GetClientRect(hwnd, &client_rect);
+
+ // Use client area to set video window size
+ video_settings->window_width = client_rect.right - client_rect.left;
+ video_settings->window_height = client_rect.bottom - client_rect.top;
+
+ Print(" Instance initialized.\n");
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Game::InitVideo()
+{
+ if (server) return true;
+
+ // create a video factory, and video object:
+ video_factory = new(__FILE__,__LINE__) VideoFactory(hwnd);
+
+ if (video_factory) {
+ Print(" Init Video...\n");
+ Print(" Request %s mode\n", video_settings->GetModeDescription());
+
+ video = video_factory->CreateVideo(video_settings);
+
+ if (video) {
+ if (!video->IsHardware()) {
+ video_factory->DestroyVideo(video);
+ video = 0;
+
+ Panic("3D Hardware Not Found");
+ }
+
+ // save a copy of the device-specific video settings:
+ else if (video->GetVideoSettings()) {
+ *video_settings = *video->GetVideoSettings();
+ is_windowed = video_settings->IsWindowed();
+ }
+ }
+
+ soundcard = video_factory->CreateSoundCard();
+ }
+
+ return (video && video->Status() == Video::VIDEO_OK);
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Game::ResetVideo()
+{
+ if (server) return true;
+ if (!video_factory) return InitVideo();
+
+ Print(" Reset Video...\n");
+ Print(" Request %s mode\n", video_settings->GetModeDescription());
+
+ delete screen;
+
+ if (video && !video->Reset(video_settings)) {
+ video_factory->DestroyVideo(video);
+ video = video_factory->CreateVideo(video_settings);
+ }
+
+ if (!video || video->Status() != Video::VIDEO_OK) {
+ Panic("Could not re-create Video Interface.");
+ return false;
+ }
+
+ Print(" Re-created video object.\n");
+
+ // save a copy of the device-specific video settings:
+ if (video->GetVideoSettings()) {
+ *video_settings = *video->GetVideoSettings();
+ is_windowed = video_settings->IsWindowed();
+ }
+
+ Color::UseVideo(video);
+
+ screen = new(__FILE__,__LINE__) Screen(video);
+ if (!screen) {
+ Panic("Could not re-create Screen object.");
+ return false;
+ }
+
+ Print(" Re-created screen object.\n");
+
+ if (!screen->SetBackgroundColor(Color::Black))
+ Print(" WARNING: could not set video background color to Black\n");
+
+ screen->ClearAllFrames(true);
+ video->SetGammaLevel(gamma);
+
+ Print(" Re-established requested video parameters.\n");
+
+ Bitmap::CacheUpdate();
+ Print(" Refreshed texture bitmaps.\n\n");
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Game::ResizeVideo()
+{
+ if (!video || !video_settings) return false;
+ if (!is_windowed) return false;
+ if (ignore_size_change) return true;
+
+ HRESULT hr = S_OK;
+ RECT client_old;
+
+ client_old = client_rect;
+
+ // Update window properties
+ GetWindowRect(hwnd, &bounds_rect);
+ GetClientRect(hwnd, &client_rect);
+
+ if (client_old.right - client_old.left !=
+ client_rect.right - client_rect.left ||
+ client_old.bottom - client_old.top !=
+ client_rect.bottom - client_rect.top) {
+
+ // A new window size will require a new backbuffer
+ // size, so the 3D structures must be changed accordingly.
+ Pause(true);
+
+ video_settings->is_windowed = true;
+ video_settings->window_width = client_rect.right - client_rect.left;
+ video_settings->window_height = client_rect.bottom - client_rect.top;
+
+ ::Print("ResizeVideo() %d x %d\n", video_settings->window_width, video_settings->window_height);
+
+ if (video) {
+ video->Reset(video_settings);
+ }
+
+ Pause(false);
+ }
+
+ // save a copy of the device-specific video settings:
+ if (video->GetVideoSettings()) {
+ *video_settings = *video->GetVideoSettings();
+ is_windowed = video_settings->IsWindowed();
+ }
+
+ screen->Resize(video_settings->window_width, video_settings->window_height);
+
+ return hr == S_OK;
+}
+
+bool
+Game::ToggleFullscreen()
+{
+ bool result = false;
+
+ if (video && video_settings) {
+ Pause(true);
+ ignore_size_change = true;
+
+ // Toggle the windowed state
+ is_windowed = !is_windowed;
+ video_settings->is_windowed = is_windowed;
+
+ // Prepare window for windowed/fullscreen change
+ AdjustWindowForChange();
+
+ // Reset the 3D device
+ if (!video->Reset(video_settings)) {
+ // reset failed, try to restore...
+ ignore_size_change = false;
+
+ if (!is_windowed) {
+ // Restore window type to windowed mode
+ is_windowed = !is_windowed;
+ video_settings->is_windowed = is_windowed;
+
+ AdjustWindowForChange();
+
+ SetWindowPos(hwnd,
+ HWND_NOTOPMOST,
+ bounds_rect.left,
+ bounds_rect.top,
+ bounds_rect.right - bounds_rect.left,
+ bounds_rect.bottom - bounds_rect.top,
+ SWP_SHOWWINDOW);
+ }
+
+ ::Print("Unable to toggle %s fullscreen mode.\n", is_windowed ? "into" : "out of");
+ }
+
+ else {
+ ignore_size_change = false;
+
+ // When moving from fullscreen to windowed mode, it is important to
+ // adjust the window size after resetting the device rather than
+ // beforehand to ensure that you get the window size you want. For
+ // example, when switching from 640x480 fullscreen to windowed with
+ // a 1000x600 window on a 1024x768 desktop, it is impossible to set
+ // the window size to 1000x600 until after the display mode has
+ // changed to 1024x768, because windows cannot be larger than the
+ // desktop.
+
+ if (is_windowed) {
+ SetWindowPos(hwnd,
+ HWND_NOTOPMOST,
+ bounds_rect.left,
+ bounds_rect.top,
+ bounds_rect.right - bounds_rect.left,
+ bounds_rect.bottom - bounds_rect.top,
+ SWP_SHOWWINDOW);
+ }
+
+ GetClientRect(hwnd, &client_rect); // Update our copy
+ Pause(false);
+
+ if (is_windowed)
+ screen->Resize(video_settings->window_width,
+ video_settings->window_height);
+
+ else
+ screen->Resize(video_settings->fullscreen_mode.width,
+ video_settings->fullscreen_mode.height);
+
+ result = true;
+ }
+ }
+
+ return result;
+}
+
+bool
+Game::AdjustWindowForChange()
+{
+ if (is_windowed) {
+ // Set windowed-mode style
+ SetWindowLong(hwnd, GWL_STYLE, window_style);
+ if (hmenu != NULL) {
+ SetMenu(hwnd, hmenu);
+ hmenu = NULL;
+ }
+ }
+ else {
+ // Set fullscreen-mode style
+ SetWindowLong(hwnd, GWL_STYLE, WS_POPUP|WS_SYSMENU|WS_VISIBLE);
+ if (hmenu == NULL) {
+ hmenu = GetMenu(hwnd);
+ SetMenu(hwnd, NULL);
+ }
+ }
+
+ return true;
+}
+
+
+// +--------------------------------------------------------------------+
+
+bool
+Game::InitGame()
+{
+ if (server) {
+ Print(" InitGame() - server mode.\n");
+ }
+
+ else {
+ if (!SetupPalette()) {
+ Panic("Could not set up the palette.");
+ return false;
+ }
+
+ Print(" Palette loaded.\n");
+
+ if (!InitVideo() || !video || video->Status() != Video::VIDEO_OK) {
+ if (!panicbuf[0])
+ Panic("Could not create the Video Interface.");
+ return false;
+ }
+
+ Print(" Created video object.\n");
+
+ Color::UseVideo(video);
+
+ screen = new(__FILE__,__LINE__) Screen(video);
+ if (!screen) {
+ if (!panicbuf[0])
+ Panic("Could not create the Screen object.");
+ return false;
+ }
+
+ Print(" Created screen object.\n");
+
+ if (!screen->SetBackgroundColor(Color::Black))
+ Print(" WARNING: could not set video background color to Black\n");
+ screen->ClearAllFrames(true);
+
+ video->SetGammaLevel(gamma);
+
+ Print(" Established requested video parameters.\n\n");
+ }
+
+ return true;
+}
+
+
+// +--------------------------------------------------------------------+
+
+bool
+Game::InitContent()
+{
+ DataLoader* loader = DataLoader::GetLoader();
+ List<Text> bundles;
+
+ loader->SetDataPath("Content/");
+ loader->ListFiles("content*", bundles);
+
+ ListIter<Text> iter = bundles;
+ while (++iter) {
+ Text* filename = iter.value();
+ int n = filename->indexOf('_');
+
+ if (n > 0) {
+ Locale::ParseLocale(filename->data() + n);
+ }
+ else {
+ delete content;
+ content = new(__FILE__,__LINE__) ContentBundle("content", 0);
+ }
+ }
+
+ loader->SetDataPath(0);
+ return true;
+}
+
+void
+Game::UseLocale(Locale* locale)
+{
+ if (game) {
+ DataLoader* loader = DataLoader::GetLoader();
+ loader->SetDataPath("Content/");
+ delete game->content;
+
+ game->content = new(__FILE__,__LINE__) ContentBundle("content", locale);
+
+ loader->SetDataPath(0);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Game::SetupPalette()
+{
+ if (LoadPalette(standard_palette, inverse_palette)) {
+ Color::SetPalette(standard_palette, 256, inverse_palette);
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Game::LoadPalette(PALETTEENTRY* pal, BYTE* inv)
+{
+ char palheader[32];
+ struct {
+ WORD Version;
+ WORD NumberOfEntries;
+ PALETTEENTRY Entries[256];
+
+ } Palette = { 0x300, 256 };
+
+ DataLoader* loader = DataLoader::GetLoader();
+ BYTE* block;
+ char fname[256];
+ sprintf(fname, "%s.pal", palette_name);
+
+ if (!loader->LoadBuffer(fname, block)) {
+ Print(" Could not open file '%s'\n", fname);
+ return false;
+ }
+
+ memcpy(&palheader, block, 24);
+ memcpy((char*) Palette.Entries, (block+24), 256*4);
+
+ for (int i = 0; i < 256; i++) {
+ *pal++ = Palette.Entries[i];
+ }
+
+ loader->ReleaseBuffer(block);
+
+ sprintf(fname, "%s.ipl", palette_name);
+ int size = loader->LoadBuffer(fname, block);
+ if (size < 32768) {
+ Print(" Could not open file '%s'\n", fname);
+ return false;
+ }
+
+ memcpy(inv, block, 32768);
+ loader->ReleaseBuffer(block);
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Game::Run()
+{
+ MSG msg;
+
+ status = RUN;
+ Print("\n");
+ Print("+====================================================================+\n");
+ Print("| RUN |\n");
+ Print("+====================================================================+\n");
+
+ // Polling messages from event queue until quit
+ while (status < EXIT) {
+ if (PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE)) {
+ if (msg.message == WM_QUIT)
+ break;
+
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ else {
+ if (ProfileGameLoop())
+ WaitMessage();
+ }
+ }
+
+ return exit_code ? exit_code : msg.wParam;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Game::Exit()
+{
+ Print("\n\n*** Game::Exit()\n");
+ status = EXIT;
+}
+
+void
+Game::Panic(const char* msg)
+{
+ if (msg) Print("*** PANIC: %s\n", msg);
+ else Print("*** PANIC! ***\n");
+
+ if (!msg) msg = "Unspecified fatal error.";
+ sprintf(panicbuf, "%s\nThis game will now terminate.", msg);
+
+ if (game) {
+ game->status = PANIC;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Game::Activate(bool f)
+{
+ active = f;
+
+ if (active && video)
+ video->InvalidateCache();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Game::Pause(bool f)
+{
+ if (f) {
+ if (soundcard)
+ soundcard->Pause();
+ paused = true;
+ }
+ else {
+ if (soundcard)
+ soundcard->Resume();
+ paused = false;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool ProfileGameLoop(void)
+{
+ return game->GameLoop();
+}
+
+bool
+Game::GameLoop()
+{
+ bool wait_for_windows_events = true;
+
+ if (active && !paused) {
+ if (!server) {
+ // Route Events to EventTargets
+ EventDispatch* ed = EventDispatch::GetInstance();
+ if (ed)
+ ed->Dispatch();
+ }
+
+ UpdateWorld();
+ GameState();
+
+ if (!server) {
+ UpdateScreen();
+ CollectStats();
+ }
+
+ wait_for_windows_events = false;
+ }
+ else if (active && paused) {
+ if (GetKey()=='P')
+ Pause(false);
+ }
+
+ QueryPerformanceCounter(&perf_cnt2);
+
+ double freq = (double) (perf_freq.QuadPart);
+ double msec = (double) (perf_cnt2.QuadPart - perf_cnt1.QuadPart);
+
+ msec /= freq;
+ msec *= 1000.0;
+
+ if (msec < 1)
+ msec = 1;
+
+ real_time += (DWORD) msec;
+
+ frame_number++;
+ Mouse::w = 0;
+
+ perf_cnt1 = perf_cnt2;
+
+ return wait_for_windows_events;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Game::UpdateWorld()
+{
+ long new_time = real_time;
+ double delta = new_time - frame_time;
+ gui_seconds = delta * 0.001;
+ seconds = max_frame_length;
+
+ if (time_comp == 1)
+ {
+ if (delta < max_frame_length * 1000)
+ seconds = delta * 0.001;
+ }
+ else
+ {
+ seconds = time_comp * delta * 0.001;
+ }
+
+ frame_time = new_time;
+ game_time += (DWORD) (seconds * 1000);
+
+ if (world)
+ world->ExecFrame(seconds);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Game::GameState()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Game::UpdateScreen()
+{
+ if (!screen || !video) return;
+
+ if (screen->Refresh()) {
+ if (Keyboard::KeyDown(VK_F12)) {
+ if (Keyboard::KeyDown(VK_SHIFT)) {
+ if (!avi_file) {
+ AVICapture(); // begin capturing
+ SetMaxFrameLength(MAX_FRAME_TIME_VIDEO);
+ }
+ else {
+ delete avi_file; // end capture;
+ avi_file = 0;
+ SetMaxFrameLength(MAX_FRAME_TIME_NORMAL);
+ }
+ }
+ else {
+ if (!avi_file) {
+ ScreenCapture();
+ }
+ else {
+ delete avi_file; // end capture;
+ avi_file = 0;
+ SetMaxFrameLength(MAX_FRAME_TIME_NORMAL);
+ }
+ }
+ }
+ else if (avi_file) {
+ AVICapture(); // continue capturing...
+ }
+
+ video->Present();
+ }
+ else {
+ Panic("Screen refresh failed.");
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Game*
+Game::GetInstance()
+{
+ return game;
+}
+
+Video*
+Game::GetVideo()
+{
+ if (game)
+ return game->video;
+
+ return 0;
+}
+
+Color
+Game::GetScreenColor()
+{
+ if (game)
+ return game->screen_color;
+
+ return Color::Black;
+}
+
+void
+Game::SetScreenColor(Color c)
+{
+ if (game) {
+ game->screen_color = c;
+
+ if (game->screen)
+ game->screen->SetBackgroundColor(c);
+ }
+}
+
+int
+Game::GetScreenWidth()
+{
+ if (game && game->video)
+ return game->video->Width();
+
+ return 0;
+}
+
+int
+Game::GetScreenHeight()
+{
+ if (game && game->video)
+ return game->video->Height();
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Game::ScreenCapture(const char* name)
+{
+ if (server || !video || !screen) return;
+
+ static DWORD last_shot = 0;
+ static DWORD shot_num = 1;
+ DataLoader* loader = DataLoader::GetLoader();
+ char filename[256];
+
+ if (!name && (real_time - last_shot) < 1000)
+ return;
+
+ // try not to overwrite existing screen shots...
+ if (loader) {
+ bool use_file_sys = loader->IsFileSystemEnabled();
+ loader->UseFileSystem(true);
+ loader->SetDataPath(0);
+ List<Text> shot_list;
+ loader->ListFiles("*.PCX", shot_list);
+ loader->UseFileSystem(use_file_sys);
+
+ for (int i = 0; i < shot_list.size(); i++) {
+ Text* s = shot_list[i];
+ int n = 0;
+
+ sscanf(s->data()+1, "%d", &n);
+ if (shot_num <= (DWORD) n)
+ shot_num = n+1;
+ }
+
+ shot_list.destroy();
+ }
+
+ if (name)
+ strcpy(filename, name);
+ else
+ sprintf(filename, "A%d.PCX", shot_num++);
+
+ Bitmap bmp;
+
+ if (video && video->Capture(bmp)) {
+ PcxImage pcx(bmp.Width(), bmp.Height(), (LPDWORD) bmp.HiPixels());
+ pcx.Save((char*) filename);
+ }
+
+ last_shot = real_time;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Game::AVICapture(const char* name)
+{
+ if (server || !video || !screen) return;
+
+ if (!avi_file) {
+ char filename[256];
+ Bitmap bmp;
+ DataLoader* loader = DataLoader::GetLoader();
+ DWORD avi_num = 1;
+
+ // try not to overwrite existing screen shots...
+ if (loader) {
+ bool use_file_sys = loader->IsFileSystemEnabled();
+ loader->UseFileSystem(true);
+ loader->SetDataPath(0);
+ List<Text> avi_list;
+ loader->ListFiles("*.avi", avi_list);
+ loader->UseFileSystem(use_file_sys);
+
+ for (int i = 0; i < avi_list.size(); i++) {
+ Text* s = avi_list[i];
+ int n = 0;
+
+ sscanf(s->data()+1, "%d", &n);
+ if (avi_num <= (DWORD) n)
+ avi_num = n+1;
+ }
+
+ avi_list.destroy();
+ }
+
+ if (name)
+ strcpy(filename, name);
+ else
+ sprintf(filename, "A%d.avi", avi_num);
+
+ if (video && video->Capture(bmp)) {
+ //bmp.ScaleTo(bmp.Width()/2, bmp.Height()/2);
+ avi_file = new(__FILE__,__LINE__) AviFile(filename, Rect(0,0,bmp.Width(),bmp.Height()), VIDEO_FPS);
+ }
+
+ }
+
+ else {
+ Bitmap bmp;
+
+ if (video && video->Capture(bmp)) {
+ //bmp.ScaleTo(bmp.Width()/2, bmp.Height()/2);
+ avi_file->AddFrame(bmp);
+ }
+ }
+}
+
+
+
+// +--------------------------------------------------------------------+
+
+void
+Game::CollectStats()
+{
+ frame_count++;
+
+ if (!totaltime) totaltime = real_time;
+
+ if (frame_time - frame_time0 > 200) {
+ frame_rate = (frame_count - frame_count0) * 1000.0 / (frame_time - frame_time0);
+ frame_time0 = frame_time;
+ frame_count0 = frame_count;
+ }
+
+ if (video) {
+ stats.nframe = video->GetStats().nframe;
+ stats.nverts = video->GetStats().nverts;
+ stats.npolys = video->GetStats().npolys;
+ stats.nlines = video->GetStats().nlines;
+ stats.ncalls += video->GetStats().ncalls;
+
+ stats.total_verts += stats.nverts;
+ stats.total_polys += stats.npolys;
+ stats.total_lines += stats.nlines;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Game::ShowStats()
+{
+ if (server) return;
+
+ totaltime = real_time - totaltime;
+
+ Print("\n");
+ Print("Performance Data:\n");
+ Print("-----------------\n");
+
+ Print(" Time: %d msec\n", totaltime);
+ Print(" Frames: %d\n", stats.nframe);
+ Print(" Polys Rendered: %d\n", stats.total_polys);
+ Print(" Lines Rendered: %d\n", stats.total_lines);
+ Print(" Verts Rendered: %d\n", stats.total_verts);
+ Print(" Render Calls: %d\n", stats.ncalls);
+ Print("\n");
+
+ Print("Performance Statistics:\n");
+ Print("-----------------------\n");
+
+ Print(" Frames/Second: %.2f\n", (stats.nframe * 1000.0) / totaltime);
+ Print(" Polys/Frame: %.2f\n", (double) stats.total_polys / (double) stats.nframe);
+ Print(" Polys/Call: %.2f\n", (double) stats.total_polys / (double) stats.ncalls);
+ Print(" Polys/Second: %.2f\n", (stats.total_polys * 1000.0) / totaltime);
+ Print(" Lines/Second: %.2f\n", (stats.total_lines * 1000.0) / totaltime);
+ Print(" Verts/Second: %.2f\n", (stats.total_verts * 1000.0) / totaltime);
+
+ Print("\n");
+}
+
+// +====================================================================+
+// WndProc
+// +====================================================================+
+
+#ifndef WM_MOUSEWHEEL
+#define WM_MOUSEWHEEL 0x20A
+#endif
+
+LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM uParam, LPARAM lParam)
+{
+ switch (message) {
+ case WM_SYSKEYDOWN:
+ if (uParam == VK_TAB || uParam == VK_F4)
+ return DefWindowProc(hwnd, message, uParam, lParam);
+
+ return 0;
+
+ case WM_MENUCHAR:
+ return MNC_CLOSE << 16;
+
+ case WM_ACTIVATEAPP:
+ // Keep track of whether or not the app is in the foreground
+ if (game)
+ game->Activate(uParam?true:false);
+ break;
+
+ case WM_PAINT:
+ if (!game || !game->OnPaint())
+ return DefWindowProc(hwnd, message, uParam, lParam);
+ break;
+
+ case WM_SETCURSOR:
+ if (Game::ShowMouse()) {
+ return DefWindowProc(hwnd, message, uParam, lParam);
+ }
+ else {
+ // hide the windows mouse cursor
+ SetCursor(NULL);
+ return 1;
+ }
+ break;
+
+ case WM_ENTERSIZEMOVE:
+ // Halt frame movement while the app is sizing or moving
+ if (game)
+ game->Pause(true);
+ break;
+
+ case WM_SIZE:
+ // Pick up possible changes to window style due to maximize, etc.
+ if (game && game->hwnd != NULL ) {
+ game->window_style = GetWindowLong(game->hwnd, GWL_STYLE );
+
+ if (uParam == SIZE_MINIMIZED) {
+ game->Pause(true); // Pause while we're minimized
+ game->is_minimized = true;
+ game->is_maximized = false;
+ }
+
+ else if (uParam == SIZE_MAXIMIZED) {
+ if (game->is_minimized)
+ game->Pause(false); // Unpause since we're no longer minimized
+
+ game->is_minimized = false;
+ game->is_maximized = true;
+ game->ResizeVideo();
+ }
+
+ else if (uParam == SIZE_RESTORED) {
+ if (game->is_maximized) {
+ game->is_maximized = false;
+ game->ResizeVideo();
+ }
+
+ else if (game->is_minimized) {
+ game->Pause(false); // Unpause since we're no longer minimized
+ game->is_minimized = false;
+ game->ResizeVideo();
+ }
+ else {
+ // If we're neither maximized nor minimized, the window size
+ // is changing by the user dragging the window edges. In this
+ // case, we don't reset the device yet -- we wait until the
+ // user stops dragging, and a WM_EXITSIZEMOVE message comes.
+ }
+ }
+ }
+ break;
+
+ case WM_EXITSIZEMOVE:
+ if (game) {
+ game->Pause(false);
+ game->ResizeVideo();
+ }
+ break;
+
+
+ case WM_ENTERMENULOOP:
+ if (game)
+ game->Pause(true);
+ break;
+
+ case WM_EXITMENULOOP:
+ if (game)
+ game->Pause(false);
+ break;
+
+ /*
+ case WM_HELP:
+ if (game)
+ return game->OnHelp();
+ break;
+ */
+
+ case WM_KEYDOWN:
+ BufferKey(uParam);
+ return 0;
+
+ case WM_DESTROY:
+ PostQuitMessage(0);
+ break;
+
+ case WM_MOUSEMOVE:
+ Mouse::x = LOWORD(lParam);
+ Mouse::y = HIWORD(lParam);
+ break;
+
+ case WM_LBUTTONDOWN:
+ Mouse::l = 1;
+ break;
+
+ case WM_LBUTTONDBLCLK:
+ Mouse::l = 2;
+ break;
+
+ case WM_LBUTTONUP:
+ Mouse::l = 0;
+ break;
+
+ case WM_MBUTTONDOWN:
+ Mouse::m = 1;
+ break;
+
+ case WM_MBUTTONDBLCLK:
+ Mouse::m = 2;
+ break;
+
+ case WM_MBUTTONUP:
+ Mouse::m = 0;
+ break;
+
+ case WM_RBUTTONDOWN:
+ Mouse::r = 1;
+ break;
+
+ case WM_RBUTTONDBLCLK:
+ Mouse::r = 2;
+ break;
+
+ case WM_RBUTTONUP:
+ Mouse::r = 0;
+ break;
+
+ case WM_MOUSEWHEEL:
+ {
+ int w = (int) (uParam >> 16);
+ if (w > 32000) w -= 65536;
+ Mouse::w += w;
+ }
+ break;
+
+ case WM_CLOSE:
+ if (game) // && game->Server())
+ game->Exit();
+ break;
+
+ default:
+ return DefWindowProc(hwnd, message, uParam, lParam);
+ }
+
+ return 0;
+}
+
+// +====================================================================+
+
+const int MAX_KEY_BUF = 512;
+static int vkbuf[MAX_KEY_BUF];
+static int vkshiftbuf[MAX_KEY_BUF];
+static int vkins = 0;
+static int vkext = 0;
+
+void
+FlushKeys()
+{
+ Keyboard::FlushKeys();
+ vkins = vkext = 0;
+}
+
+void
+BufferKey(int vkey)
+{
+ if (vkey < 1) return;
+
+ int shift = 0;
+
+ if (GetAsyncKeyState(VK_SHIFT))
+ shift |= 1;
+
+ if (GetAsyncKeyState(VK_CONTROL))
+ shift |= 2;
+
+ if (GetAsyncKeyState(VK_MENU))
+ shift |= 4;
+
+ vkbuf[vkins] = vkey;
+ vkshiftbuf[vkins++] = shift;
+
+ if (vkins >= MAX_KEY_BUF)
+ vkins = 0;
+
+ if (vkins == vkext) {
+ vkext++;
+ if (vkext >= MAX_KEY_BUF)
+ vkext = 0;
+ }
+}
+
+int
+GetKey()
+{
+ if (vkins == vkext) return 0;
+
+ int result = vkbuf[vkext++];
+ if (vkext >= MAX_KEY_BUF)
+ vkext = 0;
+
+ return result;
+}
+
+int
+GetKeyPlus(int& key, int& shift)
+{
+ if (vkins == vkext) return 0;
+
+ key = vkbuf[vkext];
+ shift = vkshiftbuf[vkext++];
+
+ if (vkext >= MAX_KEY_BUF)
+ vkext = 0;
+
+ return key;
+}
+
+// +====================================================================+
+
+void Print(const char* fmt, ...)
+{
+ if (ErrLog) {
+ vsprintf(ErrBuf, fmt, (char *)(&fmt+1));
+
+ fprintf(ErrLog, ErrBuf);
+ fflush(ErrLog);
+ }
+}
+
+// +====================================================================+
+
+DWORD GetRealTime()
+{
+ if (game)
+ return Game::RealTime();
+
+ return timeGetTime();
+}
+
+DWORD Game::RealTime()
+{
+ return real_time;
+}
+
+DWORD Game::GameTime()
+{
+ return game_time;
+}
+
+DWORD Game::TimeCompression()
+{
+ return time_comp;
+}
+
+void Game::SetTimeCompression(DWORD comp)
+{
+ if (comp > 0 && comp <= 100)
+ time_comp = comp;
+}
+
+DWORD Game::Frame()
+{
+ return frame_number;
+}
+
+void Game::ResetGameTime()
+{
+ game_time = 0;
+}
+
+void Game::SkipGameTime(double seconds)
+{
+ if (seconds > 0)
+ game_time += (DWORD) (seconds * 1000);
+}
diff --git a/nGenEx/Game.h b/nGenEx/Game.h
new file mode 100644
index 0000000..ceec324
--- /dev/null
+++ b/nGenEx/Game.h
@@ -0,0 +1,220 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Game.h
+ AUTHOR: John DiCamillo
+
+*/
+
+#ifndef Game_h
+#define Game_h
+
+#include "Types.h"
+#include "Screen.h"
+#include "Video.h"
+
+// +--------------------------------------------------------------------+
+
+void Print(const char* fmt, ...);
+
+LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
+
+void FlushKeys();
+void BufferKey(int vkey);
+int GetKey();
+int GetKeyPlus(int& key, int& shift);
+
+extern "C" bool ProfileGameLoop(void);
+
+// +--------------------------------------------------------------------+
+
+class ContentBundle;
+class Locale;
+class Universe;
+class Sound;
+class SoundCard;
+class Video;
+class VideoFactory;
+class VideoSettings;
+class AviFile;
+class Text;
+
+// +--------------------------------------------------------------------+
+
+class Game
+{
+public:
+ static const char* TYPENAME() { return "Game"; }
+ enum STATUS { OK, RUN, EXIT, PANIC, INIT_FAILED, TOO_MANY };
+
+ Game();
+ virtual ~Game();
+
+ //
+ // MAIN GAME FUNCTIONALITY:
+ //
+
+ virtual bool Init(HINSTANCE hi, HINSTANCE hpi, LPSTR cmdline, int nCmdShow);
+ virtual int Run();
+ virtual void Exit();
+ virtual bool OnPaint() { return false; }
+ virtual bool OnHelp() { return false; }
+
+ virtual void Activate(bool f);
+ virtual void Pause(bool f);
+ int Status() const { return status; }
+
+ virtual void ScreenCapture(const char* name = 0);
+ virtual void AVICapture(const char* fname = 0);
+
+ const RenderStats& GetPolyStats() { return stats; }
+
+ //
+ // GENERAL GAME CLASS UTILITY METHODS:
+ //
+
+ static void Panic(const char* msg=0);
+ static bool DisplayModeSupported(int w, int h, int bpp);
+ static int MaxTexSize();
+ static int MaxTexAspect();
+ static int GammaLevel();
+ static void SetGammaLevel(int g);
+ static void SetMaxTexSize(int n);
+
+ static DWORD RealTime();
+ static DWORD GameTime();
+ static DWORD TimeCompression();
+ static void SetTimeCompression(DWORD comp);
+ static DWORD Frame();
+ static void ResetGameTime();
+ static void SkipGameTime(double seconds);
+
+ static double FrameRate();
+ static double FrameTime();
+ static double GUITime();
+ static void SetMaxFrameLength(double seconds) { max_frame_length = seconds; }
+ static void SetMinFrameLength(double seconds) { min_frame_length = seconds; }
+ static double GetMaxFrameLength() { return max_frame_length; }
+ static double GetMinFrameLength() { return min_frame_length; }
+
+ static Game* GetInstance();
+ static Video* GetVideo();
+ static Color GetScreenColor();
+ static void SetScreenColor(Color c);
+ static int GetScreenWidth();
+ static int GetScreenHeight();
+
+ static bool Active() { return active; }
+ static bool Paused() { return paused; }
+ static bool Server() { return server; }
+ static bool ShowMouse() { return show_mouse; }
+ static bool IsWindowed();
+
+ static HINSTANCE GetHINST();
+ static HWND GetHWND();
+
+ static void UseLocale(Locale* locale);
+ static Text GetText(const char* key);
+
+ static const char* GetPanicMessage() { return panicbuf; }
+
+ virtual bool GameLoop();
+ virtual void UpdateWorld();
+ virtual void GameState();
+ virtual void UpdateScreen();
+ virtual void CollectStats();
+
+ virtual bool InitApplication(HINSTANCE);
+ virtual bool InitInstance(HINSTANCE, int);
+ virtual bool InitContent();
+ virtual bool InitGame();
+ virtual bool InitVideo();
+ virtual bool ResizeVideo();
+ virtual bool ResetVideo();
+ virtual bool ToggleFullscreen();
+ virtual bool AdjustWindowForChange();
+
+ virtual bool SetupPalette();
+ virtual bool LoadPalette(PALETTEENTRY* pal, BYTE* inv);
+ virtual void ShowStats();
+
+protected:
+ friend bool ProfileGameLoop(void);
+ friend LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM uParam, LPARAM lParam);
+
+ ContentBundle* content;
+ Universe* world;
+ VideoFactory* video_factory;
+ Video* video;
+ VideoSettings* video_settings;
+ SoundCard* soundcard;
+ Screen* screen;
+ int gamma;
+ int max_tex_size;
+
+ RenderStats stats;
+ DWORD totaltime;
+
+ PALETTEENTRY standard_palette[256];
+ BYTE inverse_palette[32768];
+
+ HINSTANCE hInst;
+ HWND hwnd;
+ HMENU hmenu;
+ DWORD winstyle;
+
+ char* app_name;
+ char* title_text;
+ char* palette_name;
+
+ // Internal variables for the state of the app
+ bool is_windowed;
+ bool is_active;
+ bool is_device_lost;
+ bool is_minimized;
+ bool is_maximized;
+ bool ignore_size_change;
+ bool is_device_initialized;
+ bool is_device_restored;
+ DWORD window_style; // Saved window style for mode switches
+ RECT bounds_rect; // Saved window bounds for mode switches
+ RECT client_rect; // Saved client area size for mode switches
+
+
+ double gui_seconds;
+ double seconds;
+ double frame_rate;
+ int frame_count;
+ int frame_count0;
+ int frame_time;
+ int frame_time0;
+
+ int status;
+ int exit_code;
+ Color screen_color;
+
+ AviFile* avi_file;
+
+ static bool active;
+ static bool paused;
+ static bool server;
+ static bool show_mouse;
+ static DWORD base_game_time;
+ static DWORD real_time;
+ static DWORD game_time;
+ static DWORD time_comp;
+ static DWORD frame_number;
+
+ static double max_frame_length;
+ static double min_frame_length;
+
+ static char panicbuf[256];
+};
+
+// +--------------------------------------------------------------------+
+
+#endif Game_h
+
+
diff --git a/nGenEx/Geometry.cpp b/nGenEx/Geometry.cpp
new file mode 100644
index 0000000..9f3e22b
--- /dev/null
+++ b/nGenEx/Geometry.cpp
@@ -0,0 +1,698 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Geometry.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Geometric Utilities
+*/
+
+#include "MemDebug.h"
+#include "Geometry.h"
+
+// +--------------------------------------------------------------------+
+
+void Rect::Inflate(int dx, int dy)
+{
+ x -= dx;
+ w += dx*2;
+ y -= dy;
+ h += dy*2;
+}
+
+void Rect::Deflate(int dx, int dy)
+{
+ x += dx;
+ w -= dx*2;
+ y += dy;
+ h -= dy*2;
+}
+
+void Rect::Inset(int l, int r, int t, int b)
+{
+ x += l;
+ y += t;
+ w -= l + r;
+ h -= t + b;
+}
+
+int Rect::Contains(int ax, int ay) const
+{
+ if (ax < x) return 0;
+ if (ax > x+w) return 0;
+ if (ay < y) return 0;
+ if (ay > y+h) return 0;
+
+ return 1;
+}
+
+// +--------------------------------------------------------------------+
+
+double
+Point::Normalize()
+{
+ double scale = 1.0;
+ double len = length();
+
+ if (len)
+ scale /= len;
+
+ x *= scale;
+ y *= scale;
+ z *= scale;
+
+ return len;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Point::SetElement(int i, double v)
+{
+ switch (i) {
+ case 0: x = v; break;
+ case 1: y = v; break;
+ case 2: z = v; break;
+ default: break;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Point
+Point::operator*(const Matrix& m) const
+{
+ Point result;
+
+ result.x = (m.elem[0][0] * x) + (m.elem[1][0] * y) + (m.elem[2][0] * z);
+ result.y = (m.elem[0][1] * x) + (m.elem[1][1] * y) + (m.elem[2][1] * z);
+ result.z = (m.elem[0][2] * x) + (m.elem[1][2] * y) + (m.elem[2][2] * z);
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+double ClosestApproachTime(const Point& loc1, const Point& vel1,
+ const Point& loc2, const Point& vel2)
+{
+ double t = 0;
+
+ Point D = loc1-loc2;
+ Point Dv = vel1-vel2;
+
+ if (Dv.x || Dv.y || Dv.z)
+ t = -1 * (Dv*D) / (Dv*Dv);
+
+ return t;
+}
+
+// +--------------------------------------------------------------------+
+
+float
+Vec2::Normalize()
+{
+ float scale = 1.0f;
+ float len = length();
+
+ if (len)
+ scale /= len;
+
+ x *= scale;
+ y *= scale;
+
+ return len;
+}
+
+// +--------------------------------------------------------------------+
+
+float
+Vec3::Normalize()
+{
+ float scale = 1.0f;
+ float len = length();
+
+ if (len)
+ scale /= len;
+
+ x *= scale;
+ y *= scale;
+ z *= scale;
+
+ return len;
+}
+
+// +--------------------------------------------------------------------+
+
+Vec3
+Vec3::operator*(const Matrix& m) const
+{
+ Vec3 result;
+
+ result.x = (float) ((m.elem[0][0] * x) + (m.elem[1][0] * y) + (m.elem[2][0] * z));
+ result.y = (float) ((m.elem[0][1] * x) + (m.elem[1][1] * y) + (m.elem[2][1] * z));
+ result.z = (float) ((m.elem[0][2] * x) + (m.elem[1][2] * y) + (m.elem[2][2] * z));
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+double ClosestApproachTime(const Vec3& loc1, const Vec3& vel1,
+ const Vec3& loc2, const Vec3& vel2)
+{
+ double t = 0;
+
+ Point D = loc1-loc2;
+ Point Dv = vel1-vel2;
+
+ if (Dv.x || Dv.y || Dv.z)
+ t = -1 * (Dv*D) / (Dv*Dv);
+
+ return t;
+}
+
+// +--------------------------------------------------------------------+
+
+double
+Quaternion::Normalize()
+{
+ double scale = 1.0;
+ double len = length();
+
+ if (len)
+ scale /= len;
+
+ x *= scale;
+ y *= scale;
+ z *= scale;
+ w *= scale;
+
+ return len;
+}
+
+// +--------------------------------------------------------------------+
+
+Matrix::Matrix()
+{
+ Identity();
+}
+
+Matrix::Matrix(const Matrix& m)
+{
+ CopyMemory(elem, m.elem, sizeof(elem));
+}
+
+Matrix::Matrix(const Point& vrt, const Point& vup, const Point& vpn)
+{
+ elem[0][0] = vrt.x;
+ elem[0][1] = vrt.y;
+ elem[0][2] = vrt.z;
+
+ elem[1][0] = vup.x;
+ elem[1][1] = vup.y;
+ elem[1][2] = vup.z;
+
+ elem[2][0] = vpn.x;
+ elem[2][1] = vpn.y;
+ elem[2][2] = vpn.z;
+}
+
+// +--------------------------------------------------------------------+
+
+Matrix&
+Matrix::operator =(const Matrix& m)
+{
+ CopyMemory(elem, m.elem, sizeof(elem));
+
+ return *this;
+}
+
+// +--------------------------------------------------------------------+
+
+Matrix&
+Matrix::operator*=(const Matrix& m)
+{
+ return *this = *this * m;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Matrix::Identity()
+{
+ elem[0][0] = 1;
+ elem[0][1] = 0;
+ elem[0][2] = 0;
+
+ elem[1][0] = 0;
+ elem[1][1] = 1;
+ elem[1][2] = 0;
+
+ elem[2][0] = 0;
+ elem[2][1] = 0;
+ elem[2][2] = 1;
+}
+
+// +--------------------------------------------------------------------+
+
+inline void swap_elem(double& a, double& b) { double t=a; a=b; b=t; }
+
+void
+Matrix::Transpose()
+{
+ swap_elem(elem[0][1], elem[1][0]);
+ swap_elem(elem[0][2], elem[2][0]);
+ swap_elem(elem[1][2], elem[2][1]);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Matrix::Rotate(double roll, double pitch, double yaw)
+{
+ double e[3][3];
+ CopyMemory(e, elem, sizeof(elem));
+
+ double sr = sin(roll);
+ double cr = cos(roll);
+ double sp = sin(pitch);
+ double cp = cos(pitch);
+ double sy = sin(yaw);
+ double cy = cos(yaw);
+
+ double a,b,c;
+
+ a = cy*cr;
+ b = cy*sr;
+ c = -sy;
+
+ elem[0][0] = a*e[0][0] + b*e[1][0] + c*e[2][0];
+ elem[0][1] = a*e[0][1] + b*e[1][1] + c*e[2][1];
+ elem[0][2] = a*e[0][2] + b*e[1][2] + c*e[2][2];
+
+ a = cp*-sr + sp*sy*cr;
+ b = cp* cr + sp*sy*sr;
+ c = sp*cy;
+
+ elem[1][0] = a*e[0][0] + b*e[1][0] + c*e[2][0];
+ elem[1][1] = a*e[0][1] + b*e[1][1] + c*e[2][1];
+ elem[1][2] = a*e[0][2] + b*e[1][2] + c*e[2][2];
+
+ a = -sp*-sr + cp*sy*cr;
+ b = -sp* cr + cp*sy*sr;
+ c = cp*cy;
+
+ elem[2][0] = a*e[0][0] + b*e[1][0] + c*e[2][0];
+ elem[2][1] = a*e[0][1] + b*e[1][1] + c*e[2][1];
+ elem[2][2] = a*e[0][2] + b*e[1][2] + c*e[2][2];
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Matrix::Roll(double roll)
+{
+ double s = sin(roll);
+ double c = cos(roll);
+
+ double e00 = elem[0][0];
+ double e01 = elem[0][1];
+ double e02 = elem[0][2];
+ double e10 = elem[1][0];
+ double e11 = elem[1][1];
+ double e12 = elem[1][2];
+
+ elem[0][0] = c*e00 + s*e10;
+ elem[0][1] = c*e01 + s*e11;
+ elem[0][2] = c*e02 + s*e12;
+
+ elem[1][0] = -s*e00 + c*e10;
+ elem[1][1] = -s*e01 + c*e11;
+ elem[1][2] = -s*e02 + c*e12;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Matrix::Pitch(double pitch)
+{
+ double s = sin(pitch);
+ double c = cos(pitch);
+
+ double e10 = elem[1][0];
+ double e11 = elem[1][1];
+ double e12 = elem[1][2];
+ double e20 = elem[2][0];
+ double e21 = elem[2][1];
+ double e22 = elem[2][2];
+
+ elem[1][0] = c*e10 + s*e20;
+ elem[1][1] = c*e11 + s*e21;
+ elem[1][2] = c*e12 + s*e22;
+
+ elem[2][0] = -s*e10 + c*e20;
+ elem[2][1] = -s*e11 + c*e21;
+ elem[2][2] = -s*e12 + c*e22;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Matrix::Yaw(double yaw)
+{
+ double s = sin(yaw);
+ double c = cos(yaw);
+
+ double e00 = elem[0][0];
+ double e01 = elem[0][1];
+ double e02 = elem[0][2];
+ double e20 = elem[2][0];
+ double e21 = elem[2][1];
+ double e22 = elem[2][2];
+
+ elem[0][0] = c*e00 - s*e20;
+ elem[0][1] = c*e01 - s*e21;
+ elem[0][2] = c*e02 - s*e22;
+
+ elem[2][0] = s*e00 + c*e20;
+ elem[2][1] = s*e01 + c*e21;
+ elem[2][2] = s*e02 + c*e22;
+}
+
+// +--------------------------------------------------------------------+
+
+inline int sign(double d) { return (d >= 0); }
+
+void
+Matrix::ComputeEulerAngles(double& roll, double& pitch, double& yaw) const
+{
+ double cy;
+
+ yaw = asin(-elem[0][2]);
+ cy = cos(yaw);
+ roll = asin(elem[0][1] / cy);
+ pitch = asin(elem[1][2] / cy);
+
+ if (sign(cos(roll)*cy) != sign(elem[0][0]))
+ roll = PI - roll;
+
+ if (sign(cos(pitch)*cy) != sign(elem[2][2]))
+ pitch = PI - pitch;
+}
+
+// +--------------------------------------------------------------------+
+
+Matrix
+Matrix::operator*(const Matrix& m) const
+{
+ Matrix r;
+
+ r.elem[0][0] = elem[0][0]*m.elem[0][0] + elem[0][1]*m.elem[1][0] + elem[0][2]*m.elem[2][0];
+ r.elem[0][1] = elem[0][0]*m.elem[0][1] + elem[0][1]*m.elem[1][1] + elem[0][2]*m.elem[2][1];
+ r.elem[0][2] = elem[0][0]*m.elem[0][2] + elem[0][1]*m.elem[1][2] + elem[0][2]*m.elem[2][2];
+
+ r.elem[1][0] = elem[1][0]*m.elem[0][0] + elem[1][1]*m.elem[1][0] + elem[1][2]*m.elem[2][0];
+ r.elem[1][1] = elem[1][0]*m.elem[0][1] + elem[1][1]*m.elem[1][1] + elem[1][2]*m.elem[2][1];
+ r.elem[1][2] = elem[1][0]*m.elem[0][2] + elem[1][1]*m.elem[1][2] + elem[1][2]*m.elem[2][2];
+
+ r.elem[2][0] = elem[2][0]*m.elem[0][0] + elem[2][1]*m.elem[1][0] + elem[2][2]*m.elem[2][0];
+ r.elem[2][1] = elem[2][0]*m.elem[0][1] + elem[2][1]*m.elem[1][1] + elem[2][2]*m.elem[2][1];
+ r.elem[2][2] = elem[2][0]*m.elem[0][2] + elem[2][1]*m.elem[1][2] + elem[2][2]*m.elem[2][2];
+
+ return r;
+}
+
+// +--------------------------------------------------------------------+
+
+Point
+Matrix::operator*(const Point& p) const
+{
+ Point result;
+
+ result.x = (elem[0][0] * p.x) + (elem[0][1] * p.y) + (elem[0][2] * p.z);
+ result.y = (elem[1][0] * p.x) + (elem[1][1] * p.y) + (elem[1][2] * p.z);
+ result.z = (elem[2][0] * p.x) + (elem[2][1] * p.y) + (elem[2][2] * p.z);
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+Vec3
+Matrix::operator*(const Vec3& v) const
+{
+ Vec3 result;
+
+ result.x = (float) ((elem[0][0] * v.x) + (elem[0][1] * v.y) + (elem[0][2] * v.z));
+ result.y = (float) ((elem[1][0] * v.x) + (elem[1][1] * v.y) + (elem[1][2] * v.z));
+ result.z = (float) ((elem[2][0] * v.x) + (elem[2][1] * v.y) + (elem[2][2] * v.z));
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+double
+Matrix::Cofactor(int i, int j) const
+{
+ int i1=0;
+ int i2=2;
+ int j1=0;
+ int j2=2;
+
+ if (i==0) i1=1; else if (i==2) i2=1;
+ if (j==0) j1=1; else if (j==2) j2=1;
+
+ double factor = elem[i1][j1]*elem[i2][j2] - elem[i1][j2]*elem[i2][j1];
+
+ if ((i+j) & 1)
+ factor *= -1;
+
+ return factor;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Matrix::Invert()
+{
+ double f[3][3];
+ int i, j;
+
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 3; j++)
+ f[i][j] = Cofactor(j,i);
+
+ double det = elem[0][0] * f[0][0] +
+ elem[0][1] * f[1][0] +
+ elem[0][2] * f[2][0];
+
+ if (det != 0) {
+ double inv = 1/det;
+
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 3; j++)
+ elem[i][j] = f[i][j] * inv;
+ }
+}
+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+
+Plane::Plane()
+ : distance(0.0f)
+{ }
+
+Plane::Plane(const Point& p0, const Point& p1, const Point& p2)
+{
+ Point d1 = p1 - p0;
+ Point d2 = p2 - p0;
+
+ normal = (Vec3) d1.cross(d2);
+ normal.Normalize();
+
+ distance = (float) (normal * p0);
+}
+
+Plane::Plane(const Vec3& v0, const Vec3& v1, const Vec3& v2)
+{
+ Vec3 d1 = v1 - v0;
+ Vec3 d2 = v2 - v0;
+
+ normal = d1.cross(d2);
+ normal.Normalize();
+
+ distance = normal * v0;
+}
+
+void Plane::Rotate(const Vec3& v0, const Matrix& m)
+{
+ normal = normal * m;
+ distance = normal * v0;
+}
+
+void Plane::Translate(const Vec3& v0)
+{
+ distance = normal * v0;
+}
+
+// +--------------------------------------------------------------------+
+// 3-D dot product.
+
+double DotProduct(const Point& a, const Point& b)
+{
+ return (a.x * b.x) + (a.y * b.y) + (a.z * b.z);
+}
+
+// +--------------------------------------------------------------------+
+// 3-D cross product.
+
+void CrossProduct(const Point& a, const Point& b, Point& out)
+{
+ out.x = (a.y * b.z) - (a.z * b.y);
+ out.y = (a.z * b.x) - (a.x * b.z);
+ out.z = (a.x * b.y) - (a.y * b.x);
+}
+
+// +--------------------------------------------------------------------+
+// Concatenate two 3x3 matrices.
+
+void MConcat(double in1[3][3], double in2[3][3], double out[3][3])
+{
+ int i, j;
+
+ for (i=0 ; i<3 ; i++) {
+ for (j=0 ; j<3 ; j++) {
+ out[i][j] = in1[i][0] * in2[0][j] +
+ in1[i][1] * in2[1][j] +
+ in1[i][2] * in2[2][j];
+ }
+ }
+}
+
+/* GRAPHICS GEMS II ----------------------------------------------------
+ *
+ * lines_intersect: AUTHOR: Mukesh Prasad
+ *
+ * This function computes whether two line segments,
+ * respectively joining the input points (x1,y1) -- (x2,y2)
+ * and the input points (x3,y3) -- (x4,y4) intersect.
+ * If the lines intersect, the output variables x, y are
+ * set to coordinates of the point of intersection.
+ *
+ * All values are in integers. The returned value is rounded
+ * to the nearest integer point.
+ *
+ * If non-integral grid points are relevant, the function
+ * can easily be transformed by substituting floating point
+ * calculations instead of integer calculations.
+ *
+ * Entry
+ * x1, y1, x2, y2 Coordinates of endpoints of one segment.
+ * x3, y3, x4, y4 Coordinates of endpoints of other segment.
+ *
+ * Exit
+ * x, y Coordinates of intersection point.
+ *
+ * The value returned by the function is one of:
+ *
+ * DONT_INTERSECT 0
+ * DO_INTERSECT 1
+ * COLLINEAR 2
+ *
+ * Error conditions:
+ *
+ * Depending upon the possible ranges, and particularly on 16-bit
+ * computers, care should be taken to protect from overflow.
+ *
+ * In the following code, 'long' values have been used for this
+ * purpose, instead of 'int'.
+ *
+ */
+
+#define DONT_INTERSECT 0
+#define DO_INTERSECT 1
+#define COLLINEAR 2
+
+inline int SAME_SIGNS(double a, double b)
+{
+ return ((a>=0 && b>=0) || (a<0 && b<0));
+}
+
+int
+lines_intersect(
+ /* 1st line segment */ double x1, double y1, double x2, double y2,
+ /* 2nd line segment */ double x3, double y3, double x4, double y4,
+ /* pt of intersect */ double& ix, double& iy)
+{
+ double a1, a2, b1, b2, c1, c2; /* Coefficients of line eqns. */
+ double r1, r2, r3, r4; /* 'Sign' values */
+ double denom, offset, num; /* Intermediate values */
+
+ /* Compute a1, b1, c1, where line joining points 1 and 2
+ * is "a1 x + b1 y + c1 = 0". */
+
+ a1 = y2 - y1;
+ b1 = x1 - x2;
+ c1 = x2 * y1 - x1 * y2;
+
+ /* Compute r3 and r4. */
+
+ r3 = a1 * x3 + b1 * y3 + c1;
+ r4 = a1 * x4 + b1 * y4 + c1;
+
+ /* Check signs of r3 and r4. If both point 3 and point 4 lie on
+ * same side of line 1, the line segments do not intersect. */
+
+ if ( r3 != 0 &&
+ r4 != 0 &&
+ SAME_SIGNS( r3, r4 ))
+ return ( DONT_INTERSECT );
+
+ /* Compute a2, b2, c2 */
+
+ a2 = y4 - y3;
+ b2 = x3 - x4;
+ c2 = x4 * y3 - x3 * y4;
+
+ /* Compute r1 and r2 */
+
+ r1 = a2 * x1 + b2 * y1 + c2;
+ r2 = a2 * x2 + b2 * y2 + c2;
+
+ /* Check signs of r1 and r2. If both point 1 and point 2 lie
+ * on same side of second line segment, the line segments do
+ * not intersect. */
+
+ if ( r1 != 0 &&
+ r2 != 0 &&
+ SAME_SIGNS( r1, r2 ))
+ return ( DONT_INTERSECT );
+
+ /* Line segments intersect: compute intersection point. */
+
+ denom = a1 * b2 - a2 * b1;
+ if ( denom == 0 )
+ return ( DONT_INTERSECT );
+ offset = denom < 0 ? - denom / 2 : denom / 2;
+
+ /* The denom/2 is to get rounding instead of truncating. It
+ * is added or subtracted to the numerator, depending upon the
+ * sign of the numerator. */
+
+ num = b1 * c2 - b2 * c1;
+ ix = ( num < 0 ? num - offset : num + offset ) / denom;
+
+ num = a2 * c1 - a1 * c2;
+ iy = ( num < 0 ? num - offset : num + offset ) / denom;
+
+ return ( DO_INTERSECT );
+}
+
diff --git a/nGenEx/Geometry.h b/nGenEx/Geometry.h
new file mode 100644
index 0000000..faa55fa
--- /dev/null
+++ b/nGenEx/Geometry.h
@@ -0,0 +1,302 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Geometry.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Geometric classes: Rect, Vec3, Point, Matrix, Plane
+*/
+
+#ifndef Geometry_h
+#define Geometry_h
+
+#include "Types.h"
+
+// +--------------------------------------------------------------------+
+
+struct Rect;
+struct Insets;
+struct Matrix;
+struct Vec3;
+struct Point;
+struct Quaternion;
+struct Plane;
+
+const double PI = 3.14159265358979323846;
+const double DEGREES = (PI/180);
+
+// +--------------------------------------------------------------------+
+
+struct Rect
+{
+ static const char* TYPENAME() { return "Rect"; }
+
+ Rect() : x(0), y(0), w(0), h(0) { }
+ Rect(int ix, int iy, int iw, int ih) : x(ix), y(iy), w(iw), h(ih) { }
+
+ int operator==(const Rect& r) const { return x==r.x && y==r.y && w==r.w && h==r.h; }
+ int operator!=(const Rect& r) const { return x!=r.x || y!=r.y || w!=r.w || h!=r.h; }
+
+ void Inflate(int dw, int dh);
+ void Deflate(int dw, int dh);
+ void Inset(int left, int right, int top, int bottom);
+ int Contains(int x, int y) const;
+
+ int x, y, w, h;
+};
+
+// +--------------------------------------------------------------------+
+
+struct Insets
+{
+ Insets() : left(0), right(0), top(0), bottom(0) { }
+ Insets(WORD l, WORD r, WORD t, WORD b) : left(l), right(r), top(t), bottom(b) { }
+
+ WORD left;
+ WORD right;
+ WORD top;
+ WORD bottom;
+};
+
+// +--------------------------------------------------------------------+
+
+struct Matrix
+{
+ static const char* TYPENAME() { return "Matrix"; }
+
+ Matrix();
+ Matrix(const Matrix& m);
+ Matrix(const Point& vrt, const Point& vup, const Point& vpn);
+
+ Matrix& operator = (const Matrix& m);
+ Matrix& operator *= (const Matrix& m);
+
+ double operator() (int i, int j) const { return elem[i][j]; }
+ double& operator() (int i, int j) { return elem[i][j]; }
+
+ void Identity();
+ void Transpose();
+ void Rotate(double roll, double pitch, double yaw);
+ void Roll(double roll);
+ void Pitch(double pitch);
+ void Yaw(double yaw);
+ void ComputeEulerAngles(double& roll, double& pitch, double& yaw) const;
+
+ double Cofactor(int i, int j) const;
+ void Invert();
+
+ Matrix Inverse() const {
+ Matrix result(*this);
+ result.Invert();
+ return result;
+ }
+
+ Matrix operator*(const Matrix& m) const;
+ Point operator*(const Point & p) const;
+ Vec3 operator*(const Vec3& v) const;
+
+ double elem[3][3];
+
+private:
+ Matrix(int no_init) { }
+};
+
+// +--------------------------------------------------------------------+
+
+struct Vec2
+{
+ static const char* TYPENAME() { return "Vec2"; }
+
+ Vec2() { }
+ Vec2(int ix, int iy) : x((float) ix), y((float) iy) { }
+ Vec2(float ix, float iy) : x(ix), y(iy) { }
+ Vec2(double ix, double iy) : x((float) ix), y((float) iy) { }
+
+ operator void*() const { return (void*) (x || y); }
+ int operator==(const Vec2& p) const { return x==p.x && y==p.y; }
+ int operator!=(const Vec2& p) const { return x!=p.x || y!=p.y; }
+ Vec2 operator+ (const Vec2& p) const { return Vec2(x+p.x, y+p.y); }
+ Vec2 operator- (const Vec2& p) const { return Vec2(x-p.x, y-p.y); }
+ Vec2 operator- () const { return Vec2(-x, -y); }
+ Vec2 operator* (float s) const { return Vec2(x*s, y*s); }
+ Vec2 operator/ (float s) const { return Vec2(x/s, y/s); }
+ float operator*(const Vec2& p) const { return (x*p.x + y*p.y); }
+
+ Vec2& operator= (const Vec2& p) { x =p.x; y =p.y; return *this; }
+ Vec2& operator+=(const Vec2& p) { x+=p.x; y+=p.y; return *this; }
+ Vec2& operator-=(const Vec2& p) { x-=p.x; y-=p.y; return *this; }
+ Vec2& operator*=(float s) { x*=s; y*=s; return *this; }
+ Vec2& operator/=(float s) { x/=s; y/=s; return *this; }
+
+ float length() const { return (float) sqrt(x*x+y*y); }
+ float Normalize();
+ float dot(const Vec2& p) const { return (x*p.x + y*p.y); }
+ Vec2 normal() const { return Vec2(-y, x); }
+
+ float x, y;
+};
+
+// +--------------------------------------------------------------------+
+
+struct Vec3
+{
+ static const char* TYPENAME() { return "Vec3"; }
+
+ Vec3() { }
+ Vec3(int ix, int iy, int iz) : x((float) ix), y((float) iy), z((float) iz) { }
+ Vec3(float ix, float iy, float iz) : x(ix), y(iy), z(iz) { }
+ Vec3(double ix, double iy, double iz) : x((float) ix), y((float) iy), z((float) iz) { }
+
+ operator void*() const { return (void*) (x || y || z); }
+ int operator==(const Vec3& p) const { return x==p.x && y==p.y && z==p.z; }
+ int operator!=(const Vec3& p) const { return x!=p.x || y!=p.y || z!=p.z; }
+ Vec3 operator+ (const Vec3& p) const { return Vec3(x+p.x, y+p.y, z+p.z); }
+ Vec3 operator- (const Vec3& p) const { return Vec3(x-p.x, y-p.y, z-p.z); }
+ Vec3 operator- () const { return Vec3(-x, -y, -z); }
+ Vec3 operator* (float s) const { return Vec3(x*s, y*s, z*s); }
+ Vec3 operator/ (float s) const { return Vec3(x/s, y/s, z/s); }
+ float operator* (const Vec3& p) const { return (x*p.x + y*p.y + z*p.z); }
+ Vec3 operator* (const Matrix&) const;
+
+ Vec3& operator= (const Vec3& p) { x =p.x; y =p.y; z =p.z; return *this; }
+ Vec3& operator+=(const Vec3& p) { x+=p.x; y+=p.y; z+=p.z; return *this; }
+ Vec3& operator-=(const Vec3& p) { x-=p.x; y-=p.y; z-=p.z; return *this; }
+ Vec3& operator*=(float s) { x*=s; y*=s; z*=s; return *this; }
+ Vec3& operator/=(float s) { x/=s; y/=s; z/=s; return *this; }
+
+ void SwapYZ() { float t = y; y = z; z = t; }
+ float length() const { return (float) sqrt(x*x+y*y+z*z); }
+ float Normalize();
+
+ float dot(const Vec3& p) const { return (x*p.x + y*p.y + z*p.z); }
+ Vec3 cross(const Vec3& v) const { return Vec3((y*v.z) - (z*v.y),
+ (z*v.x) - (x*v.z),
+ (x*v.y) - (y*v.x)); }
+
+ float x, y, z;
+};
+
+double ClosestApproachTime(const Vec3& loc1, const Vec3& vel1,
+ const Vec3& loc2, const Vec3& vel2);
+
+// +--------------------------------------------------------------------+
+
+struct Point
+{
+ static const char* TYPENAME() { return "Point"; }
+
+ Point() : x(0), y(0), z(0) { }
+ Point(double ix, double iy, double iz) : x(ix), y(iy), z(iz) { }
+ Point(const Point& p) : x(p.x), y(p.y), z(p.z) { }
+ Point(const Vec3& v) : x(v.x), y(v.y), z(v.z) { }
+
+ operator Vec3() const { return Vec3((float) x, (float) y, (float) z); }
+
+ operator void*() const { return (void*) (x || y || z); }
+ int operator==(const Point& p) const { return x==p.x && y==p.y && z==p.z; }
+ int operator!=(const Point& p) const { return x!=p.x || y!=p.y || z!=p.z; }
+ Point operator+ (const Point& p) const { return Point(x+p.x, y+p.y, z+p.z); }
+ Point operator- (const Point& p) const { return Point(x-p.x, y-p.y, z-p.z); }
+ Point operator- () const { return Point(-x, -y, -z); }
+ Point operator* (double s) const { return Point(x*s, y*s, z*s); }
+ Point operator/ (double s) const { return Point(x/s, y/s, z/s); }
+ double operator*(const Point& p) const { return (x*p.x + y*p.y + z*p.z); }
+ Point operator* (const Matrix& m) const;
+
+ Point& operator= (const Point& p) { x =p.x; y =p.y; z =p.z; return *this; }
+ Point& operator+=(const Point& p) { x+=p.x; y+=p.y; z+=p.z; return *this; }
+ Point& operator-=(const Point& p) { x-=p.x; y-=p.y; z-=p.z; return *this; }
+ Point& operator*=(double s) { x*=s; y*=s; z*=s; return *this; }
+ Point& operator/=(double s) { x/=s; y/=s; z/=s; return *this; }
+
+ double length() const { return sqrt(x*x+y*y+z*z); }
+ double Normalize();
+ void SwapYZ() { double t = y; y = z; z = t; }
+ Point OtherHand() const { return Point(-x, z, y); }
+
+ void SetElement(int i, double v);
+
+ double dot(const Point& p) const { return (x*p.x + y*p.y + z*p.z); }
+ Point cross(const Point& p) const { return Point((y*p.z) - (z*p.y),
+ (z*p.x) - (x*p.z),
+ (x*p.y) - (y*p.x)); }
+
+ double x, y, z;
+};
+
+double ClosestApproachTime(const Point& loc1, const Point& vel1,
+ const Point& loc2, const Point& vel2);
+
+// +--------------------------------------------------------------------+
+
+struct Quaternion
+{
+ static const char* TYPENAME() { return "Quaternion"; }
+
+ Quaternion() : x(0), y(0), z(0), w(0) { }
+ Quaternion(double ix,
+ double iy,
+ double iz,
+ double iw) : x(ix), y(iy), z(iz), w(iw) { }
+ Quaternion(const Quaternion& q) : x(q.x), y(q.y), z(q.z), w(q.w) { }
+
+ int operator==(const Quaternion& q) const { return x==q.x && y==q.y && z==q.z && w==q.w; }
+ int operator!=(const Quaternion& q) const { return x!=q.x || y!=q.y || z!=q.z || w!=q.w; }
+
+ Quaternion operator+ (const Quaternion& q) const { return Quaternion(x+q.x, y+q.y, z+q.z, w+q.w); }
+ Quaternion operator- (const Quaternion& q) const { return Quaternion(x-q.x, y-q.y, z-q.z, w-q.w); }
+ Quaternion operator- () const { return Quaternion(-x, -y, -z, -w); }
+ Quaternion operator* (double s) const { return Quaternion(x*s, y*s, z*s, w*s); }
+ Quaternion operator/ (double s) const { return Quaternion(x/s, y/s, z/s, w/s); }
+
+ Quaternion& operator= (const Quaternion& q) { x =q.x; y =q.y; z =q.z; w =q.w; return *this; }
+ Quaternion& operator+=(const Quaternion& q) { x+=q.x; y+=q.y; z+=q.z; w+=q.w; return *this; }
+ Quaternion& operator-=(const Quaternion& q) { x-=q.x; y-=q.y; z-=q.z; w-=q.w; return *this; }
+ Quaternion& operator*=(double s) { x*=s; y*=s; z*=s; w*=s; return *this; }
+ Quaternion& operator/=(double s) { x/=s; y/=s; z/=s; w/=s; return *this; }
+
+ double length() const { return sqrt(x*x + y*y + z*z + w*w); }
+ double Normalize();
+
+ double x, y, z, w;
+};
+
+// +--------------------------------------------------------------------+
+
+struct Plane
+{
+ static const char* TYPENAME() { return "Plane"; }
+
+ Plane();
+ Plane(const Point& p0, const Point& p1, const Point& p2);
+ Plane(const Vec3& v0, const Vec3& v1, const Vec3& v2);
+
+ void Rotate(const Vec3& v0, const Matrix& m);
+ void Translate(const Vec3& v0);
+
+ float distance;
+ Vec3 normal;
+};
+
+// +--------------------------------------------------------------------+
+
+double DotProduct(const Point& a, const Point& b);
+void CrossProduct(const Point& a, const Point& b, Point& out);
+void MConcat(double in1[3][3], double in2[3][3], double out[3][3]);
+
+// +--------------------------------------------------------------------+
+
+int lines_intersect(
+ /* 1st line segment */ double x1, double y1, double x2, double y2,
+ /* 2nd line segment */ double x3, double y3, double x4, double y4,
+ /* intersect point */ double& x, double& y);
+
+// +--------------------------------------------------------------------+
+
+#endif Geometry_h
+
diff --git a/nGenEx/Graphic.cpp b/nGenEx/Graphic.cpp
new file mode 100644
index 0000000..7d63301
--- /dev/null
+++ b/nGenEx/Graphic.cpp
@@ -0,0 +1,170 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Graphic.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Abstract 3D Graphic Object
+*/
+
+#include "MemDebug.h"
+#include "Graphic.h"
+#include "Scene.h"
+#include "Projector.h"
+
+// +--------------------------------------------------------------------+
+
+int Graphic::id_key = 1;
+
+// +--------------------------------------------------------------------+
+
+Graphic::Graphic()
+ : id(id_key++), visible(true), loc(0.0f, 0.0f, 0.0f),
+ radius(0.0f), infinite(0), foreground(0), hidden(0), life(-1),
+ trans(false), shadow(false), luminous(false), depth(0.0f), scene(0)
+{
+ screen_rect.x = 0;
+ screen_rect.y = 0;
+ screen_rect.w = 0;
+ screen_rect.h = 0;
+
+ ZeroMemory(name, sizeof(name));
+ strcpy(name, "Graphic");
+}
+
+// +--------------------------------------------------------------------+
+
+Graphic::~Graphic()
+{ }
+
+int
+Graphic::operator < (const Graphic& g) const
+{
+ if (!infinite && g.infinite)
+ return 1;
+
+ else if (infinite && !g.infinite)
+ return 0;
+
+ double za = fabs(Depth());
+ double zb = fabs(g.Depth());
+
+ return (za < zb);
+}
+
+int
+Graphic::operator <= (const Graphic& g) const
+{
+ if (!infinite && g.infinite)
+ return 1;
+
+ else if (infinite && !g.infinite)
+ return 0;
+
+ double za = fabs(Depth());
+ double zb = fabs(g.Depth());
+
+ return (za <= zb);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Graphic::SetInfinite(bool b)
+{
+ infinite = (BYTE) b;
+
+ if (infinite)
+ depth = 1.0e9f;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Graphic::Nearer(Graphic* a, Graphic* b)
+{
+ if (a->depth < b->depth) return -1;
+ else if (a->depth == b->depth) return 0;
+ else return 1;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Graphic::Farther(Graphic* a, Graphic* b)
+{
+ if (a->depth > b->depth) return -1;
+ else if (a->depth == b->depth) return 0;
+ else return 1;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Graphic::Destroy()
+{
+ if (scene)
+ scene->DelGraphic(this);
+
+ delete this;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Graphic::CollidesWith(Graphic& o)
+{
+ Point delta_loc = loc - o.loc;
+
+ // bounding spheres test:
+ if (delta_loc.length() > radius + o.radius)
+ return 0;
+
+ return 1;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Graphic::CheckRayIntersection(Point Q, Point w, double len, Point& ipt,
+ bool treat_translucent_polys_as_solid)
+{
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Graphic::ProjectScreenRect(Projector* p)
+{
+ screen_rect.x = 2000;
+ screen_rect.y = 2000;
+ screen_rect.w = 0;
+ screen_rect.h = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Graphic::CheckVisibility(Projector& projector)
+{
+ if (projector.IsVisible( Location(), Radius()) &&
+ projector.ApparentRadius(Location(), Radius()) > 1) {
+
+ visible = true;
+ }
+ else {
+ visible = false;
+ screen_rect.x = 2000;
+ screen_rect.y = 2000;
+ screen_rect.w = 0;
+ screen_rect.h = 0;
+ }
+
+ return visible;
+}
diff --git a/nGenEx/Graphic.h b/nGenEx/Graphic.h
new file mode 100644
index 0000000..1416bf5
--- /dev/null
+++ b/nGenEx/Graphic.h
@@ -0,0 +1,139 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Graphic.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Abstract 3D Graphic Object
+*/
+
+#ifndef Graphic_h
+#define Graphic_h
+
+#include "Geometry.h"
+#include "Color.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+#define GRAPHIC_DESTROY(x) if (x) { x->Destroy(); x = 0; }
+
+// +--------------------------------------------------------------------+
+
+class Projector;
+class Light;
+class Scene;
+class Video;
+
+// +--------------------------------------------------------------------+
+
+class Graphic
+{
+public:
+ static const char* TYPENAME() { return "Graphic"; }
+
+ enum TYPE { OTHER, SOLID, SPRITE, BOLT, QUAD };
+
+ enum RENDER_FLAGS {
+ RENDER_SOLID = 0x0001,
+ RENDER_ALPHA = 0x0002,
+ RENDER_ADDITIVE = 0x0004,
+ RENDER_FIRST_LIGHT = 0x1000,
+ RENDER_ADD_LIGHT = 0x2000
+ };
+
+ Graphic();
+ virtual ~Graphic();
+
+ int operator == (const Graphic& g) const { return id == g.id; }
+ int operator < (const Graphic& g) const;
+ int operator <= (const Graphic& g) const;
+
+ // operations
+ virtual void Render(Video* video, DWORD flags) { }
+ virtual void Update() { }
+ virtual void SetOrientation(const Matrix& o) { }
+
+ virtual int CollidesWith(Graphic& o);
+
+ // accessors / mutators
+ int Identity() const { return id; }
+ const char* Name() const { return name; }
+ bool IsVisible() const { return visible; }
+ void SetVisible(bool v) { visible = v; }
+ float Radius() const { return radius; }
+
+ Point Location() const { return loc; }
+ virtual void MoveTo(const Point& p) { loc = p; }
+ virtual void TranslateBy(const Point& ref) { loc = loc - ref; }
+
+ virtual float Depth() const { return depth; }
+ virtual void SetDepth(float d) { depth = d; }
+ static int Nearer(Graphic* a, Graphic* b);
+ static int Farther(Graphic* a, Graphic* b);
+
+ virtual int IsInfinite() const { return infinite; }
+ virtual void SetInfinite(bool b);
+ virtual int IsForeground() const { return foreground; }
+ virtual void SetForeground(bool f){ foreground = f; }
+ virtual int IsBackground() const { return background; }
+ virtual void SetBackground(bool b){ background = b; }
+
+ virtual int Hidden() const { return hidden; }
+ virtual int Life() const { return life; }
+ virtual void Destroy();
+ virtual void Hide() { hidden = true; }
+ virtual void Show() { hidden = false; }
+ virtual bool Luminous() const { return luminous;}
+ virtual void SetLuminous(bool l) { luminous = l; }
+ virtual bool Translucent() const { return trans; }
+ virtual bool CastsShadow() const { return shadow; }
+ virtual void SetShadow(bool s) { shadow = s; }
+
+ virtual bool IsSolid() const { return false; }
+ virtual bool IsSprite() const { return false; }
+ virtual bool IsBolt() const { return false; }
+ virtual bool IsQuad() const { return false; }
+
+ virtual void ProjectScreenRect(Projector* p);
+ const Rect& ScreenRect() const { return screen_rect; }
+ virtual Scene* GetScene() const { return scene; }
+ virtual void SetScene(Scene*s) { scene = s; }
+
+ virtual int CheckRayIntersection(Point pt, Point vpn, double len, Point& ipt,
+ bool treat_translucent_polys_as_solid=true);
+
+ virtual bool CheckVisibility(Projector& projector);
+
+protected:
+ static int id_key;
+
+ int id;
+ Point loc;
+ float depth;
+ float radius;
+ int life;
+
+ bool visible;
+ bool infinite;
+ bool foreground;
+ bool background;
+ bool hidden;
+ bool trans;
+ bool shadow;
+ bool luminous;
+
+ Rect screen_rect;
+ Scene* scene;
+ char name[32];
+};
+
+// +--------------------------------------------------------------------+
+
+#endif Graphic_h
+
diff --git a/nGenEx/IA3D.H b/nGenEx/IA3D.H
new file mode 100644
index 0000000..9ecef73
--- /dev/null
+++ b/nGenEx/IA3D.H
@@ -0,0 +1,128 @@
+/*---------------------------------------------------------------------
+ *
+ * ia3d.h
+ *
+ *---------------------------------------------------------------------
+ *
+ * $Id: ia3d.h%v 1.1 1996/09/02 10:50:35 mike Exp mike $
+ *
+ *---------------------------------------------------------------------
+ *
+ * ia3d header file. It's the part the outside world needs to see.
+ *
+ *---------------------------------------------------------------------
+ *
+ * AUREAL SEMICONDUCTOR, INC. PROPRIETARY AND CONFIDENTIAL
+ * Copyright (c) 1996 Aureal Semiconductor, Inc. - All rights
+ * reserved.
+ *
+ *---------------------------------------------------------------------
+ */
+
+
+#ifndef _IA3D_H_
+#define _IA3D_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+// A3d Class ID! {D8F1EEE0-F634-11cf-8700-00A0245D918B}
+DEFINE_GUID(CLSID_A3d,
+0xd8f1eee0, 0xf634, 0x11cf, 0x87, 0x0, 0x0, 0xa0, 0x24, 0x5d, 0x91, 0x8b);
+
+// A3d Interface ID! {D8F1EEE1-F634-11cf-8700-00A0245D918B}
+DEFINE_GUID(IID_IA3d,
+0xd8f1eee1, 0xf634, 0x11cf, 0x87, 0x0, 0x0, 0xa0, 0x24, 0x5d, 0x91, 0x8b);
+
+
+// Bits for manipulating output modes
+
+// Values for bOutputMode
+#define OUTPUT_MODE_STEREO 0x00000001
+#define OUTPUT_MODE_QUAD 0x00000002
+
+// Values for FrontXtalkMode and bRearXtalkMode
+#define OUTPUT_HEADPHONES 0x00000001 // headphones
+#define OUTPUT_SPEAKERS_WIDE 0x00000002
+#define OUTPUT_SPEAKERS_NARROW 0x00000003
+
+// Values for Resource Management Mode
+#define A3D_RESOURCE_MODE_OFF 0x00000000
+#define A3D_RESOURCE_MODE_NOTIFY 0x00000001
+#define A3D_RESOURCE_MODE_DYNAMIC 0x00000002
+
+// Declare the IA3d Interface. It's not very complex at all.
+
+#undef INTERFACE
+#define INTERFACE IA3d
+
+typedef struct IA3d *LPIA3D;
+
+DECLARE_INTERFACE_(IA3d, IUnknown)
+{
+ // IUnknown
+ STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID * ppvObj) PURE;
+ STDMETHOD_(ULONG,AddRef) (THIS) PURE;
+ STDMETHOD_(ULONG,Release) (THIS) PURE;
+
+ // IA3d
+ STDMETHOD(SetOutputMode)(THIS_ DWORD dwFrontXtalkMode, DWORD dwBackXtalkMode, DWORD dwQuadMode) PURE;
+ STDMETHOD(GetOutputMode)(THIS_ DWORD *lpdwFrontXtalkMode, DWORD *lpdwBackXtalkMode, DWORD *lpdwQuadMode) PURE;
+
+ STDMETHOD(SetResourceManagerMode) (THIS_ DWORD ) PURE;
+ STDMETHOD(GetResourceManagerMode) (THIS_ DWORD *) PURE;
+
+ STDMETHOD(SetHFAbsorbFactor)(THIS_ FLOAT ) PURE;
+ STDMETHOD(GetHFAbsorbFactor)(THIS_ FLOAT *) PURE;
+
+};
+
+
+
+
+// The library function that gets things going. It returns an interface
+// pointer to DirectSound.
+
+#define A3D_OK 1 // A3dCreate returns this upon detection of A3D enabled hardware.
+
+_declspec (dllexport) HRESULT WINAPI
+A3dCreate(GUID * lpGUID, LPDIRECTSOUND * ppDS, IUnknown FAR *pUnkOuter );
+
+// Usefull Macros for C folks.
+
+#if !defined(__cplusplus) || defined(CINTERFACE)
+#define IA3d_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b)
+#define IA3d_AddRef(p) (p)->lpVtbl->AddRef(p)
+#define IA3d_Release(p) (p)->lpVtbl->Release(p)
+#define IA3d_SetOutputMode(p,a,b,c) (p)->lpVtbl->SetOutputMode(p,a,b,c)
+#define IA3d_GetOutputMode(p,a,b,c) (p)->lpVtbl->GetOutputMode(p,a,b,c)
+#define IA3d_SetResourceManagerMode(p,a) (p)->lpVtbl->SetResourceManagerMode(p,a)
+#define IA3d_GetResourceManagerMode(p,a) (p)->lpVtbl->GetResourceManagerMode(p,a)
+#define IA3d_SetHFAbsorbFactor(p,a) (p)->lpVtbl->SetHFAbsorbFactor(p,a)
+#define IA3d_GetHFAbsorbFactor(p,a) (p)->lpVtbl->GetHFAbsorbFactor(p,a)
+
+
+#else
+#define IA3d_QueryInterface(p,a,b) (p)->QueryInterface(a,b)
+#define IA3d_AddRef(p) (p)->AddRef()
+#define IA3d_Release(p) (p)->Release()
+#define IA3d_SetOutputMode(p,a,b,c) (p)->SetOutputMode(a,b,c)
+#define IA3d_GetOutputMode(p,a,b,c) (p)->GetOutputMode(a,b,c)
+#define IA3d_SetResourceManagerMode(p,a) (p)->SetResourceManagerMode(a)
+#define IA3d_GetResourceManagerMode(p,a) (p)->GetResourceManagerMode(a)
+#define IA3d_SetHFAbsorbFactor(p,a) (p)->SetHFAbsorbFactor(a)
+#define IA3d_GetHFAbsorbFactor(p,a) (p)->GetHFAbsorbFactor(a)
+
+#endif
+
+
+
+#ifdef __cplusplus
+};
+#endif
+
+
+
+#endif // _IA3D_H_
diff --git a/nGenEx/ImageBox.cpp b/nGenEx/ImageBox.cpp
new file mode 100644
index 0000000..36b866d
--- /dev/null
+++ b/nGenEx/ImageBox.cpp
@@ -0,0 +1,298 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: ImageBox.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ ImageBox class
+*/
+
+#include "MemDebug.h"
+#include "ImageBox.h"
+#include "Video.h"
+#include "Bitmap.h"
+#include "Font.h"
+#include "DataLoader.h"
+
+// +--------------------------------------------------------------------+
+
+ImageBox::ImageBox(ActiveWindow* p, int ax, int ay, int aw, int ah, DWORD aid)
+ : ActiveWindow(p->GetScreen(), ax, ay, aw, ah, aid, 0, p), blend_mode(Video::BLEND_ALPHA)
+{
+ picture_loc = 1;
+ text_align = DT_CENTER;
+
+ char buf[32];
+ sprintf(buf, "ImageBox %d", id);
+ desc = buf;
+}
+
+ImageBox::ImageBox(Screen* s, int ax, int ay, int aw, int ah, DWORD aid)
+ : ActiveWindow(s, ax, ay, aw, ah, aid, 0, 0), blend_mode(Video::BLEND_ALPHA)
+{
+ picture_loc = 1;
+ text_align = DT_CENTER;
+
+ char buf[32];
+ sprintf(buf, "ImageBox %d", id);
+ desc = buf;
+}
+
+// +--------------------------------------------------------------------+
+
+ImageBox::~ImageBox()
+{ }
+
+// +--------------------------------------------------------------------+
+
+void
+ImageBox::Draw()
+{
+ if (!shown)
+ return;
+
+ int x = 0;
+ int y = 0;
+ int w = rect.w;
+ int h = rect.h;
+ int img_w = picture.Width();
+ int img_h = picture.Height();
+
+ Rect box_rect(x,y,w,h);
+
+ // draw the picture (if any)
+ if (picture.Width()) {
+ Rect irect = CalcPictureRect();
+ DrawBitmap(irect.x,
+ irect.y,
+ irect.x + irect.w,
+ irect.y + irect.h,
+ &picture,
+ blend_mode);
+ }
+
+ // draw the border:
+ DrawStyleRect(0, 0, w, h, style);
+
+ // draw text here:
+ if (font && text.length()) {
+ int border_size = 4;
+
+ if (style & WIN_RAISED_FRAME && style & WIN_SUNK_FRAME)
+ border_size = 8;
+
+ Rect label_rect = CalcLabelRect(img_w,img_h);
+ int vert_space = label_rect.h;
+ int horz_space = label_rect.w;
+
+ DrawText(text.data(), 0, label_rect, DT_CALCRECT | DT_WORDBREAK | DT_CENTER);
+ vert_space = (vert_space - label_rect.h)/2;
+
+ label_rect.w = horz_space;
+
+ if (vert_space > 0)
+ label_rect.y += vert_space;
+
+ Color fore = ShadeColor(fore_color, 1);
+ font->SetColor(fore);
+ DrawText(text.data(), 0, label_rect, DT_WORDBREAK | DT_CENTER);
+ }
+}
+
+void
+ImageBox::DrawTabbedText()
+{
+ if (!shown)
+ return;
+
+ int x = 0;
+ int y = 0;
+ int w = rect.w;
+ int h = rect.h;
+ int img_w = picture.Width();
+ int img_h = picture.Height();
+
+ Rect box_rect(x,y,w,h);
+
+ // draw the picture (if any)
+ if (picture.Width()) {
+ Rect irect = CalcPictureRect();
+ DrawBitmap(irect.x,
+ irect.y,
+ irect.x + irect.w,
+ irect.y + irect.h,
+ &picture,
+ Video::BLEND_ALPHA);
+ }
+
+ ActiveWindow::DrawTabbedText();
+}
+
+Rect ImageBox::CalcLabelRect(int img_w, int img_h)
+{
+ // fit the text in the bevel:
+ Rect label_rect;
+ label_rect.x = 0;
+ label_rect.y = 0;
+ label_rect.w = rect.w;
+ label_rect.h = rect.h;
+
+ label_rect.Deflate(2,2);
+
+ // and around the picture, if any:
+ if (img_h != 0) {
+ switch (picture_loc) {
+ default:
+ case 0: // the four corner positions
+ case 2: // and the center position
+ case 4: // don't affect the text position
+ case 6:
+ case 8:
+ break;
+
+ case 1: // north
+ label_rect.y += img_h;
+ label_rect.h -= img_h;
+ break;
+
+ case 3: // west
+ label_rect.x += img_w;
+ label_rect.w -= img_w;
+ break;
+
+ case 5: // east
+ label_rect.w -= img_w;
+ break;
+
+ case 7: // south
+ label_rect.h -= img_h;
+ break;
+ }
+ }
+
+ return label_rect;
+}
+
+// +--------------------------------------------------------------------+
+
+Rect
+ImageBox::CalcPictureRect()
+{
+ if (target_rect.w > 0 && target_rect.h > 0)
+ return target_rect;
+
+ int w = rect.w;
+ int h = rect.h;
+ int img_w = picture.Width();
+ int img_h = picture.Height();
+
+ if (img_h > h) img_h = h-2;
+ if (img_w > w) img_w = w-2;
+
+ int img_x_offset = 0;
+ int img_y_offset = 0;
+
+ switch (picture_loc) {
+ default:
+ // TOP ROW:
+ case 0: break;
+
+ case 1: img_x_offset = (w/2-img_w/2);
+ break;
+
+ case 2: img_x_offset = w - img_w;
+ break;
+
+ // MIDDLE ROW:
+ case 3: img_y_offset = (h/2-img_h/2);
+ break;
+ case 4: img_x_offset = (w/2-img_w/2);
+ img_y_offset = (h/2-img_h/2);
+ break;
+ case 5: img_x_offset = w - img_w;
+ img_y_offset = (h/2-img_h/2);
+ break;
+
+ // BOTTOM ROW:
+ case 6:
+ img_y_offset = h - img_h;
+ break;
+ case 7: img_x_offset = (w/2-img_w/2);
+ img_y_offset = h - img_h;
+ break;
+ case 8: img_x_offset = w - img_w;
+ img_y_offset = h - img_h;
+ break;
+ }
+
+ Rect img_rect;
+ img_rect.x = img_x_offset;
+ img_rect.y = img_y_offset;
+ img_rect.w = img_w;
+ img_rect.h = img_h;
+
+ return img_rect;
+}
+
+// +--------------------------------------------------------------------+
+
+int ImageBox::OnMouseMove(int x, int y)
+{
+ return ActiveWindow::OnMouseMove(x,y);
+}
+
+int ImageBox::OnLButtonDown(int x, int y)
+{
+ return ActiveWindow::OnLButtonDown(x,y);
+}
+
+int ImageBox::OnLButtonUp(int x, int y)
+{
+ return ActiveWindow::OnLButtonUp(x,y);
+}
+
+int ImageBox::OnClick()
+{
+ return ActiveWindow::OnClick();
+}
+
+int ImageBox::OnMouseEnter(int mx, int my)
+{
+ return ActiveWindow::OnMouseEnter(mx, my);
+}
+
+int ImageBox::OnMouseExit(int mx, int my)
+{
+ return ActiveWindow::OnMouseExit(mx, my);
+}
+
+// +--------------------------------------------------------------------+
+
+void ImageBox::GetPicture(Bitmap& img) const
+{
+ img.CopyBitmap(picture);
+}
+
+void ImageBox::SetPicture(const Bitmap& img)
+{
+ picture.CopyBitmap(img);
+ picture.AutoMask();
+ picture.MakeTexture();
+}
+
+int ImageBox::GetPictureLocation() const
+{
+ return picture_loc;
+}
+
+void ImageBox::SetPictureLocation(int n)
+{
+ if (picture_loc != n && n >= 0 && n <= 8) {
+ picture_loc = n;
+ }
+}
diff --git a/nGenEx/ImageBox.h b/nGenEx/ImageBox.h
new file mode 100644
index 0000000..8a60cf7
--- /dev/null
+++ b/nGenEx/ImageBox.h
@@ -0,0 +1,71 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: ImageBox.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ ImageBox class
+*/
+
+#ifndef ImageBox_h
+#define ImageBox_h
+
+#include "Types.h"
+#include "ActiveWindow.h"
+#include "Bitmap.h"
+
+// +--------------------------------------------------------------------+
+
+class ImageBox : public ActiveWindow
+{
+public:
+ ImageBox(ActiveWindow* p, int ax, int ay, int aw, int ah, DWORD id=0);
+ ImageBox(Screen* s, int ax, int ay, int aw, int ah, DWORD id=0);
+ virtual ~ImageBox();
+
+ // Operations:
+ virtual void Draw();
+
+ // Event Target Interface:
+ virtual int OnMouseMove(int x, int y);
+ virtual int OnLButtonDown(int x, int y);
+ virtual int OnLButtonUp(int x, int y);
+ virtual int OnClick();
+ virtual int OnMouseEnter(int x, int y);
+ virtual int OnMouseExit(int x, int y);
+
+ // Property accessors:
+ int GetBlendMode() const { return blend_mode; }
+ void SetBlendMode(int blend) { blend_mode = blend; }
+ bool GetBorder() const { return border; }
+ void SetBorder(bool bNewValue) { border = bNewValue; }
+ Color GetBorderColor() const { return border_color; }
+ void SetBorderColor(Color c) { border_color = c; }
+ void GetPicture(Bitmap& img) const;
+ void SetPicture(const Bitmap& img);
+ int GetPictureLocation() const;
+ void SetPictureLocation(int nNewValue);
+ Rect GetTargetRect() const { return target_rect; }
+ void SetTargetRect(const Rect& r) { target_rect = r; }
+
+protected:
+ virtual void DrawTabbedText();
+
+ Rect CalcLabelRect(int img_w, int img_h);
+ Rect CalcPictureRect();
+
+ bool border;
+ Color border_color;
+ Bitmap picture;
+ int picture_loc;
+ int blend_mode;
+ Rect target_rect;
+};
+
+#endif ImageBox_h
+
diff --git a/nGenEx/ImgView.cpp b/nGenEx/ImgView.cpp
new file mode 100644
index 0000000..7cfce10
--- /dev/null
+++ b/nGenEx/ImgView.cpp
@@ -0,0 +1,79 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: ImgView.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Bitmap "billboard" Image View class
+*/
+
+#include "MemDebug.h"
+#include "ImgView.h"
+#include "Color.h"
+#include "Window.h"
+#include "Video.h"
+#include "Bitmap.h"
+#include "Screen.h"
+
+// +--------------------------------------------------------------------+
+
+ImgView::ImgView(Window* c, Bitmap* bmp)
+ : View(c), img(bmp), width(0), height(0), x_offset(0), y_offset(0),
+ blend(Video::BLEND_SOLID)
+{
+ if (img) {
+ width = img->Width();
+ height = img->Height();
+ }
+
+ if (width < c->Width())
+ x_offset = (c->Width() - width) / 2;
+
+ if (height < c->Height())
+ y_offset = (c->Height() - height) / 2;
+}
+
+ImgView::~ImgView()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ImgView::Refresh()
+{
+ if (img && width > 0 && height > 0)
+ window->DrawBitmap(x_offset,
+ y_offset,
+ x_offset + width,
+ y_offset + height,
+ img,
+ blend);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ImgView::SetPicture(Bitmap* bmp)
+{
+ img = bmp;
+ width = 0;
+ height = 0;
+ x_offset = 0;
+ y_offset = 0;
+
+ if (img) {
+ width = img->Width();
+ height = img->Height();
+ }
+
+ if (window) {
+ x_offset = (window->Width() - width) / 2;
+ y_offset = (window->Height() - height) / 2;
+ }
+}
diff --git a/nGenEx/ImgView.h b/nGenEx/ImgView.h
new file mode 100644
index 0000000..8de4b8c
--- /dev/null
+++ b/nGenEx/ImgView.h
@@ -0,0 +1,51 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: ImgView.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Bitmap "Billboard" View class
+*/
+
+#ifndef ImgView_h
+#define ImgView_h
+
+#include "Types.h"
+#include "View.h"
+
+// +--------------------------------------------------------------------+
+
+class Bitmap;
+
+// +--------------------------------------------------------------------+
+
+class ImgView : public View
+{
+public:
+ static const char* TYPENAME() { return "ImgView"; }
+
+ ImgView(Window* c, Bitmap* bmp);
+ virtual ~ImgView();
+
+ // Operations:
+ virtual void Refresh();
+
+ virtual Bitmap* GetPicture() const { return img; }
+ virtual void SetPicture(Bitmap* bmp);
+ virtual int GetBlend() const { return blend; }
+ virtual void SetBlend(int b) { blend = b; }
+
+protected:
+ Bitmap* img;
+ int x_offset, y_offset;
+ int width, height;
+ int blend;
+};
+
+#endif ImgView_h
+
diff --git a/nGenEx/Joystick.cpp b/nGenEx/Joystick.cpp
new file mode 100644
index 0000000..ba3b3ae
--- /dev/null
+++ b/nGenEx/Joystick.cpp
@@ -0,0 +1,914 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Joystick.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Joystick Input class
+*/
+
+#include "MemDebug.h"
+#include "Joystick.h"
+#include "MachineInfo.h"
+#include "Game.h"
+
+#define DIRECTINPUT_VERSION 0x0700
+
+#include <dinput.h>
+
+#define JOY_POVUPRIGHT 4500
+#define JOY_POVDNRIGHT 13500
+#define JOY_POVDNLEFT 22500
+#define JOY_POVUPLEFT 31500
+
+// +--------------------------------------------------------------------+
+// DIRECT INPUT SUPPORT
+
+const int MAX_DEVICES = 8;
+
+static LPDIRECTINPUT7 pdi = 0;
+static LPDIRECTINPUTDEVICE7 pdev = 0;
+static DIDEVICEINSTANCE devices[MAX_DEVICES];
+static int ndev = 0;
+static int idev = -1;
+static int strikes = 3;
+
+static Joystick* joystick = 0;
+
+void DirectInputError(const char* msg, HRESULT err);
+char* DIErrStr(HRESULT hr);
+void ReleaseDirectInput();
+
+// +--------------------------------------------------------------------+
+
+Joystick::Joystick()
+ : x(0), y(0), z(0), p(0), r(0), w(0), t(0)
+{
+ if (!joystick)
+ joystick = this;
+
+ select = 1;
+ rudder = 0;
+ throttle = 1;
+ sensitivity = 25;
+ dead_zone = 100;
+
+ for (int i = 0; i < MotionController::MaxActions; i++)
+ action[i] = false;
+
+ for (i = 0; i < KEY_MAP_SIZE; i++)
+ map[i] = 0;
+
+ for (i = 0; i < 4; i++) {
+ for (int j = 0; j < 4; j++) {
+ hat[i][j] = false;
+ }
+ }
+
+ map_axis[0] = KEY_JOY_AXIS_X;
+ map_axis[1] = KEY_JOY_AXIS_Y;
+ map_axis[2] = KEY_JOY_AXIS_RZ;
+ map_axis[3] = KEY_JOY_AXIS_S0;
+
+ inv_axis[0] = false;
+ inv_axis[1] = false;
+ inv_axis[2] = false;
+ inv_axis[3] = false;
+
+ if (MachineInfo::GetDirectXVersion() < MachineInfo::DX_7) {
+ Print("Joystick: DI7 not found, using multimedia library\n");
+ pdi = 0;
+ pdev = 0;
+ }
+
+ else if (!pdi) {
+ HRESULT hr = DirectInputCreateEx(Game::GetHINST(),
+ DIRECTINPUT_VERSION,
+ IID_IDirectInput7,
+ (void**)&pdi,
+ NULL);
+ if FAILED(hr) {
+ DirectInputError("Failed to initialize DI7", hr);
+ pdi = 0;
+ pdev = 0;
+ }
+ else {
+ Print("Joystick: initialized DI7 pdi = %08x\n", (DWORD) pdi);
+ }
+ }
+}
+
+Joystick::~Joystick()
+{
+ ReleaseDirectInput();
+ joystick = 0;
+}
+
+void ReleaseDirectInput()
+{
+ if (pdev) {
+ pdev->Unacquire();
+ pdev->Release();
+ pdev = 0;
+ }
+
+ if (pdi) {
+ pdi->Release();
+ pdi = 0;
+ }
+}
+
+Joystick* Joystick::GetInstance()
+{
+ return joystick;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Joystick::MapKeys(KeyMapEntry* mapping, int nkeys)
+{
+ ZeroMemory(map, sizeof(map));
+
+ for (int i = 0; i < nkeys; i++) {
+ KeyMapEntry k = mapping[i];
+
+ if (k.act >= KEY_MAP_FIRST && k.act < KEY_MAP_LAST) {
+ if (k.act == KEY_JOY_SENSE)
+ sensitivity = k.key;
+
+ else if (k.act == KEY_JOY_DEAD_ZONE)
+ dead_zone = k.key;
+
+ else if (k.act == KEY_JOY_SWAP)
+ swapped = k.key;
+
+ else if (k.act == KEY_JOY_RUDDER)
+ rudder = k.key;
+
+ else if (k.act == KEY_JOY_THROTTLE)
+ throttle = k.key;
+
+ else if (k.act == KEY_JOY_SELECT)
+ select = k.key;
+
+
+ else if (k.act == KEY_AXIS_YAW)
+ map_axis[0] = k.key;
+
+ else if (k.act == KEY_AXIS_PITCH)
+ map_axis[1] = k.key;
+
+ else if (k.act == KEY_AXIS_ROLL)
+ map_axis[2] = k.key;
+
+ else if (k.act == KEY_AXIS_THROTTLE)
+ map_axis[3] = k.key;
+
+
+ else if (k.act == KEY_AXIS_YAW_INVERT)
+ inv_axis[0] = k.key ? true : false;
+
+ else if (k.act == KEY_AXIS_PITCH_INVERT)
+ inv_axis[1] = k.key ? true : false;
+
+ else if (k.act == KEY_AXIS_ROLL_INVERT)
+ inv_axis[2] = k.key ? true : false;
+
+ else if (k.act == KEY_AXIS_THROTTLE_INVERT)
+ inv_axis[3] = k.key ? true : false;
+
+ else if (k.key >= KEY_JOY_1 && k.key <= KEY_JOY_32)
+ map[k.act] = k.key;
+
+ else if (k.alt >= KEY_JOY_1 && k.alt <= KEY_JOY_32)
+ map[k.act] = k.alt;
+
+ else if (k.joy >= KEY_JOY_1 && k.joy <= KEY_JOY_32)
+ map[k.act] = k.joy;
+
+ else if (k.key >= KEY_POV_0_UP && k.key <= KEY_POV_3_RIGHT)
+ map[k.act] = k.key;
+
+ else if (k.alt >= KEY_POV_0_UP && k.alt <= KEY_POV_3_RIGHT)
+ map[k.act] = k.alt;
+
+ else if (k.joy >= KEY_POV_0_UP && k.joy <= KEY_POV_3_RIGHT)
+ map[k.act] = k.joy;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+static inline double sqr(double a) { return a*a; }
+
+BOOL FAR PASCAL EnumJoystick(LPCDIDEVICEINSTANCE pdinst, LPVOID pvSelect)
+{
+ CopyMemory(&devices[ndev++], pdinst, pdinst->dwSize);
+
+ ::Print("EnumJoystick %d: '%s'\n", ndev, pdinst->tszInstanceName);
+ ::Print(" guid: {%08x-%04x-%04x-%02x%02x%02x%02x%02x%02x} \n",
+ (DWORD) pdinst->guidInstance.Data1,
+ (WORD) pdinst->guidInstance.Data2,
+ (WORD) pdinst->guidInstance.Data3,
+ (BYTE) pdinst->guidInstance.Data4[0],
+ (BYTE) pdinst->guidInstance.Data4[1],
+ (BYTE) pdinst->guidInstance.Data4[2],
+ (BYTE) pdinst->guidInstance.Data4[3],
+ (BYTE) pdinst->guidInstance.Data4[4],
+ (BYTE) pdinst->guidInstance.Data4[5]);
+
+ if (ndev < MAX_DEVICES)
+ return DIENUM_CONTINUE;
+
+ return DIENUM_STOP;
+}
+
+bool CreateDevice(int select)
+{
+ if (!pdi || ndev < select)
+ return false;
+
+ LPCDIDEVICEINSTANCE pdinst = &devices[select-1];
+
+ ::Print("Joystick CreateDevice(%d)\n", select);
+ ::Print(" name: %s\n\n", pdinst->tszInstanceName);
+
+ // release current device before trying to get another:
+ if (pdev) {
+ pdev->Unacquire();
+ pdev->Release();
+ pdev = 0;
+ }
+
+ HRESULT hr = DI_OK;
+ // Create the DirectInput joystick device:
+ hr = pdi->CreateDeviceEx(pdinst->guidInstance,
+ IID_IDirectInputDevice7,
+ (void**)&pdev,
+ NULL);
+
+ if (hr != DI_OK || pdev == 0) {
+ DirectInputError("Create Device Ex failed", hr);
+ return false;
+ }
+
+ // Set the data format:
+ hr = pdev->SetDataFormat(&c_dfDIJoystick);
+
+ if (hr != DI_OK) {
+ DirectInputError("Set Data Format failed", hr);
+ pdev->Release();
+ pdev = 0;
+ return false;
+ }
+
+ // Set the coop level:
+ hr = pdev->SetCooperativeLevel(Game::GetHWND(), DISCL_NONEXCLUSIVE | DISCL_FOREGROUND);
+
+ if (hr != DI_OK) {
+ DirectInputError("Set Cooperative Level failed", hr);
+ pdev->Release();
+ return false;
+ }
+
+ // Set data ranges
+ DIPROPRANGE diprg;
+ diprg.lMin = -32768;
+ diprg.lMax = +32768;
+
+ diprg.diph.dwSize = sizeof(diprg);
+ diprg.diph.dwHeaderSize = sizeof(diprg.diph);
+ diprg.diph.dwObj = DIJOFS_X;
+ diprg.diph.dwHow = DIPH_BYOFFSET;
+ pdev->SetProperty(DIPROP_RANGE, &diprg.diph);
+
+ diprg.diph.dwObj = DIJOFS_Y;
+ pdev->SetProperty(DIPROP_RANGE, &diprg.diph);
+
+ diprg.diph.dwObj = DIJOFS_Z;
+ pdev->SetProperty(DIPROP_RANGE, &diprg.diph);
+
+ diprg.diph.dwObj = DIJOFS_RX;
+ pdev->SetProperty(DIPROP_RANGE, &diprg.diph);
+
+ diprg.diph.dwObj = DIJOFS_RY;
+ pdev->SetProperty(DIPROP_RANGE, &diprg.diph);
+
+ diprg.diph.dwObj = DIJOFS_RZ;
+ pdev->SetProperty(DIPROP_RANGE, &diprg.diph);
+
+ diprg.diph.dwObj = DIJOFS_SLIDER(0);
+ pdev->SetProperty(DIPROP_RANGE, &diprg.diph);
+
+ diprg.diph.dwObj = DIJOFS_SLIDER(1);
+ pdev->SetProperty(DIPROP_RANGE, &diprg.diph);
+
+ ::Print("Created joystick %d (pdev = %08x)\n", select, (DWORD) pdev);
+ idev = select;
+ return true;
+}
+
+void
+Joystick::EnumerateDevices()
+{
+ if (!pdi) {
+ Print("Joystick: no DI7, unable to enumerate devices\n");
+ ndev = 0;
+ }
+
+ else if (ndev < 1) {
+ Print("Joystick: preparing to enumerate devices\n");
+
+ ndev = 0;
+ HRESULT hr =
+ pdi->EnumDevices(DIDEVTYPE_JOYSTICK,
+ EnumJoystick,
+ (LPVOID) 0,
+ DIEDFL_ATTACHEDONLY);
+
+ if (FAILED(hr)) {
+ DirectInputError("Failed to enumerate devices", hr);
+ ReleaseDirectInput();
+ }
+
+ else if (ndev < 1) {
+ Print("Joystick: no devices found\n");
+ ReleaseDirectInput();
+ }
+ }
+}
+
+int
+Joystick::NumDevices()
+{
+ return ndev;
+}
+
+const char*
+Joystick::GetDeviceName(int i)
+{
+ if (i >= 0 && i < ndev)
+ return devices[i].tszInstanceName;
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+static DIJOYSTATE joystate;
+static JOYINFOEX joyinfo;
+
+int
+Joystick::ReadRawAxis(int a)
+{
+ if (!joystick)
+ return 0;
+
+ int result = 0;
+
+ if (pdev) {
+ switch (a) {
+ case KEY_JOY_AXIS_X: result = joystate.lX; break;
+ case KEY_JOY_AXIS_Y: result = joystate.lY; break;
+ case KEY_JOY_AXIS_Z: result = joystate.lZ; break;
+ case KEY_JOY_AXIS_RX: result = joystate.lRx; break;
+ case KEY_JOY_AXIS_RY: result = joystate.lRy; break;
+ case KEY_JOY_AXIS_RZ: result = joystate.lRz; break;
+ case KEY_JOY_AXIS_S0: result = joystate.rglSlider[0]; break;
+ case KEY_JOY_AXIS_S1: result = joystate.rglSlider[1]; break;
+ }
+ }
+
+ else {
+ switch (a) {
+ case KEY_JOY_AXIS_X:
+ if (joyinfo.dwFlags & JOY_RETURNX)
+ result = joyinfo.dwXpos;
+ break;
+
+ case KEY_JOY_AXIS_Y:
+ if (joyinfo.dwFlags & JOY_RETURNY)
+ result = joyinfo.dwYpos;
+ break;
+
+ case KEY_JOY_AXIS_Z:
+ if (joyinfo.dwFlags & JOY_RETURNZ)
+ result = joyinfo.dwZpos;
+ break;
+
+ case KEY_JOY_AXIS_RZ:
+ if (joyinfo.dwFlags & JOY_RETURNR)
+ result = joyinfo.dwRpos;
+ break;
+ }
+ }
+
+ return result;
+}
+
+double
+Joystick::ReadAxisDI(int a)
+{
+ if (a < 0 || a > 3)
+ return 0;
+
+ double result = 0;
+
+ switch (map_axis[a]) {
+ case KEY_JOY_AXIS_X: result = joystate.lX; break;
+ case KEY_JOY_AXIS_Y: result = joystate.lY; break;
+ case KEY_JOY_AXIS_Z: result = joystate.lZ; break;
+ case KEY_JOY_AXIS_RX: result = joystate.lRx; break;
+ case KEY_JOY_AXIS_RY: result = joystate.lRy; break;
+ case KEY_JOY_AXIS_RZ: result = joystate.lRz; break;
+ case KEY_JOY_AXIS_S0: result = joystate.rglSlider[0]; break;
+ case KEY_JOY_AXIS_S1: result = joystate.rglSlider[1]; break;
+ }
+
+ if (a < 3) {
+ // ignore small movements:
+ if (result > dead_zone) result -= dead_zone;
+ else if (result < -dead_zone) result += dead_zone;
+ else result = 0;
+
+ double scale = 1.0 / (32768.0-dead_zone);
+
+ if (result >= 0)
+ result = sqr(result * scale);
+ else
+ result = sqr(result * scale) * -1.0;
+
+ if (inv_axis[a])
+ result = -result;
+ }
+ else {
+ result = (result+32768.0) / 65536.0;
+
+ if (inv_axis[a])
+ result = 1 - result;
+ }
+
+
+ return result;
+}
+
+double
+Joystick::ReadAxisMM(int a)
+{
+ if (a < 0 || a > 3)
+ return 0;
+
+ double result = 0;
+
+ switch (map_axis[a]) {
+ case KEY_JOY_AXIS_X:
+ if (joyinfo.dwFlags & JOY_RETURNX)
+ result = joyinfo.dwXpos - 32768;
+ break;
+
+ case KEY_JOY_AXIS_Y:
+ if (joyinfo.dwFlags & JOY_RETURNY)
+ result = joyinfo.dwYpos - 32768;
+ break;
+
+ case KEY_JOY_AXIS_Z:
+ if (joyinfo.dwFlags & JOY_RETURNZ)
+ result = joyinfo.dwZpos - 32768;
+ break;
+
+ case KEY_JOY_AXIS_RZ:
+ if (joyinfo.dwFlags & JOY_RETURNR)
+ result = joyinfo.dwRpos - 32768;
+ break;
+ }
+
+ if (a < 3) {
+ // ignore small movements:
+ if (result > dead_zone) result -= dead_zone;
+ else if (result < -dead_zone) result += dead_zone;
+ else result = 0;
+
+ double scale = 1.0 / (32768.0-dead_zone);
+
+ if (result >= 0)
+ result = sqr(result * scale);
+ else
+ result = sqr(result * scale) * -1.0;
+
+ if (inv_axis[a])
+ result = -result;
+ }
+ else {
+ result = (result+32768.0) / 65536.0;
+
+ if (inv_axis[a])
+ result = 1 - result;
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Joystick::Acquire()
+{
+ t = x = y = z = p = r = w = 0;
+ for (int i = 0; i < MotionController::MaxActions; i++)
+ action[i] = false;
+
+ for (i = 0; i < 4; i++)
+ for (int j = 0; j < 4; j++)
+ hat[i][j] = false;
+
+ if (select == 0)
+ return;
+
+ //============================================================
+ //
+ // FIRST TRY DIRECT INPUT
+
+ bool acquired = false;
+
+ if (pdi) {
+ if (idev != select) {
+ if (ndev < 1)
+ EnumerateDevices();
+
+ if (CreateDevice(select))
+ pdev->Acquire();
+ }
+
+ if (pdev) {
+ HRESULT hr = 0;
+
+ hr = pdev->Poll();
+ hr = pdev->GetDeviceState(sizeof(joystate), &joystate);
+
+ if (hr == DIERR_INPUTLOST) {
+ pdev->Acquire();
+
+ hr = pdev->Poll();
+ hr = pdev->GetDeviceState(sizeof(joystate), &joystate);
+
+ if (FAILED(hr)) {
+ strikes--;
+ ::Print("Joystick could not re-acquire joystick (%08x)\n", hr);
+
+ // give up before you hurt yourself:
+ if (strikes <= 0) {
+ ReleaseDirectInput();
+ ndev = 0;
+ select = 0;
+ }
+
+ return;
+ }
+ }
+
+ for (int i = 0; i < 32; i++)
+ action[i] = (joystate.rgbButtons[i] & 0x80) != 0;
+
+ double joy_x = ReadAxisDI(0);
+ double joy_y = ReadAxisDI(1);
+ double joy_r = rudder ? ReadAxisDI(2) : 0;
+ double joy_t = throttle ? ReadAxisDI(3) : 0;
+
+ int joy_p = joystate.rgdwPOV[0];
+
+ ProcessAxes(joy_x, joy_y, joy_r, joy_t);
+
+ for (i = 0; i < 4; i++)
+ ProcessHat(i, joystate.rgdwPOV[i]);
+
+ acquired = true;
+ }
+ }
+
+ //============================================================
+ //
+ // THEN TRY WINDOWS MULTIMEDIA LIBRARY
+
+ if (!acquired) {
+ memset(&joyinfo, 0, sizeof(JOYINFOEX));
+ joyinfo.dwSize = sizeof(JOYINFOEX);
+ joyinfo.dwFlags = JOY_RETURNALL;
+
+ HRESULT hr = 0;
+
+ if (select == 1)
+ hr = joyGetPosEx(JOYSTICKID1, &joyinfo);
+
+ else if (select == 2)
+ hr = joyGetPosEx(JOYSTICKID2, &joyinfo);
+
+ if (hr != 0) {
+ Print("\nJoystick::Acquire() joyGetPosEx %d failed (err=%08x)\n\n", select, hr);
+ select = 0;
+ }
+
+ action[0] = (joyinfo.dwButtons & JOY_BUTTON1) ? true : false;
+ action[1] = (joyinfo.dwButtons & JOY_BUTTON2) ? true : false;
+ action[2] = (joyinfo.dwButtons & JOY_BUTTON3) ? true : false;
+ action[3] = (joyinfo.dwButtons & JOY_BUTTON4) ? true : false;
+
+ double joy_x = ReadAxisMM(0);
+ double joy_y = ReadAxisMM(1);
+ double joy_r = rudder ? ReadAxisMM(2) : 0;
+ double joy_t = throttle ? ReadAxisMM(3) : 0;
+
+ ProcessAxes(joy_x, joy_y, joy_r, joy_t);
+ ProcessHat(0, joyinfo.dwPOV);
+ }
+
+ // lateral translations:
+ if (KeyDownMap(KEY_PLUS_Y)) y = 1;
+ else if (KeyDownMap(KEY_MINUS_Y)) y = -1;
+
+ if (KeyDownMap(KEY_PLUS_Z)) z = 1;
+ else if (KeyDownMap(KEY_MINUS_Z)) z = -1;
+
+ if (KeyDownMap(KEY_MINUS_X)) x = -1;
+ else if (KeyDownMap(KEY_PLUS_X)) x = 1;
+
+ // button-based turns:
+ const double steps=10;
+ static double p1=0, r1=0, w1=0;
+
+ // if roll and yaw are swapped --------------------------
+ if (swapped) {
+ // yaw:
+ if (KeyDownMap(KEY_ROLL_LEFT)) { if (w1<steps) w1+=1; w = -sqr(w1/steps); }
+ else if (KeyDownMap(KEY_ROLL_RIGHT)) { if (w1<steps) w1+=1; w = sqr(w1/steps); }
+
+ // roll:
+ if (KeyDownMap(KEY_YAW_LEFT)) { if (r1<steps) r1+=1; r = sqr(r1/steps); }
+ else if (KeyDownMap(KEY_YAW_RIGHT)) { if (r1<steps) r1+=1; r = -sqr(r1/steps); }
+ else w1 = 0;
+ }
+
+ // else roll and yaw are NOT swapped ---------------------
+ else {
+ // roll:
+ if (KeyDownMap(KEY_ROLL_LEFT)) { if (r1<steps) r1+=1; r = sqr(r1/steps); }
+ else if (KeyDownMap(KEY_ROLL_RIGHT)) { if (r1<steps) r1+=1; r = -sqr(r1/steps); }
+
+ // yaw left-right
+ if (KeyDownMap(KEY_YAW_LEFT)) { if (w1<steps) w1+=1; w = -sqr(w1/steps); }
+ else if (KeyDownMap(KEY_YAW_RIGHT)) { if (w1<steps) w1+=1; w = sqr(w1/steps); }
+ else w1 = 0;
+ }
+
+ // pitch --------------------------------------------------
+ if (KeyDownMap(KEY_PITCH_UP)) { if (p1<steps) p1+=1; p = -sqr(p1/steps); }
+ else if (KeyDownMap(KEY_PITCH_DOWN)) { if (p1<steps) p1+=1; p = sqr(p1/steps); }
+ else p1 = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Joystick::ProcessAxes(double joy_x, double joy_y, double joy_r, double joy_t)
+{
+ int roll_enable = 0;
+
+ joy_y *= -1;
+ joy_t = 1 - joy_t;
+
+ if (map[KEY_ROLL_ENABLE])
+ roll_enable = action[map[KEY_ROLL_ENABLE] - KEY_JOY_1];
+
+ // if roll and yaw are swapped --------------------------
+ if (swapped) {
+ if (roll_enable) {
+ w = joy_x;
+ r = joy_r;
+ }
+ else {
+ w = joy_r;
+ r = -joy_x;
+ }
+ }
+
+ // else roll and yaw are NOT swapped ---------------------
+ else {
+ if (roll_enable) {
+ w = joy_r;
+ r = joy_x;
+ }
+ else {
+ w = joy_x;
+ r = -joy_r;
+ }
+ }
+
+ p = joy_y;
+
+ // read throttle:
+ if (throttle) {
+ static double init_throttle = -1;
+ static bool latch_throttle = false;
+
+ if (init_throttle < 0)
+ init_throttle = joy_t;
+ else if (init_throttle != joy_t)
+ latch_throttle = true;
+
+ if (latch_throttle)
+ t = joy_t;
+ else
+ t = 0;
+ }
+ else {
+ t = 0;
+ }
+}
+
+void
+Joystick::ProcessHat(int i, DWORD joy_pov)
+{
+ if (i < 0 || i > 3) return;
+
+ if (LOWORD(joy_pov) == 0xFFFF)
+ return;
+
+ switch (joy_pov) {
+ case JOY_POVFORWARD: hat[i][0] = true; break;
+ case JOY_POVBACKWARD: hat[i][1] = true; break;
+ case JOY_POVLEFT: hat[i][2] = true; break;
+ case JOY_POVRIGHT: hat[i][3] = true; break;
+
+ case JOY_POVUPRIGHT: hat[i][0] = true;
+ hat[i][3] = true; break;
+
+ case JOY_POVDNRIGHT: hat[i][1] = true;
+ hat[i][3] = true; break;
+
+ case JOY_POVDNLEFT: hat[i][1] = true;
+ hat[i][2] = true; break;
+
+ case JOY_POVUPLEFT: hat[i][0] = true;
+ hat[i][2] = true; break;
+
+ default: break;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool Joystick::KeyDown(int key)
+{
+ if (!joystick)
+ return false;
+
+ if (key >= KEY_JOY_1 && key <= KEY_JOY_32)
+ return joystick->action[key - KEY_JOY_1];
+
+ else if (key >= KEY_POV_0_UP && key <= KEY_POV_0_RIGHT)
+ return joystick->hat[0][key - KEY_POV_0_UP];
+
+ else if (key >= KEY_POV_1_UP && key <= KEY_POV_1_RIGHT)
+ return joystick->hat[1][key - KEY_POV_1_UP];
+
+ else if (key >= KEY_POV_2_UP && key <= KEY_POV_2_RIGHT)
+ return joystick->hat[2][key - KEY_POV_2_UP];
+
+ else if (key >= KEY_POV_3_UP && key <= KEY_POV_3_RIGHT)
+ return joystick->hat[3][key - KEY_POV_3_UP];
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool Joystick::KeyDownMap(int key)
+{
+ if (!joystick)
+ return false;
+
+ if (key >= KEY_MAP_FIRST && key <= KEY_MAP_LAST && joystick->map[key])
+ return KeyDown(joystick->map[key]);
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+int Joystick::GetAxisMap(int n)
+{
+ if (!joystick || n < 0 || n > 3)
+ return -1;
+
+ return joystick->map_axis[n];
+}
+
+int Joystick::GetAxisInv(int n)
+{
+ if (!joystick || n < 0 || n > 3)
+ return -1;
+
+ return joystick->inv_axis[n];
+}
+
+// +--------------------------------------------------------------------+
+
+void
+DirectInputError(const char* msg, HRESULT err)
+{
+ static int report = 50;
+ if (report > 0)
+ report--;
+ else
+ return;
+
+ Print(" DirectInput7: %s. [%s]\n", msg, DIErrStr(err));
+}
+
+static char errstrbuf[128];
+
+char* DIErrStr(HRESULT hr)
+{
+ switch (hr) {
+ default:
+ sprintf(errstrbuf, "Unrecognized error value = %08x.", hr);
+ return errstrbuf;
+
+ case DI_OK:
+ return "No error.";
+
+ case DI_BUFFEROVERFLOW:
+ return "The device buffer overflowed and some input was lost. This value is equal to the S_FALSE standard COM return value.";
+ case DI_DOWNLOADSKIPPED:
+ return "The parameters of the effect were successfully updated, but the effect could not be downloaded because the associated device was not acquired in exclusive mode.";
+ case DI_EFFECTRESTARTED:
+ return "The effect was stopped, the parameters were updated, and the effect was restarted.";
+ case DI_POLLEDDEVICE:
+ return "The device is a polled device. As a result, device buffering does not collect any data and event notifications is not signaled until the IDirectInputDevice7::Poll method is called.";
+ case DI_TRUNCATED:
+ return "The parameters of the effect were successfully updated, but some of them were beyond the capabilities of the device and were truncated to the nearest supported value.";
+ case DI_TRUNCATEDANDRESTARTED:
+ return "Equal to DI_EFFECTRESTARTED | DI_TRUNCATED";
+ case DIERR_ACQUIRED:
+ return "The operation cannot be performed while the device is acquired.";
+ case DIERR_ALREADYINITIALIZED:
+ return "This object is already initialized";
+ case DIERR_BADDRIVERVER:
+ return "The object could not be created due to an incompatible driver version or mismatched or incomplete driver components.";
+ case DIERR_BETADIRECTINPUTVERSION:
+ return "The application was written for an unsupported prerelease version of DirectInput.";
+ case DIERR_DEVICEFULL:
+ return "The device is full.";
+ case DIERR_DEVICENOTREG:
+ return "The device or device instance is not registered with DirectInput. This value is equal to the REGDB_E_CLASSNOTREG standard COM return value.";
+ case DIERR_EFFECTPLAYING:
+ return "The parameters were updated in memory but were not downloaded to the device because the device does not support updating an effect while it is still playing.";
+ case DIERR_HASEFFECTS:
+ return "The device cannot be reinitialized because there are still effects attached to it.";
+ case DIERR_GENERIC:
+ return "An undetermined error occurred inside the DirectInput subsystem. This value is equal to the E_FAIL standard COM return value.";
+ case DIERR_HANDLEEXISTS:
+ return "The device already has an event notification associated with it. This value is equal to the E_ACCESSDENIED standard COM return value.";
+ case DIERR_INCOMPLETEEFFECT:
+ return "The effect could not be downloaded because essential information is missing. For example, no axes have been associated with the effect, or no type-specific information has been supplied.";
+ case DIERR_INPUTLOST:
+ return "Access to the input device has been lost. It must be reacquired.";
+ case DIERR_INVALIDPARAM:
+ return "An invalid parameter was passed to the returning function, or the object was not in a state that permitted the function to be called. This value is equal to the E_INVALIDARG standard COM return value.";
+ case DIERR_MOREDATA:
+ return "Not all the requested information fit into the buffer.";
+ case DIERR_NOAGGREGATION:
+ return "This object does not support aggregation.";
+ case DIERR_NOINTERFACE:
+ return "The specified interface is not supported by the object. This value is equal to the E_NOINTERFACE standard COM return value.";
+ case DIERR_NOTACQUIRED:
+ return "The operation cannot be performed unless the device is acquired.";
+ case DIERR_NOTBUFFERED:
+ return "The device is not buffered. Set the DIPROP_BUFFERSIZE property to enable buffering.";
+ case DIERR_NOTDOWNLOADED:
+ return "The effect is not downloaded.";
+ case DIERR_NOTEXCLUSIVEACQUIRED:
+ return "The operation cannot be performed unless the device is acquired in DISCL_EXCLUSIVE mode.";
+ case DIERR_NOTFOUND:
+ return "The requested object does not exist.";
+ case DIERR_NOTINITIALIZED:
+ return "This object has not been initialized.";
+ case DIERR_OLDDIRECTINPUTVERSION:
+ return "The application requires a newer version of DirectInput.";
+ case DIERR_OUTOFMEMORY:
+ return "The DirectInput subsystem could not allocate sufficient memory to complete the call. This value is equal to the E_OUTOFMEMORY standard COM return value.";
+ case DIERR_REPORTFULL:
+ return "More information was requested to be sent than can be sent to the device.";
+ case DIERR_UNPLUGGED:
+ return "The operation could not be completed because the device is not plugged in.";
+ case DIERR_UNSUPPORTED:
+ return "The function called is not supported at this time. This value is equal to the E_NOTIMPL standard COM return value.";
+ }
+}
+
diff --git a/nGenEx/Joystick.h b/nGenEx/Joystick.h
new file mode 100644
index 0000000..e80a06b
--- /dev/null
+++ b/nGenEx/Joystick.h
@@ -0,0 +1,82 @@
+/* Project nGen
+ John DiCamillo
+ Copyright © 1997-2002. All Rights Reserved.
+
+ SUBSYSTEM: nGen.lib
+ FILE: Joystick.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Joystick Input class
+*/
+
+#ifndef Joystick_h
+#define Joystick_h
+
+#include "MotionController.h"
+
+// +--------------------------------------------------------------------+
+
+class Joystick : public MotionController
+{
+public:
+ static const char* TYPENAME() { return "Joystick"; }
+
+ Joystick();
+ virtual ~Joystick();
+
+ // setup
+ virtual void MapKeys(KeyMapEntry* mapping, int nkeys);
+
+ // sample the physical device
+ virtual void Acquire();
+
+ // translations
+ virtual double X() { return x; }
+ virtual double Y() { return y; }
+ virtual double Z() { return z; }
+
+ // rotations
+ virtual double Pitch() { return p; }
+ virtual double Roll() { return r; }
+ virtual double Yaw() { return w; }
+ virtual int Center() { return 0; }
+
+ // throttle
+ virtual double Throttle() { return t; }
+ virtual void SetThrottle(double throttle) { t = throttle; }
+
+ // actions
+ virtual int Action(int n) { return action[n]; }
+ virtual int ActionMap(int n) { return KeyDownMap(n); }
+
+ static bool KeyDown(int key);
+ static bool KeyDownMap(int key);
+
+ static Joystick* GetInstance();
+ static void EnumerateDevices();
+ static int NumDevices();
+ static const char* GetDeviceName(int i);
+
+ static int ReadRawAxis(int axis);
+ static int GetAxisMap(int n);
+ static int GetAxisInv(int n);
+
+protected:
+ double ReadAxisDI(int axis);
+ double ReadAxisMM(int axis);
+ void ProcessAxes(double joy_x, double joy_y, double joy_r, double joy_t);
+ void ProcessHat(int i, DWORD joy_pov);
+
+ double x,y,z,p,r,w,t;
+ bool action[MotionController::MaxActions];
+ bool hat[4][4];
+ int map[KEY_MAP_SIZE];
+ int map_axis[4];
+ bool inv_axis[4];
+};
+
+#endif Joystick_h
+
diff --git a/nGenEx/Keyboard.cpp b/nGenEx/Keyboard.cpp
new file mode 100644
index 0000000..52e201f
--- /dev/null
+++ b/nGenEx/Keyboard.cpp
@@ -0,0 +1,217 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Keyboard.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Keyboard Input class
+*/
+
+#include "MemDebug.h"
+#include "Keyboard.h"
+#include "Game.h"
+
+// +--------------------------------------------------------------------+
+
+static Keyboard* instance = 0;
+int Keyboard::map[KEY_MAP_SIZE];
+int Keyboard::alt[KEY_MAP_SIZE];
+
+Keyboard::Keyboard()
+ : x(0), y(0), z(0), p(0), r(0), w(0), c(0), p1(0), r1(0), w1(0), t(0)
+{
+ instance = this;
+ sensitivity = 25;
+ dead_zone = 100;
+
+ for (int i = 0; i < MotionController::MaxActions; i++)
+ action[i] = 0;
+
+ memset(map, 0, sizeof(map));
+ memset(alt, 0, sizeof(alt));
+
+ map[KEY_PLUS_X] = 'R';
+ map[KEY_MINUS_X] = 'E';
+ map[KEY_PLUS_Y] = VK_HOME;
+ map[KEY_MINUS_Y] = VK_END;
+ map[KEY_PLUS_Z] = VK_PRIOR; // page up
+ map[KEY_MINUS_Z] = VK_NEXT; // page down
+
+ map[KEY_PITCH_UP] = VK_DOWN;
+ map[KEY_PITCH_DOWN] = VK_UP;
+ map[KEY_YAW_LEFT] = VK_LEFT;
+ map[KEY_YAW_RIGHT] = VK_RIGHT;
+ map[KEY_ROLL_ENABLE] = 0; // used to be VK_CONTROL;
+}
+
+Keyboard::~Keyboard()
+{
+ instance = 0;
+}
+
+Keyboard*
+Keyboard::GetInstance()
+{
+ return instance;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Keyboard::MapKeys(KeyMapEntry* mapping, int nkeys)
+{
+ for (int i = 0; i < nkeys; i++) {
+ KeyMapEntry k = mapping[i];
+
+ if (k.act >= KEY_MAP_FIRST && k.act <= KEY_MAP_LAST) {
+ if (k.key == 0 || k.key > VK_MBUTTON && k.key < KEY_JOY_1) {
+ map[k.act] = k.key;
+ alt[k.act] = k.alt;
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool Keyboard::KeyDown(int key)
+{
+ if (key) {
+ short k = GetAsyncKeyState(key);
+ return (k<0)||(k&1);
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+bool Keyboard::KeyDownMap(int key)
+{
+ if (key >= KEY_MAP_FIRST && key <= KEY_MAP_LAST && map[key]) {
+ short k = GetAsyncKeyState(map[key]);
+ short a = -1;
+
+ if (alt[key] > 0 && alt[key] < KEY_JOY_1) {
+ a = GetAsyncKeyState(alt[key]);
+ }
+ else {
+ a = !GetAsyncKeyState(VK_SHIFT) &&
+ !GetAsyncKeyState(VK_MENU);
+ }
+
+ return ((k<0)||(k&1)) && ((a<0)||(a&1));
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Keyboard::FlushKeys()
+{
+ for (int i = 0; i < 255; i++)
+ GetAsyncKeyState(i);
+}
+
+// +--------------------------------------------------------------------+
+
+static inline double sqr(double a) { return a; } //*a; }
+
+void
+Keyboard::Acquire()
+{
+ t = x = y = z = p = r = w = c = 0;
+
+ for (int i = 0; i < MotionController::MaxActions; i++)
+ action[i] = 0;
+
+ int speed = 10;
+
+ // lateral translations:
+ if (KeyDownMap(KEY_PLUS_Y)) y = 1;
+ else if (KeyDownMap(KEY_MINUS_Y)) y = -1;
+
+ if (KeyDownMap(KEY_PLUS_Z)) z = 1;
+ else if (KeyDownMap(KEY_MINUS_Z)) z = -1;
+
+ if (KeyDownMap(KEY_MINUS_X)) x = -1;
+ else if (KeyDownMap(KEY_PLUS_X)) x = 1;
+
+ const double steps=10;
+
+ // if roll and yaw are swapped --------------------------
+ if (swapped) {
+ // yaw:
+ if (KeyDownMap(KEY_ROLL_LEFT)) { if (w1<steps) w1+=1; w = -sqr(w1/steps); }
+ else if (KeyDownMap(KEY_ROLL_RIGHT)) { if (w1<steps) w1+=1; w = sqr(w1/steps); }
+
+ // another way to yaw:
+ if (KeyDownMap(KEY_ROLL_ENABLE)) {
+ if (KeyDownMap(KEY_YAW_LEFT)) { if (w1<steps) w1+=1; w = -sqr(w1/steps); }
+ else if (KeyDownMap(KEY_YAW_RIGHT)) { if (w1<steps) w1+=1; w = sqr(w1/steps); }
+ else w1 = 0;
+ }
+
+ // roll:
+ else {
+ if (KeyDownMap(KEY_YAW_LEFT)) { if (r1<steps) r1+=1; r = sqr(r1/steps); }
+ else if (KeyDownMap(KEY_YAW_RIGHT)) { if (r1<steps) r1+=1; r = -sqr(r1/steps); }
+ else r1 = 0;
+ }
+ }
+
+ // else roll and yaw are NOT swapped ---------------------
+ else {
+ // roll:
+ if (KeyDownMap(KEY_ROLL_LEFT)) { if (r1<steps) r1+=1; r = sqr(r1/steps); }
+ else if (KeyDownMap(KEY_ROLL_RIGHT)) { if (r1<steps) r1+=1; r = -sqr(r1/steps); }
+
+ // another way to roll:
+ if (KeyDownMap(KEY_ROLL_ENABLE)) {
+ if (KeyDownMap(KEY_YAW_LEFT)) { if (r1<steps) r1+=1; r = sqr(r1/steps); }
+ else if (KeyDownMap(KEY_YAW_RIGHT)) { if (r1<steps) r1+=1; r = -sqr(r1/steps); }
+ else r1 = 0;
+ }
+
+ // yaw left-right
+ else {
+ if (KeyDownMap(KEY_YAW_LEFT)) { if (w1<steps) w1+=1; w = -sqr(w1/steps); }
+ else if (KeyDownMap(KEY_YAW_RIGHT)) { if (w1<steps) w1+=1; w = sqr(w1/steps); }
+ else w1 = 0;
+ }
+ }
+
+ // if pitch is inverted ----------------------------------
+ if (inverted) {
+ if (KeyDownMap(KEY_PITCH_DOWN)) { if (p1<steps) p1+=1; p = -sqr(p1/steps); }
+ else if (KeyDownMap(KEY_PITCH_UP)) { if (p1<steps) p1+=1; p = sqr(p1/steps); }
+ else p1 = 0;
+ }
+
+ // else pitch is NOT inverted ----------------------------
+ else {
+ if (KeyDownMap(KEY_PITCH_UP)) { if (p1<steps) p1+=1; p = -sqr(p1/steps); }
+ else if (KeyDownMap(KEY_PITCH_DOWN)) { if (p1<steps) p1+=1; p = sqr(p1/steps); }
+ else p1 = 0;
+ }
+
+ if (KeyDownMap(KEY_CENTER)) c = 1;
+
+ // actions
+ if (KeyDownMap(KEY_ACTION_0)) action[0] = 1;
+ if (KeyDownMap(KEY_ACTION_1)) action[1] = 1;
+ if (KeyDownMap(KEY_ACTION_2)) action[2] = 1;
+ if (KeyDownMap(KEY_ACTION_3)) action[3] = 1;
+}
+
+// +--------------------------------------------------------------------+
+
+
+
diff --git a/nGenEx/Keyboard.h b/nGenEx/Keyboard.h
new file mode 100644
index 0000000..db74950
--- /dev/null
+++ b/nGenEx/Keyboard.h
@@ -0,0 +1,74 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Keyboard.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Keyboard Input class
+*/
+
+#ifndef Keyboard_h
+#define Keyboard_h
+
+#include "MotionController.h"
+
+// +--------------------------------------------------------------------+
+
+class Keyboard : public MotionController
+{
+public:
+ static const char* TYPENAME() { return "Keyboard"; }
+
+ Keyboard();
+ virtual ~Keyboard();
+
+ // setup
+ virtual void MapKeys(KeyMapEntry* mapping, int nkeys);
+
+ // sample the physical device
+ virtual void Acquire();
+
+ // translations
+ virtual double X() { return x; }
+ virtual double Y() { return y; }
+ virtual double Z() { return z; }
+
+ // rotations
+ virtual double Pitch() { return p; }
+ virtual double Roll() { return r; }
+ virtual double Yaw() { return w; }
+ virtual int Center() { return c; }
+
+ // throttle
+ virtual double Throttle() { return t; }
+ virtual void SetThrottle(double throttle) { t = throttle; }
+
+ // actions
+ virtual int Action(int n) { return action[n]; }
+ virtual int ActionMap(int n) { return KeyDownMap(n); }
+
+ static bool KeyDown(int key);
+ static bool KeyDownMap(int key);
+ static void FlushKeys();
+
+ static Keyboard* GetInstance();
+
+protected:
+ double x,y,z,p,r,w,t;
+ double p1, r1, w1;
+ int c;
+ int action[MotionController::MaxActions];
+
+ static int map[KEY_MAP_SIZE];
+ static int alt[KEY_MAP_SIZE];
+};
+
+// +--------------------------------------------------------------------+
+
+#endif Keyboard_h
+
diff --git a/nGenEx/Layout.cpp b/nGenEx/Layout.cpp
new file mode 100644
index 0000000..dd2d792
--- /dev/null
+++ b/nGenEx/Layout.cpp
@@ -0,0 +1,246 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Layout.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Layout Resource class implementation
+*/
+
+#include "MemDebug.h"
+#include "Layout.h"
+
+// +--------------------------------------------------------------------+
+
+Layout::Layout()
+{ }
+
+Layout::~Layout()
+{ }
+
+// +--------------------------------------------------------------------+
+
+bool
+Layout::DoLayout(ActiveWindow* panel)
+{
+ if (!panel || panel->GetChildren().size() < 1)
+ return false;
+
+ if (cols.size() < 1 || rows.size() < 1)
+ return false;
+
+ ArrayList cell_x;
+ ArrayList cell_y;
+
+ ScaleWeights();
+ CalcCells(panel->Width(), panel->Height(), cell_x, cell_y);
+
+ ListIter<ActiveWindow> iter = panel->GetChildren();
+ while (++iter) {
+ ActiveWindow* w = iter.value();
+ Rect c = w->GetCells();
+ Rect r;
+ Rect rp = panel->GetRect();
+
+ if (c.x < 0) c.x = 0;
+ else if (c.x >= cell_x.size()) c.x = cell_x.size() - 1;
+ if (c.y < 0) c.y = 0;
+ else if (c.y >= cell_y.size()) c.y = cell_y.size() - 1;
+ if (c.x+c.w >= cell_x.size()) c.w = cell_x.size() - c.x - 1;
+ if (c.y+c.h >= cell_y.size()) c.h = cell_y.size() - c.y - 1;
+
+ r.x = cell_x[c.x] + w->GetCellInsets().left;
+ r.y = cell_y[c.y] + w->GetCellInsets().top;
+ r.w = cell_x[c.x+c.w] - w->GetCellInsets().right - r.x;
+ r.h = cell_y[c.y+c.h] - w->GetCellInsets().bottom - r.y;
+
+ r.x += panel->X();
+ r.y += panel->Y();
+
+ if (w->GetFixedWidth() && w->GetFixedWidth() < r.w)
+ r.w = w->GetFixedWidth();
+
+ if (w->GetFixedHeight() && w->GetFixedHeight() < r.h)
+ r.h = w->GetFixedHeight();
+
+ if (w->GetID() == 330 || w->GetID() == 125) {
+ int y1 = r.y + r.h;
+ int y2 = rp.y + rp.h;
+ }
+
+ if (w->GetHidePartial() && (r.x + r.w > rp.x + rp.w)) {
+ w->MoveTo(Rect(0,0,0,0));
+ }
+
+ else if (w->GetHidePartial() && (r.y + r.h > rp.y + rp.h)) {
+ w->MoveTo(Rect(0,0,0,0));
+ }
+
+ else {
+ w->MoveTo(r);
+ }
+ }
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Layout::ScaleWeights()
+{
+ int i;
+ float total = 0;
+
+ for (i = 0; i < col_weights.size(); i++)
+ total += col_weights[i];
+
+ if (total > 0) {
+ for (i = 0; i < col_weights.size(); i++)
+ col_weights[i] = col_weights[i] / total;
+ }
+
+ total = 0;
+ for (i = 0; i < row_weights.size(); i++)
+ total += row_weights[i];
+
+ if (total > 0) {
+ for (i = 0; i < row_weights.size(); i++)
+ row_weights[i] = row_weights[i] / total;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Layout::CalcCells(DWORD w, DWORD h, ArrayList& cell_x, ArrayList& cell_y)
+{
+ DWORD x = 0;
+ DWORD y = 0;
+ DWORD min_x = 0;
+ DWORD min_y = 0;
+ DWORD ext_x = 0;
+ DWORD ext_y = 0;
+ int i;
+
+ for (i = 0; i < cols.size(); i++)
+ min_x += cols[i];
+
+ for (i = 0; i < rows.size(); i++)
+ min_y += rows[i];
+
+ if (min_x < w)
+ ext_x = w - min_x;
+
+ if (min_y < h)
+ ext_y = h - min_y;
+
+ cell_x.append(x);
+ for (i = 0; i < cols.size(); i++) {
+ x += cols[i] + (DWORD) (ext_x * col_weights[i]);
+ cell_x.append(x);
+ }
+
+ cell_y.append(y);
+ for (i = 0; i < rows.size(); i++) {
+ y += rows[i] + (DWORD) (ext_y * row_weights[i]);
+ cell_y.append(y);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Layout::Clear()
+{
+ cols.clear();
+ rows.clear();
+
+ col_weights.clear();
+ row_weights.clear();
+}
+
+void
+Layout::AddCol(DWORD min_width, float col_factor)
+{
+ cols.append(min_width);
+ col_weights.append(col_factor);
+}
+
+void
+Layout::AddRow(DWORD min_height, float row_factor)
+{
+ rows.append(min_height);
+ row_weights.append(row_factor);
+}
+
+void
+Layout::SetConstraints(const ArrayList& min_x,
+ const ArrayList& min_y,
+ const FloatList& weight_x,
+ const FloatList& weight_y)
+{
+ Clear();
+
+ if (min_x.size() == weight_x.size() &&
+ min_y.size() == weight_y.size()) {
+
+ cols.append(min_x);
+ rows.append(min_y);
+
+ col_weights.append(weight_x);
+ row_weights.append(weight_y);
+ }
+}
+
+void
+Layout::SetConstraints(const FloatList& min_x,
+ const FloatList& min_y,
+ const FloatList& weight_x,
+ const FloatList& weight_y)
+{
+ Clear();
+
+ if (min_x.size() == weight_x.size() &&
+ min_y.size() == weight_y.size()) {
+
+ for (int i = 0; i < min_x.size(); i++)
+ cols.append((DWORD) min_x[i]);
+
+ for (i = 0; i < min_y.size(); i++)
+ rows.append((DWORD) min_y[i]);
+
+ col_weights.append(weight_x);
+ row_weights.append(weight_y);
+ }
+}
+
+void
+Layout::SetConstraints(int ncols,
+ int nrows,
+ const int* min_x,
+ const int* min_y,
+ const float* weight_x,
+ const float* weight_y)
+{
+ Clear();
+
+ if (nrows > 0 && ncols > 0) {
+ int i = 0;
+
+ for (i = 0; i < ncols; i++) {
+ cols.append(min_x[i]);
+ col_weights.append(weight_x[i]);
+ }
+
+ for (i = 0; i < nrows; i++) {
+ rows.append(min_y[i]);
+ row_weights.append(weight_y[i]);
+ }
+ }
+}
diff --git a/nGenEx/Layout.h b/nGenEx/Layout.h
new file mode 100644
index 0000000..9e901fd
--- /dev/null
+++ b/nGenEx/Layout.h
@@ -0,0 +1,66 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Layout.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Layout Manager class for ActiveWindow panels
+*/
+
+#ifndef Layout_h
+#define Layout_h
+
+#include "ActiveWindow.h"
+#include "ArrayList.h"
+
+// +--------------------------------------------------------------------+
+
+class Layout
+{
+public:
+ static const char* TYPENAME() { return "Layout"; }
+
+ Layout();
+ virtual ~Layout();
+
+ virtual bool DoLayout(ActiveWindow* panel);
+
+ virtual void Clear();
+ virtual void AddCol(DWORD min_width, float col_factor);
+ virtual void AddRow(DWORD min_height, float row_factor);
+
+ virtual void SetConstraints(const ArrayList& min_x,
+ const ArrayList& min_y,
+ const FloatList& weight_x,
+ const FloatList& weight_y);
+
+ virtual void SetConstraints(const FloatList& min_x,
+ const FloatList& min_y,
+ const FloatList& weight_x,
+ const FloatList& weight_y);
+
+ virtual void SetConstraints(int ncols,
+ int nrows,
+ const int* min_x,
+ const int* min_y,
+ const float* weight_x,
+ const float* weight_y);
+
+
+protected:
+ virtual void ScaleWeights();
+ virtual void CalcCells(DWORD w, DWORD h, ArrayList& cell_x, ArrayList& cell_y);
+
+ ArrayList cols;
+ ArrayList rows;
+ FloatList col_weights;
+ FloatList row_weights;
+};
+
+#endif Layout_h
+
diff --git a/nGenEx/Light.cpp b/nGenEx/Light.cpp
new file mode 100644
index 0000000..fa4d822
--- /dev/null
+++ b/nGenEx/Light.cpp
@@ -0,0 +1,72 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Light.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Dynamic Light Source
+*/
+
+#include "MemDebug.h"
+#include "Light.h"
+#include "Scene.h"
+
+// +--------------------------------------------------------------------+
+
+int Light::id_key = 1;
+
+// +--------------------------------------------------------------------+
+
+Light::Light(float l, float dl, int time)
+ : id(id_key++), type(LIGHT_POINT), life(time),
+ light(l), dldt(dl), color(255,255,255),
+ active(true), shadow(false), scene(0)
+{ }
+
+// +--------------------------------------------------------------------+
+
+Light::~Light()
+{ }
+
+// +--------------------------------------------------------------------+
+
+void
+Light::Update()
+{
+ if (dldt < 1.0f)
+ light *= dldt;
+
+ if (life > 0) life--;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Light::Destroy()
+{
+ if (scene)
+ scene->DelLight(this);
+
+ delete this;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Light::MoveTo(const Point& dst)
+{
+ //if (type != LIGHT_DIRECTIONAL)
+ loc = dst;
+}
+
+void
+Light::TranslateBy(const Point& ref)
+{
+ if (type != LIGHT_DIRECTIONAL)
+ loc = loc - ref;
+}
diff --git a/nGenEx/Light.h b/nGenEx/Light.h
new file mode 100644
index 0000000..3c3cfe9
--- /dev/null
+++ b/nGenEx/Light.h
@@ -0,0 +1,96 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Light.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Dynamic Light Source
+*/
+
+#ifndef Light_h
+#define Light_h
+
+#include "Geometry.h"
+#include "Color.h"
+
+// +--------------------------------------------------------------------+
+
+#define LIGHT_DESTROY(x) if (x) { x->Destroy(); x = 0; }
+
+// +--------------------------------------------------------------------+
+
+class Scene;
+
+// +--------------------------------------------------------------------+
+
+class Light
+{
+public:
+ static const char* TYPENAME() { return "Light"; }
+
+ enum TYPES {
+ LIGHT_POINT = 1,
+ LIGHT_SPOT = 2,
+ LIGHT_DIRECTIONAL = 3,
+ LIGHT_FORCE_DWORD = 0x7fffffff
+ };
+
+ Light(float l=0.0f, float dl=1.0f, int time=-1);
+ virtual ~Light();
+
+ int operator == (const Light& l) const { return id == l.id; }
+
+ // operations
+ virtual void Update();
+
+ // accessors / mutators
+ int Identity() const { return id; }
+ Point Location() const { return loc; }
+
+ DWORD Type() const { return type; }
+ void SetType(DWORD t) { type = t; }
+ float Intensity() const { return light; }
+ void SetIntensity(float f) { light = f; }
+ Color GetColor() const { return color; }
+ void SetColor(Color c) { color = c; }
+ bool IsActive() const { return active; }
+ void SetActive(bool a) { active = a; }
+ bool CastsShadow() const { return shadow; }
+ void SetShadow(bool s) { shadow = s; }
+
+ bool IsPoint() const { return type == LIGHT_POINT; }
+ bool IsSpot() const { return type == LIGHT_SPOT; }
+ bool IsDirectional() const { return type == LIGHT_DIRECTIONAL; }
+
+ virtual void MoveTo(const Point& dst);
+ virtual void TranslateBy(const Point& ref);
+
+ virtual int Life() const { return life; }
+ virtual void Destroy();
+ virtual Scene* GetScene() const { return scene; }
+ virtual void SetScene(Scene*s) { scene = s; }
+
+protected:
+ static int id_key;
+
+ int id;
+ DWORD type;
+ Point loc;
+ int life;
+ float light;
+ float dldt;
+ Color color;
+ bool active;
+ bool shadow;
+ Scene* scene;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif Light_h
+
diff --git a/nGenEx/ListBox.cpp b/nGenEx/ListBox.cpp
new file mode 100644
index 0000000..27671b1
--- /dev/null
+++ b/nGenEx/ListBox.cpp
@@ -0,0 +1,1333 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: ListBox.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ ListBox ActiveWindow class
+*/
+
+#include "MemDebug.h"
+#include "ListBox.h"
+#include "Button.h"
+#include "Bitmap.h"
+#include "FormWindow.h"
+#include "Video.h"
+#include "Font.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+
+// +--------------------------------------------------------------------+
+
+class ListBoxCell
+{
+public:
+ static const char* TYPENAME() { return "ListBoxCell"; }
+
+ ListBoxCell() : data(0), image(0) { }
+
+ Text text;
+ DWORD data;
+ Bitmap* image;
+};
+
+// +--------------------------------------------------------------------+
+
+class ListBoxItem
+{
+public:
+ static const char* TYPENAME() { return "ListBoxItem"; }
+
+ ListBoxItem() : data(0), image(0), selected(false), listbox(0), color(Color::White) { }
+ ~ListBoxItem() { subitems.destroy(); }
+
+ int operator < (const ListBoxItem& item) const;
+ int operator <=(const ListBoxItem& item) const;
+ int operator ==(const ListBoxItem& item) const;
+
+ Text text;
+ DWORD data;
+ Bitmap* image;
+ bool selected;
+ Color color;
+ List<ListBoxCell> subitems;
+
+ ListBox* listbox;
+};
+
+// +--------------------------------------------------------------------+
+
+class ListBoxColumn
+{
+public:
+ static const char* TYPENAME() { return "ListBoxColumn"; }
+
+ ListBoxColumn() : width(0), align(0), sort(0), color(Color::White), use_color(0), percent(0) { }
+
+ Text title;
+ int width;
+ int align;
+ int sort;
+ Color color;
+ int use_color;
+
+ double percent;
+};
+
+// +--------------------------------------------------------------------+
+
+int ListBoxItem::operator < (const ListBoxItem& item) const
+{
+ int sort_column = listbox->GetSortColumn() - 1;
+ int sort_criteria = listbox->GetSortCriteria();
+
+ if (listbox && listbox == item.listbox) {
+ if (sort_column == -1) {
+ switch (sort_criteria) {
+ case ListBox::LIST_SORT_NUMERIC_DESCENDING:
+ return data > item.data;
+
+ case ListBox::LIST_SORT_ALPHA_DESCENDING:
+ return text > item.text;
+
+ case ListBox::LIST_SORT_ALPHA_ASCENDING:
+ return text < item.text;
+
+ case ListBox::LIST_SORT_NUMERIC_ASCENDING:
+ return data < item.data;
+ }
+ }
+
+ else if (sort_column >= 0 &&
+ sort_column <= subitems.size() &&
+ sort_column <= item.subitems.size()) {
+
+ switch (sort_criteria) {
+ case ListBox::LIST_SORT_NUMERIC_DESCENDING:
+ return subitems[sort_column]->data > item.subitems[sort_column]->data;
+
+ case ListBox::LIST_SORT_ALPHA_DESCENDING:
+ return subitems[sort_column]->text > item.subitems[sort_column]->text;
+
+ case ListBox::LIST_SORT_ALPHA_ASCENDING:
+ return subitems[sort_column]->text < item.subitems[sort_column]->text;
+
+ case ListBox::LIST_SORT_NUMERIC_ASCENDING:
+ return subitems[sort_column]->data < item.subitems[sort_column]->data;
+ }
+ }
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+int ListBoxItem::operator <=(const ListBoxItem& item) const
+{
+ int sort_column = listbox->GetSortColumn() - 1;
+ int sort_criteria = listbox->GetSortCriteria();
+
+ if (listbox && listbox == item.listbox) {
+ if (sort_column == -1) {
+ switch (sort_criteria) {
+ case ListBox::LIST_SORT_NUMERIC_DESCENDING:
+ return data >= item.data;
+
+ case ListBox::LIST_SORT_ALPHA_DESCENDING:
+ return text >= item.text;
+
+ case ListBox::LIST_SORT_ALPHA_ASCENDING:
+ return text <= item.text;
+
+ case ListBox::LIST_SORT_NUMERIC_ASCENDING:
+ return data <= item.data;
+ }
+ }
+
+ else if (sort_column >= 0 &&
+ sort_column <= subitems.size() &&
+ sort_column <= item.subitems.size()) {
+
+ switch (sort_criteria) {
+ case ListBox::LIST_SORT_NUMERIC_DESCENDING:
+ return subitems[sort_column]->data >= item.subitems[sort_column]->data;
+
+ case ListBox::LIST_SORT_ALPHA_DESCENDING:
+ return subitems[sort_column]->text >= item.subitems[sort_column]->text;
+
+ case ListBox::LIST_SORT_ALPHA_ASCENDING:
+ return subitems[sort_column]->text <= item.subitems[sort_column]->text;
+
+ case ListBox::LIST_SORT_NUMERIC_ASCENDING:
+ return subitems[sort_column]->data <= item.subitems[sort_column]->data;
+ }
+ }
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+int ListBoxItem::operator == (const ListBoxItem& item) const
+{
+ int sort_column = listbox->GetSortColumn() - 1;
+ int sort_criteria = listbox->GetSortCriteria();
+
+ if (listbox && listbox == item.listbox) {
+ if (sort_column == -1) {
+ switch (sort_criteria) {
+ case ListBox::LIST_SORT_NUMERIC_DESCENDING:
+ return data == item.data;
+
+ case ListBox::LIST_SORT_ALPHA_DESCENDING:
+ return text == item.text;
+
+ case ListBox::LIST_SORT_ALPHA_ASCENDING:
+ return text == item.text;
+
+ case ListBox::LIST_SORT_NUMERIC_ASCENDING:
+ return data == item.data;
+ }
+ }
+
+ else if (sort_column >= 0 &&
+ sort_column <= subitems.size() &&
+ sort_column <= item.subitems.size()) {
+
+ switch (sort_criteria) {
+ case ListBox::LIST_SORT_NUMERIC_DESCENDING:
+ return subitems[sort_column]->data == item.subitems[sort_column]->data;
+
+ case ListBox::LIST_SORT_ALPHA_DESCENDING:
+ return subitems[sort_column]->text == item.subitems[sort_column]->text;
+
+ case ListBox::LIST_SORT_ALPHA_ASCENDING:
+ return subitems[sort_column]->text == item.subitems[sort_column]->text;
+
+ case ListBox::LIST_SORT_NUMERIC_ASCENDING:
+ return subitems[sort_column]->data == item.subitems[sort_column]->data;
+ }
+ }
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+static int old_cursor;
+
+// +--------------------------------------------------------------------+
+
+ListBox::ListBox(ActiveWindow* p, int ax, int ay, int aw, int ah, DWORD aid)
+ : ScrollWindow(p->GetScreen(), ax, ay, aw, ah, aid, 0, p)
+{
+ show_headings = false;
+ multiselect = false;
+ list_index = 0;
+ selcount = 0;
+
+ selected_color = Color(255, 255, 128);
+
+ sort_column = 0;
+ item_style = LIST_ITEM_STYLE_PLAIN;
+ seln_style = LIST_ITEM_STYLE_PLAIN;
+
+ char buf[32];
+ sprintf(buf, "ListBox %d", id);
+ desc = buf;
+}
+
+ListBox::ListBox(Screen* s, int ax, int ay, int aw, int ah, DWORD aid)
+ : ScrollWindow(s, ax, ay, aw, ah, aid)
+{
+ show_headings = false;
+ multiselect = false;
+ list_index = 0;
+ selcount = 0;
+
+ selected_color = Color(255, 255, 128);
+
+ sort_column = 0;
+ item_style = LIST_ITEM_STYLE_PLAIN;
+ seln_style = LIST_ITEM_STYLE_PLAIN;
+
+ char buf[32];
+ sprintf(buf, "ListBox %d", id);
+ desc = buf;
+}
+
+ListBox::~ListBox()
+{
+ items.destroy();
+ columns.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ListBox::DrawContent(const Rect& ctrl_rect)
+{
+ SizeColumns();
+
+ Rect item_rect = ctrl_rect;
+ item_rect.h = line_height;
+
+ int h = rect.h;
+
+ // draw headings at top, if needed:
+ if (show_headings) {
+ Color save_color = back_color;
+ back_color = ShadeColor(back_color, 1.3);
+ font->SetColor(fore_color);
+
+ int max_column = columns.size()-1;
+ item_rect.h += HEADING_EXTRA;
+
+ page_size = (h-item_rect.h) / (line_height + leading);
+
+ for (int column = 0; column <= max_column; column++) {
+ item_rect.w = GetColumnWidth(column);
+
+ // draw heading button
+ FillRect(item_rect, back_color);
+ DrawStyleRect(item_rect, WIN_RAISED_FRAME);
+
+ Rect title_rect = item_rect;
+ title_rect.Deflate(3,3);
+
+ DrawText(GetColumnTitle(column),
+ 0,
+ title_rect,
+ DT_CENTER|DT_SINGLELINE);
+
+ item_rect.x += item_rect.w;
+ }
+
+ item_rect.y += item_rect.h;
+ back_color = save_color;
+ item_rect.h = line_height;
+ }
+
+ int index = 0;
+ ListIter<ListBoxItem> iter = items;
+
+ while (++iter && item_rect.y < h) {
+ ListBoxItem* item = iter.value();
+
+ if (index++ >= top_index) {
+ // draw main item:
+ int column = 0;
+ item_rect.x = ctrl_rect.x;
+ item_rect.w = GetColumnWidth(column) - 2;
+
+ if (item_rect.y + item_rect.h > h) {
+ item_rect.h = h - item_rect.y - 1;
+ }
+
+ Color item_color = GetItemColor(index-1, 0);
+
+ if (item->selected) {
+ font->SetColor(selected_color);
+
+ if (seln_style == LIST_ITEM_STYLE_FILLED_BOX)
+ FillRect(item_rect, selected_color * 0.25);
+
+ if (seln_style >= LIST_ITEM_STYLE_BOX)
+ DrawRect(item_rect, selected_color);
+ }
+ else {
+ font->SetColor(item_color);
+
+ if (item_style == LIST_ITEM_STYLE_FILLED_BOX)
+ FillRect(item_rect, item_color * 0.25);
+
+ if (item_style >= LIST_ITEM_STYLE_BOX)
+ DrawRect(item_rect, item_color);
+ }
+
+ Rect text_rect = item_rect;
+
+ if (item->image && item->image->Width() > 0 && item->image->Height() > 0) {
+ DrawBitmap(text_rect.x, text_rect.y, text_rect.x + text_rect.w, text_rect.y + line_height, item->image);
+ }
+ else {
+ text_rect.Deflate(2,0);
+ DrawText(item->text.data(),
+ item->text.length(),
+ text_rect,
+ GetColumnAlign(column)|DT_SINGLELINE);
+ }
+
+ // draw subitems:
+ ListIter<ListBoxCell> sub_iter = item->subitems;
+ while (++sub_iter) {
+ ListBoxCell* sub = sub_iter.value();
+
+ column++;
+ item_rect.x += item_rect.w + 2;
+ item_rect.w = GetColumnWidth(column) - 2;
+
+ if (item->selected) {
+ if (seln_style == LIST_ITEM_STYLE_FILLED_BOX)
+ FillRect(item_rect, selected_color * 0.25);
+
+ if (seln_style >= LIST_ITEM_STYLE_BOX)
+ DrawRect(item_rect, selected_color);
+ }
+ else {
+ if (item_style == LIST_ITEM_STYLE_FILLED_BOX)
+ FillRect(item_rect, item_color * 0.25);
+
+ if (item_style >= LIST_ITEM_STYLE_BOX)
+ DrawRect(item_rect, item_color);
+ }
+
+ if (item->selected)
+ font->SetColor(selected_color);
+ else
+ font->SetColor(GetItemColor(index-1, column));
+
+ Rect text_rect = item_rect;
+ if (sub->image && sub->image->Width() > 0 && sub->image->Height() > 0) {
+ DrawBitmap(text_rect.x, text_rect.y, text_rect.x + text_rect.w, text_rect.y + line_height, sub->image);
+ }
+ else {
+ text_rect.Deflate(2,0);
+ DrawText(sub->text.data(),
+ sub->text.length(),
+ text_rect,
+ GetColumnAlign(column)|DT_SINGLELINE);
+ }
+ }
+
+ item_rect.y += line_height + leading;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ListBox::SizeColumns()
+{
+ ListBoxColumn* c = columns.first();
+
+ if (c->percent < 0.001) {
+ double total = 0;
+
+ ListIter<ListBoxColumn> iter = columns;
+ while (++iter) {
+ c = iter.value();
+ total += c->width;
+ }
+
+ iter.reset();
+ while (++iter) {
+ c = iter.value();
+ c->percent = c->width / total;
+ }
+ }
+
+ int usable_width = rect.w;
+ int used = 0;
+
+ if (IsScrollVisible()) {
+ usable_width -= SCROLL_WIDTH + 2;
+ }
+ else {
+ usable_width -= 3;
+ }
+
+ for (int i = 0; i < columns.size(); i++) {
+ c = columns[i];
+
+ if (i < columns.size() - 1)
+ c->width = (int) (c->percent * usable_width);
+ else
+ c->width = usable_width - used;
+
+ used += c->width;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+int ListBox::NumItems()
+{
+ return items.size();
+}
+
+int ListBox::NumColumns()
+{
+ return columns.size();
+}
+
+Text ListBox::GetItemText(int index)
+{
+ if (index >= 0 && index < items.size())
+ return items[index]->text;
+
+ return Text();
+}
+
+void ListBox::SetItemText(int index, const char* text)
+{
+ if (index >= 0 && index < items.size()) {
+ items[index]->text = text;
+ }
+}
+
+DWORD ListBox::GetItemData(int index)
+{
+ if (index >= 0 && index < items.size())
+ return items[index]->data;
+
+ return 0;
+}
+
+void ListBox::SetItemData(int index, DWORD data)
+{
+ if (index >= 0 && index < items.size()) {
+ items[index]->data = data;
+ }
+}
+
+Bitmap* ListBox::GetItemImage(int index)
+{
+ if (index >= 0 && index < items.size())
+ return items[index]->image;
+
+ return 0;
+}
+
+void ListBox::SetItemImage(int index, Bitmap* img)
+{
+ if (index >= 0 && index < items.size()) {
+ items[index]->image = img;
+ }
+}
+
+Color ListBox::GetItemColor(int index)
+{
+ if (index >= 0 && index < items.size())
+ return items[index]->color;
+
+ return Color::White;
+}
+
+void ListBox::SetItemColor(int index, Color c)
+{
+ if (index >= 0 && index < items.size()) {
+ items[index]->color = c;
+ }
+}
+
+Text ListBox::GetItemText(int index, int column)
+{
+ if (column == 0) {
+ return GetItemText(index);
+ }
+
+ if (index >= 0 && index < items.size()) {
+ ListBoxItem* item = items[index];
+
+ column--;
+ if (column >= 0 && column < item->subitems.size())
+ return item->subitems[column]->text;
+ }
+
+ return Text();
+}
+
+void ListBox::SetItemText(int index, int column, const char* text)
+{
+ if (column == 0) {
+ SetItemText(index, text);
+ return;
+ }
+
+ if (index >= 0 && index < items.size()) {
+ ListBoxItem* item = items[index];
+
+ column--;
+ if (column >= 0 && column < columns.size()-1) {
+ while (column >= item->subitems.size()) {
+ ListBoxCell* cell = new(__FILE__,__LINE__) ListBoxCell;
+ if (cell)
+ item->subitems.append(cell);
+ }
+
+ item->subitems[column]->text = text;
+ }
+ }
+}
+
+DWORD ListBox::GetItemData(int index, int column)
+{
+ if (column == 0) {
+ return GetItemData(index);
+ }
+
+ if (index >= 0 && index < items.size()) {
+ ListBoxItem* item = items[index];
+
+ column--;
+ if (column >= 0 && column < item->subitems.size())
+ return item->subitems[column]->data;
+ }
+
+ return 0;
+}
+
+void ListBox::SetItemData(int index, int column, DWORD data)
+{
+ if (column == 0) {
+ SetItemData(index, data);
+ return;
+ }
+
+ if (index >= 0 && index < items.size()) {
+ ListBoxItem* item = items[index];
+
+ column--;
+ if (column >= 0 && column < columns.size()-1) {
+ while (column >= item->subitems.size()) {
+ ListBoxCell* cell = new(__FILE__,__LINE__) ListBoxCell;
+ if (cell)
+ item->subitems.append(cell);
+ }
+
+ item->subitems[column]->data = data;
+ }
+ }
+}
+
+Bitmap* ListBox::GetItemImage(int index, int column)
+{
+ if (column == 0) {
+ return GetItemImage(index);
+ }
+
+ if (index >= 0 && index < items.size()) {
+ ListBoxItem* item = items[index];
+
+ column--;
+ if (column >= 0 && column < item->subitems.size())
+ return item->subitems[column]->image;
+ }
+
+ return 0;
+}
+
+void ListBox::SetItemImage(int index, int column, Bitmap* img)
+{
+ if (column == 0) {
+ SetItemImage(index, img);
+ return;
+ }
+
+ if (index >= 0 && index < items.size()) {
+ ListBoxItem* item = items[index];
+
+ column--;
+ if (column >= 0 && column < columns.size()-1) {
+ while (column >= item->subitems.size()) {
+ ListBoxCell* cell = new(__FILE__,__LINE__) ListBoxCell;
+ if (cell)
+ item->subitems.append(cell);
+ }
+
+ item->subitems[column]->image = img;
+ }
+ }
+}
+
+int ListBox::AddItem(const char* text)
+{
+ ListBoxItem* item = new(__FILE__,__LINE__) ListBoxItem;
+
+ if (item) {
+ item->text = text;
+ item->color = fore_color;
+ item->listbox = this;
+
+ items.append(item);
+
+ line_count = items.size();
+ list_index = items.size()-1;
+ }
+
+ return list_index+1;
+}
+
+int ListBox::AddItemWithData(const char* text, int data)
+{
+ ListBoxItem* item = new(__FILE__,__LINE__) ListBoxItem;
+
+ if (item) {
+ item->text = text;
+ item->data = data;
+ item->color = fore_color;
+ item->listbox = this;
+
+ items.append(item);
+
+ line_count = items.size();
+ list_index = items.size()-1;
+ }
+
+ return list_index+1;
+}
+
+int ListBox::AddImage(Bitmap* img)
+{
+ ListBoxItem* item = new(__FILE__,__LINE__) ListBoxItem;
+
+ if (item) {
+ item->image = img;
+ item->color = fore_color;
+ item->listbox = this;
+
+ items.append(item);
+
+ line_count = items.size();
+ list_index = items.size()-1;
+ }
+
+ return list_index+1;
+}
+
+void ListBox::InsertItem(int index, const char* text)
+{
+ if (index >=0 && index < items.size()) {
+ ListBoxItem* item = new(__FILE__,__LINE__) ListBoxItem;
+
+ if (item) {
+ item->text = text;
+ item->color = fore_color;
+ item->listbox = this;
+
+ list_index = index;
+ items.insert(item, list_index);
+ line_count = items.size();
+ }
+ }
+}
+
+void ListBox::InsertItemWithData(int index, const char* text, int data)
+{
+ if (index >=0 && index < items.size()) {
+ ListBoxItem* item = new(__FILE__,__LINE__) ListBoxItem;
+
+ if (item) {
+ item->text = text;
+ item->data = data;
+ item->color = fore_color;
+ item->listbox = this;
+
+ list_index = index;
+ items.insert(item, list_index);
+ line_count = items.size();
+ }
+ }
+}
+
+void ListBox::ClearItems()
+{
+ items.destroy();
+ selcount = 0;
+ top_index = 0;
+ list_index = 0;
+ line_count = 0;
+}
+
+void ListBox::RemoveItem(int index)
+{
+ if (index >= 0 && index < items.size()) {
+ if (items[index]->selected)
+ selcount--;
+ items.removeIndex(index);
+ line_count = items.size();
+ }
+}
+
+void ListBox::RemoveSelectedItems()
+{
+ if (selcount) {
+ ListIter<ListBoxItem> item = items;
+ while (++item) {
+ if (item->selected) {
+ delete item.removeItem();
+ }
+ }
+
+ line_count = items.size();
+ selcount = 0;
+ }
+}
+
+void ListBox::AddColumn(const char* title, int width, int align, int sort)
+{
+ ListBoxColumn* column = new(__FILE__,__LINE__) ListBoxColumn;
+
+ if (column) {
+ column->title = title;
+ column->width = width;
+ column->align = align;
+ column->sort = sort;
+
+ columns.append(column);
+ }
+}
+
+Text ListBox::GetColumnTitle(int index)
+{
+ if (index >= 0 && index < columns.size())
+ return columns[index]->title;
+
+ return Text();
+}
+
+void ListBox::SetColumnTitle(int index, const char* title)
+{
+ if (index >= 0 && index < columns.size()) {
+ columns[index]->title = title;
+ }
+}
+
+int ListBox::GetColumnWidth(int index)
+{
+ if (index >= 0 && index < columns.size())
+ return columns[index]->width;
+
+ return 0;
+}
+
+void ListBox::SetColumnWidth(int index, int width)
+{
+ if (index >= 0 && index < columns.size()) {
+ columns[index]->width = width;
+ }
+}
+
+int ListBox::GetColumnAlign(int index)
+{
+ if (index >= 0 && index < columns.size())
+ return columns[index]->align;
+
+ return 0;
+}
+
+void ListBox::SetColumnAlign(int index, int align)
+{
+ if (index >= 0 && index < columns.size()) {
+ columns[index]->align = align;
+ }
+}
+
+int ListBox::GetColumnSort(int index)
+{
+ if (index >= 0 && index < columns.size())
+ return columns[index]->sort;
+
+ return 0;
+}
+
+void ListBox::SetColumnSort(int index, int sort)
+{
+ if (index >= 0 && index < columns.size()) {
+ columns[index]->sort = sort;
+ }
+}
+
+Color ListBox::GetColumnColor(int index)
+{
+ if (index >= 0 && index < columns.size())
+ return columns[index]->color;
+
+ return Color::White;
+}
+
+void ListBox::SetColumnColor(int index, Color c)
+{
+ if (index >= 0 && index < columns.size()) {
+ columns[index]->color = c;
+ columns[index]->use_color = true;
+ }
+}
+
+Color ListBox::GetItemColor(int index, int column)
+{
+ Color c = Color::White;
+
+ if (index >= 0 && index < items.size())
+ c = items[index]->color;
+
+ if (column >= 0 && column < columns.size()) {
+ if (columns[column]->use_color)
+ c = columns[column]->color;
+ }
+
+ return c;
+}
+
+int ListBox::GetMultiSelect()
+{
+ return multiselect;
+}
+
+void ListBox::SetMultiSelect(int nNewValue)
+{
+ if (multiselect != nNewValue && (nNewValue == 0 || nNewValue == 1)) {
+ multiselect = nNewValue;
+ ClearSelection();
+ }
+}
+
+bool ListBox::GetShowHeadings()
+{
+ return show_headings;
+}
+
+void ListBox::SetShowHeadings(bool nNewValue)
+{
+ if (show_headings != nNewValue) {
+ show_headings = nNewValue;
+ }
+}
+
+Color ListBox::GetSelectedColor()
+{
+ return selected_color;
+}
+
+void ListBox::SetSelectedColor(Color c)
+{
+ if (selected_color != c) {
+ selected_color = c;
+ }
+}
+
+int ListBox::GetItemStyle() const
+{
+ return item_style;
+}
+
+void ListBox::SetItemStyle(int style)
+{
+ if (style >= LIST_ITEM_STYLE_PLAIN && style <= LIST_ITEM_STYLE_FILLED_BOX) {
+ item_style = style;
+ }
+}
+
+int ListBox::GetSelectedStyle() const
+{
+ return seln_style;
+}
+
+void ListBox::SetSelectedStyle(int style)
+{
+ if (style >= LIST_ITEM_STYLE_PLAIN && style <= LIST_ITEM_STYLE_FILLED_BOX) {
+ seln_style = style;
+ }
+}
+
+bool ListBox::IsSelected(int index)
+{
+ if (index >= 0 && index < items.size())
+ return items[index]->selected;
+
+ return false;
+}
+
+void ListBox::SetSelected(int index, bool bNewValue)
+{
+ if (index >= 0 && index < items.size()) {
+ if (!multiselect)
+ ClearSelection();
+
+ if (items[index]->selected != bNewValue) {
+ items[index]->selected = bNewValue;
+
+ if (bNewValue) {
+ list_index = index;
+ selcount++;
+ }
+ else {
+ selcount--;
+ }
+ }
+ }
+}
+
+void ListBox::ClearSelection()
+{
+ ListIter<ListBoxItem> item = items;
+ while (++item)
+ item->selected = false;
+
+ selcount = 0;
+}
+
+int ListBox::GetListIndex()
+{
+ return list_index;
+}
+
+int ListBox::GetLineCount()
+{
+ line_count = items.size();
+ return line_count;
+}
+
+int ListBox::GetSelCount()
+{
+ return selcount;
+}
+
+int ListBox::GetSelection()
+{
+ for (int i = 0; i < items.size(); i++)
+ if (items[i]->selected)
+ return i;
+
+ return -1;
+}
+
+Text ListBox::GetSelectedItem()
+{
+ int n = GetSelection();
+ return GetItemText(n);
+}
+
+// +--------------------------------------------------------------------+
+
+int ListBox::GetSortColumn()
+{
+ return sort_column;
+}
+
+void ListBox::SetSortColumn(int col_index)
+{
+ if (col_index >= 0 && col_index <= columns.size())
+ sort_column = col_index;
+}
+
+int ListBox::GetSortCriteria()
+{
+ return GetColumnSort(sort_column);
+}
+
+void ListBox::SetSortCriteria(SORT sort)
+{
+ SetColumnSort(sort_column, sort);
+}
+
+// +--------------------------------------------------------------------+
+
+void ListBox::SortItems()
+{
+ if (sort_column >=0 && sort_column <= columns.size())
+ items.sort();
+}
+
+// +--------------------------------------------------------------------+
+
+int ListBox::IndexFromPoint(int x, int y) const
+{
+ int sel_index = -1;
+
+ if (show_headings)
+ sel_index = top_index + (y - (line_height+HEADING_EXTRA)) / (line_height + leading);
+
+ else
+ sel_index = top_index + y / (line_height + leading);
+
+ return sel_index;
+}
+
+// +--------------------------------------------------------------------+
+
+int ListBox::OnMouseMove(int x, int y)
+{
+ bool dirty = false;
+
+ if (captured) {
+ ActiveWindow* test = GetCapture();
+
+ if (test != this) {
+ captured = false;
+ dirty = true;
+ }
+
+ else {
+ if (selecting && !dragging) {
+ if (dragdrop && (x < rect.x ||
+ x > rect.x+rect.w ||
+ y < rect.y ||
+ y > rect.y+rect.h)) {
+
+ dragging = true;
+ OnDragStart(x,y);
+ }
+ }
+
+ if (scrolling == SCROLL_THUMB) {
+ mouse_y = y - rect.y - TRACK_START;
+
+ int dest = (int) ((double) mouse_y/track_length * (items.size()-1));
+ ScrollTo(dest);
+ dirty = true;
+ }
+ }
+ }
+
+ return ActiveWindow::OnMouseMove(x,y);
+}
+
+// +--------------------------------------------------------------------+
+
+static bool preselected = false;
+
+int ListBox::OnLButtonDown(int x, int y)
+{
+ if (!captured)
+ captured = SetCapture();
+
+ mouse_x = x - rect.x;
+ mouse_y = y - rect.y;
+
+ int x_scroll_bar = rect.w;
+
+ if (IsScrollVisible())
+ x_scroll_bar -= SCROLL_WIDTH;
+
+ if (mouse_x < x_scroll_bar) {
+ scrolling = SCROLL_NONE;
+
+ if (show_headings && mouse_y < line_height+BORDER_WIDTH+EXTRA_WIDTH) {
+ int next_col_start = 0;
+ int max_column = columns.size()-1;
+
+ for (int column = 0; column < max_column; column++) {
+ next_col_start += GetColumnWidth(column);
+
+ if (mouse_x < next_col_start)
+ break;
+ }
+
+ sort_column = column;
+
+ int& sort_criteria = columns[sort_column]->sort;
+
+ if (sort_criteria != LIST_SORT_NEVER) {
+ if (!sort_criteria)
+ sort_criteria = LIST_SORT_ALPHA_DESCENDING;
+ else
+ sort_criteria = -sort_criteria;
+
+ SortItems();
+ }
+ }
+
+ else {
+ selecting = true;
+ }
+ }
+
+ else {
+ selecting = false;
+
+ if (mouse_y < TRACK_START) {
+ scrolling = SCROLL_UP;
+ Scroll(scrolling, 1);
+ Button::PlaySound(Button::SND_LIST_SCROLL);
+ }
+
+ else if (mouse_y > rect.h-TRACK_START) {
+ scrolling = SCROLL_DOWN;
+ if (top_index < items.size()-1)
+ top_index++;
+ Button::PlaySound(Button::SND_LIST_SCROLL);
+ }
+
+ else if (mouse_y < thumb_pos) {
+ scrolling = SCROLL_PAGE_UP;
+ Scroll(scrolling, page_size);
+ Button::PlaySound(Button::SND_LIST_SCROLL);
+ }
+
+ else if (mouse_y > thumb_pos+THUMB_HEIGHT) {
+ scrolling = SCROLL_PAGE_DOWN;
+ Scroll(scrolling, page_size);
+ Button::PlaySound(Button::SND_LIST_SCROLL);
+ }
+
+ else {
+ scrolling = SCROLL_THUMB;
+ }
+ }
+
+ if (selecting) {
+ list_index = IndexFromPoint(mouse_x, mouse_y);
+ preselected = IsSelected(list_index);
+ if (!multiselect || !Keyboard::KeyDown(VK_SHIFT))
+ ClearSelection();
+ SetSelected(list_index);
+ EnsureVisible(list_index);
+ Button::PlaySound(Button::SND_LIST_SELECT);
+ }
+
+ return ActiveWindow::OnLButtonDown(x,y);
+}
+
+// +--------------------------------------------------------------------+
+
+int ListBox::OnLButtonUp(int x, int y)
+{
+ if (captured) {
+ mouse_x = x-rect.x;
+ mouse_y = y-rect.y;
+
+ if (dragging) {
+ if (mouse_x < 0 || mouse_x > rect.w || mouse_y < 0 || mouse_y > rect.h) {
+ FormWindow* parent_form = (FormWindow*) form;
+
+ if (parent_form) {
+ ActiveWindow* drop_target = parent_form->FindControl(x,y);
+
+ if (drop_target && drop_target->IsEnabled() && drop_target->IsShown())
+ drop_target->OnDragDrop(x,y,this);
+ }
+ }
+ }
+ else if (preselected) {
+ if (multiselect && Keyboard::KeyDown(VK_SHIFT)) {
+ SetSelected(list_index, false);
+ Button::PlaySound(Button::SND_LIST_SELECT);
+ }
+ }
+
+ ReleaseCapture();
+ captured = false;
+
+ Mouse::SetCursor((Mouse::CURSOR) old_cursor);
+ }
+
+ dragging = false;
+ selecting = false;
+
+ return ActiveWindow::OnLButtonUp(x,y);
+}
+
+// +--------------------------------------------------------------------+
+
+int ListBox::OnMouseWheel(int wheel)
+{
+ return ScrollWindow::OnMouseWheel(wheel);
+}
+
+// +--------------------------------------------------------------------+
+
+int ListBox::OnClick()
+{
+ int fire_select = !scrolling;
+
+ if (scrolling == SCROLL_THUMB)
+ scrolling = SCROLL_NONE;
+
+ if (fire_select)
+ return ActiveWindow::OnSelect();
+ else
+ return ActiveWindow::OnClick();
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+int ListBox::OnKeyDown(int vk, int flags)
+{
+ if (selcount == 1 && list_index >= 0 && list_index < items.size()) {
+ ListBoxItem* item = items[list_index];
+
+ if (vk == VK_DOWN) {
+ if (list_index < items.size() - 1) {
+ item->selected = false;
+ list_index++;
+ item = items[list_index];
+ item->selected = true;
+ OnClick();
+ return ActiveWindow::OnKeyDown(vk, flags);
+ }
+ }
+
+ else if (vk == VK_UP) {
+ if (list_index > 0) {
+ item->selected = false;
+ list_index--;
+ item = items[list_index];
+ item->selected = true;
+ OnClick();
+ return ActiveWindow::OnKeyDown(vk, flags);
+ }
+ }
+ }
+
+ return ScrollWindow::OnKeyDown(vk, flags);
+}
+
+// +--------------------------------------------------------------------+
+
+int ListBox::OnDragStart(int x, int y)
+{
+ old_cursor = Mouse::SetCursor(Mouse::DRAG);
+ return ActiveWindow::OnDragStart(x,y);
+}
+
+// +--------------------------------------------------------------------+
+
+int ListBox::OnDragDrop(int x, int y, ActiveWindow* source)
+{
+ if (!dragdrop) return 0;
+
+ ListBox* drag_source = (ListBox*) source;
+
+ if (drag_source) {
+ int max_col = NumColumns();
+
+ if (max_col != drag_source->NumColumns())
+ max_col = 0;
+
+ for (int i = 0; i < drag_source->NumItems(); i++) {
+ if (drag_source->IsSelected(i)) {
+ AddItemWithData(drag_source->GetItemText(i),
+ drag_source->GetItemData(i));
+
+ for (int c = 1; c < max_col; c++) {
+ SetItemText(list_index, c, drag_source->GetItemText(i,c));
+ SetItemData(list_index, c, drag_source->GetItemData(i,c));
+ }
+
+ if (!multiselect)
+ ClearSelection();
+
+ items[list_index]->selected = true;
+ selcount++;
+ }
+ }
+
+ drag_source->RemoveSelectedItems();
+ Button::PlaySound(Button::SND_LIST_DROP);
+ }
+
+ return ActiveWindow::OnDragDrop(x,y,source);
+}
diff --git a/nGenEx/ListBox.h b/nGenEx/ListBox.h
new file mode 100644
index 0000000..289a2fa
--- /dev/null
+++ b/nGenEx/ListBox.h
@@ -0,0 +1,170 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: ListBox.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ ListBox ActiveWindow class
+*/
+
+#ifndef ListBox_h
+#define ListBox_h
+
+#include "Types.h"
+#include "ScrollWindow.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class Bitmap;
+class ListBox;
+class ListBoxCell;
+class ListBoxItem;
+class ListBoxColumn;
+
+// +--------------------------------------------------------------------+
+
+class ListBox : public ScrollWindow
+{
+public:
+ enum SORT { LIST_SORT_NUMERIC_DESCENDING = -2,
+ LIST_SORT_ALPHA_DESCENDING,
+ LIST_SORT_NONE,
+ LIST_SORT_ALPHA_ASCENDING,
+ LIST_SORT_NUMERIC_ASCENDING,
+ LIST_SORT_NEVER
+ };
+
+ enum ALIGN { LIST_ALIGN_LEFT = DT_LEFT,
+ LIST_ALIGN_CENTER = DT_CENTER,
+ LIST_ALIGN_RIGHT = DT_RIGHT
+ };
+
+ enum STYLE { LIST_ITEM_STYLE_PLAIN,
+ LIST_ITEM_STYLE_BOX,
+ LIST_ITEM_STYLE_FILLED_BOX
+ };
+
+ ListBox(ActiveWindow* p, int ax, int ay, int aw, int ah, DWORD aid);
+ ListBox(Screen* s, int ax, int ay, int aw, int ah, DWORD aid);
+ virtual ~ListBox();
+
+ // Operations:
+ virtual void DrawContent(const Rect& ctrl_rect);
+
+ // Event Target Interface:
+ virtual int OnMouseMove(int x, int y);
+ virtual int OnLButtonDown(int x, int y);
+ virtual int OnLButtonUp(int x, int y);
+ virtual int OnMouseWheel(int wheel);
+ virtual int OnClick();
+
+ virtual int OnKeyDown(int vk, int flags);
+
+ // pseudo-events:
+ virtual int OnDragStart(int x, int y);
+ virtual int OnDragDrop(int x, int y, ActiveWindow* source);
+
+ // Property accessors:
+ int NumItems();
+ int NumColumns();
+
+ Text GetItemText(int index);
+ void SetItemText(int index, const char* text);
+ DWORD GetItemData(int index);
+ void SetItemData(int index, DWORD data);
+ Bitmap* GetItemImage(int index);
+ void SetItemImage(int index, Bitmap* img);
+ Color GetItemColor(int index);
+ void SetItemColor(int index, Color c);
+
+ Text GetItemText(int index, int column);
+ void SetItemText(int index, int column, const char* text);
+ DWORD GetItemData(int index, int column);
+ void SetItemData(int index, int column, DWORD data);
+ Bitmap* GetItemImage(int index, int column);
+ void SetItemImage(int index, int column, Bitmap* img);
+
+ int AddItem(const char* text);
+ int AddImage(Bitmap* img);
+ int AddItemWithData(const char* text, int data);
+ void InsertItem(int index, const char* text);
+ void InsertItemWithData(int index, const char* text, int data);
+ void ClearItems();
+ void RemoveItem(int index);
+ void RemoveSelectedItems();
+
+ void AddColumn(const char* title,
+ int width,
+ int align = ListBox::LIST_ALIGN_LEFT,
+ int sort = ListBox::LIST_SORT_NONE);
+
+ Text GetColumnTitle(int index);
+ void SetColumnTitle(int index, const char* title);
+ int GetColumnWidth(int index);
+ void SetColumnWidth(int index, int width);
+ int GetColumnAlign(int index);
+ void SetColumnAlign(int index, int align);
+ int GetColumnSort(int index);
+ void SetColumnSort(int index, int sort);
+ Color GetColumnColor(int index);
+ void SetColumnColor(int index, Color c);
+
+ Color GetItemColor(int index, int column);
+
+ int GetMultiSelect();
+ void SetMultiSelect(int nNewValue);
+ bool GetShowHeadings();
+ void SetShowHeadings(bool nNewValue);
+ Color GetSelectedColor();
+ void SetSelectedColor(Color c);
+
+ int GetItemStyle() const;
+ void SetItemStyle(int style);
+ int GetSelectedStyle() const;
+ void SetSelectedStyle(int style);
+
+ bool IsSelected(int index);
+ void SetSelected(int index, bool bNewValue=true);
+ void ClearSelection();
+
+ int GetSortColumn();
+ void SetSortColumn(int col_index);
+ int GetSortCriteria();
+ void SetSortCriteria(SORT sort);
+ void SortItems();
+ void SizeColumns();
+
+ // read-only:
+ virtual int GetListIndex();
+ virtual int GetLineCount();
+ virtual int GetSelCount();
+ virtual int GetSelection();
+ virtual Text GetSelectedItem();
+
+protected:
+ int IndexFromPoint(int x, int y) const;
+
+ // properties:
+ List<ListBoxItem> items;
+ List<ListBoxColumn> columns;
+
+ bool show_headings;
+ int multiselect;
+ int list_index;
+ int selcount;
+
+ Color selected_color;
+
+ int sort_column;
+ int item_style;
+ int seln_style;
+};
+
+#endif ListBox_h
+
diff --git a/nGenEx/Locale.cpp b/nGenEx/Locale.cpp
new file mode 100644
index 0000000..750e020
--- /dev/null
+++ b/nGenEx/Locale.cpp
@@ -0,0 +1,237 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2006. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Locale.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ 3D Locale (Polygon) Object
+*/
+
+#include "MemDebug.h"
+#include "Locale.h"
+
+void Print(const char* fmt, ...);
+
+// +--------------------------------------------------------------------+
+
+static List<Locale> locales;
+
+// +--------------------------------------------------------------------+
+
+Locale::Locale(const char* l, const char* c, const char* v)
+{
+ ZeroMemory(this, sizeof(Locale));
+ if (l && *l) {
+ strncpy(language, l, 6);
+ char* p = language;
+ while (*p) {
+ *p = tolower(*p);
+ p++;
+ }
+ }
+
+ if (c && *c) {
+ strncpy(country, c, 6);
+ char* p = country;
+ while (*p) {
+ *p = toupper(*p);
+ p++;
+ }
+ }
+
+ if (v && *v) {
+ strncpy(variant, v, 6);
+ char* p = variant;
+ while (*p) {
+ *p = tolower(*p);
+ p++;
+ }
+ }
+
+ locales.append(this);
+}
+
+// +--------------------------------------------------------------------+
+
+Locale::~Locale()
+{
+ locales.remove(this);
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Locale::operator == (const Locale& that) const
+{
+ if (this == &that) return 1;
+
+ return !stricmp(language, that.language) &&
+ !stricmp(country, that.country) &&
+ !stricmp(variant, that.variant);
+}
+
+// +--------------------------------------------------------------------+
+
+Locale*
+Locale::ParseLocale(const char* str)
+{
+ if (str && *str) {
+ int i = 0;
+ char s1[4];
+ char s2[4];
+ char s3[4];
+
+ while (*str && *str != '_' && i < 3) {
+ s1[i] = *str++;
+ i++;
+ }
+ s1[i] = 0;
+ i = 0;
+
+ if (*str == '_')
+ str++;
+
+ while (*str && *str != '_' && i < 3) {
+ s2[i] = *str++;
+ i++;
+ }
+ s2[i] = 0;
+ i = 0;
+
+ if (*str == '_')
+ str++;
+
+ while (*str && *str != '_' && i < 3) {
+ s3[i] = *str++;
+ i++;
+ }
+ s3[i] = 0;
+ i = 0;
+
+ return CreateLocale(s1, s2, s3);
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+Locale*
+Locale::CreateLocale(const char* l, const char* c, const char* v)
+{
+ ListIter<Locale> iter = locales;
+ while (++iter) {
+ Locale* loc = iter.value();
+ if (!stricmp(l, loc->GetLanguage())) {
+ if (c && *c) {
+ if (!stricmp(c, loc->GetCountry())) {
+ if (v && *v) {
+ if (!stricmp(v, loc->GetVariant())) {
+ return loc;
+ }
+ }
+ else {
+ return loc;
+ }
+ }
+ }
+ else {
+ return loc;
+ }
+ }
+ }
+
+ if (l[0]) {
+ if (c[0]) {
+ if (v[0]) {
+ return new(__FILE__,__LINE__) Locale(l, c, v);
+ }
+ return new(__FILE__,__LINE__) Locale(l, c);
+ }
+ return new(__FILE__,__LINE__) Locale(l);
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+const List<Locale>&
+Locale::GetAllLocales()
+{
+ return locales;
+}
+
+// +--------------------------------------------------------------------+
+
+const Text
+Locale::GetFullCode() const
+{
+ Text result = language;
+ if (*country) {
+ result.append("_");
+ result.append(country);
+
+ if (*variant) {
+ result.append("_");
+ result.append(variant);
+ }
+ }
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+static const char* languages[] = {
+ "en", "English",
+ "fr", "French",
+ "de", "German",
+ "it", "Italian",
+ "pt", "Portuguese",
+ "ru", "Russian",
+ "es", "Spanish"
+};
+
+static const char* countries[] = {
+ "US", "USA",
+ "CA", "Canada",
+ "FR", "France",
+ "DE", "Germany",
+ "IT", "Italy",
+ "PT", "Portugal",
+ "RU", "Russia",
+ "ES", "Spain",
+ "UK", "United Kingdom"
+};
+
+const Text
+Locale::GetDisplayName() const
+{
+ Text result;
+ if (*language) {
+ for (int i = 0; i < 14; i += 2) {
+ if (!stricmp(language, languages[i])) {
+ result = languages[i+1];
+ break;
+ }
+ }
+
+ if (*country) {
+ for (int i = 0; i < 18; i += 2) {
+ if (!stricmp(country, countries[i])) {
+ result.append(" - ");
+ result.append(countries[i+1]);
+ break;
+ }
+ }
+ }
+
+ }
+ return result;
+}
+
diff --git a/nGenEx/Locale.h b/nGenEx/Locale.h
new file mode 100644
index 0000000..f649592
--- /dev/null
+++ b/nGenEx/Locale.h
@@ -0,0 +1,53 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2006. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Locale.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Description of locale by ISO language, country, and variant
+*/
+
+#ifndef Locale_h
+#define Locale_h
+
+#include "List.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class Locale
+{
+public:
+ static const char* TYPENAME() { return "Locale"; }
+
+ Locale(const char* language, const char* country=0, const char* variant=0);
+ ~Locale();
+
+ int operator == (const Locale& that) const;
+
+ // Operations:
+ static const List<Locale>& GetAllLocales();
+ static Locale* ParseLocale(const char* str);
+
+ // Property accessors:
+ const char* GetLanguage() const { return language; }
+ const char* GetCountry() const { return country; }
+ const char* GetVariant() const { return variant; }
+ const Text GetFullCode() const;
+ const Text GetDisplayName() const;
+
+
+protected:
+ static Locale* CreateLocale(const char* language, const char* country=0, const char* variant=0);
+ char language[8];
+ char country[8];
+ char variant[8];
+};
+
+#endif Locale_h
+
diff --git a/nGenEx/MCIWave.cpp b/nGenEx/MCIWave.cpp
new file mode 100644
index 0000000..fdab8cd
--- /dev/null
+++ b/nGenEx/MCIWave.cpp
@@ -0,0 +1,155 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: MCIWave.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ MCI Wave Output stuff
+*/
+
+#include "MemDebug.h"
+#include "Types.h"
+
+// +----------------------------------------------------------------------+
+
+void Print(const char* fmt, ...);
+
+// +----------------------------------------------------------------------+
+
+const int MCI_MAX_STR = 128;
+static char ret_str[MCI_MAX_STR];
+static char err_str[MCI_MAX_STR];
+static MCIERROR mci_err;
+static MMRESULT wav_err;
+extern HWND hwndApp;
+
+static int mci_send_string(const char* cmd_str)
+{
+ mci_err = mciSendString(cmd_str, ret_str, sizeof(ret_str), hwndApp);
+ if (mci_err) {
+ if (mciGetErrorString(mci_err, err_str, sizeof(err_str)))
+ Print("Error (%s): '%s'\n", cmd_str, err_str);
+ else
+ Print("Error (%s): %d - UNKNOWN\n", cmd_str, mci_err);
+ return 0;
+ }
+
+ return 1;
+}
+
+// +--------------------------------------------------------------------+
+
+static void print_wav_error()
+{
+ waveOutGetErrorText(wav_err, err_str, MCI_MAX_STR);
+ Print(err_str);
+}
+
+// +--------------------------------------------------------------------+
+
+int load_wave_file(const char* fname, LPWAVEHDR hdr, LPWAVEFORMATEX format)
+{
+ HMMIO hmmio; /* file handle for open file */
+ MMCKINFO mmckinfoParent; /* parent chunk information structure */
+ MMCKINFO mmckinfoSubchunk; /* subchunk information structure */
+ DWORD dwFmtSize; /* size of "fmt" chunk */
+ DWORD dwDataSize; /* size of "data" chunk */
+
+ /*
+ * Open the given file for reading with buffered I/O
+ * using the default internal buffer.
+ */
+ hmmio = mmioOpen((LPSTR) fname, NULL, MMIO_READ | MMIO_ALLOCBUF);
+
+ if (hmmio == NULL) {
+ Print("load_wave_file(): '%s' - Failed to open file.\n", fname);
+ return 0;
+ }
+
+ /*
+ * Locate a "RIFF" chunk with a "WAVE" form type
+ * to make sure the file is a WAVE file.
+ */
+ mmckinfoParent.fccType = mmioFOURCC('W', 'A', 'V', 'E');
+ if (mmioDescend(hmmio, (LPMMCKINFO) &mmckinfoParent, NULL, MMIO_FINDRIFF)) {
+ Print("load_wave_file(): '%s' - This is not a WAVE file.\n", fname);
+ mmioClose(hmmio, 0);
+ return 0;
+ }
+
+ /*
+ * Find the "fmt " chunk (form type "fmt "); it must be
+ * a subchunk of the "RIFF" parent chunk.
+ */
+ mmckinfoSubchunk.ckid = mmioFOURCC('f', 'm', 't', ' ');
+ if (mmioDescend(hmmio, &mmckinfoSubchunk, &mmckinfoParent, MMIO_FINDCHUNK)) {
+ Print("load_wave_file(): '%s' - WAVE file has no \"fmt\" chunk\n", fname);
+ mmioClose(hmmio, 0);
+ return 0;
+ }
+
+ /*
+ * Get the size of the "fmt " chunk--allocate and lock memory for it.
+ */
+ dwFmtSize = mmckinfoSubchunk.cksize;
+
+ /* Read the "fmt " chunk. */
+ if (mmioRead(hmmio, (HPSTR) format, dwFmtSize) != (LRESULT)dwFmtSize) {
+ Print("load_wave_file(): '%s' - Failed to read format chunk.\n", fname);
+ mmioClose(hmmio, 0);
+ return 0;
+ }
+
+ /* Ascend out of the "fmt " subchunk. */
+ mmioAscend(hmmio, &mmckinfoSubchunk, 0);
+
+ /*
+ * Find the data subchunk. The current file position
+ * should be at the beginning of the data chunk.
+ */
+ mmckinfoSubchunk.ckid = mmioFOURCC('d', 'a', 't', 'a');
+ if (mmioDescend(hmmio, &mmckinfoSubchunk, &mmckinfoParent, MMIO_FINDCHUNK)) {
+ Print("load_wave_file(): '%s' - WAVE file has no data chunk.\n", fname);
+ mmioClose(hmmio, 0);
+ return 0;
+ }
+
+ /* Get the size of the data subchunk. */
+ dwDataSize = mmckinfoSubchunk.cksize;
+ if (dwDataSize == 0L) {
+ Print("load_wave_file(): '%s' - The data chunk contains no data.\n", fname);
+ mmioClose(hmmio, 0);
+ return 0;
+ }
+
+ // allocate the data block:
+ hdr->lpData = (LPSTR) new(__FILE__,__LINE__) BYTE[dwDataSize];
+ hdr->dwBufferLength = dwDataSize;
+
+ /* Read the waveform data subchunk. */
+ if (mmioRead(hmmio, (HPSTR) hdr->lpData, dwDataSize) != (LRESULT)dwDataSize) {
+ Print("load_wave_file(): '%s' - Failed to read data chunk.\n", fname);
+ mmioClose(hmmio, 0);
+ return 0;
+ }
+
+ /* Close the file. */
+ mmioClose(hmmio, 0);
+
+ return 1;
+}
+
+// +--------------------------------------------------------------------+
+
+void delete_wave_file(LPWAVEHDR hdr, LPWAVEFORMATEX format)
+{
+ if (hdr) {
+ delete hdr->lpData;
+ hdr->lpData = 0;
+ }
+}
diff --git a/nGenEx/MCIWave.h b/nGenEx/MCIWave.h
new file mode 100644
index 0000000..d2ea654
--- /dev/null
+++ b/nGenEx/MCIWave.h
@@ -0,0 +1,25 @@
+/* Project nGen
+ John DiCamillo Software Consulting
+ Copyright © 1997-2000. All Rights Reserved.
+
+ SUBSYSTEM: nGen.lib
+ FILE: MCIWave.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ MCI Wave Output stuff
+*/
+
+#ifndef MCI_WAVE_H
+#define MCI_WAVE_H
+
+// +--------------------------------------------------------------------+
+
+int load_wave_file(const char* fname, LPWAVEHDR hdr, LPWAVEFORMATEX format);
+void delete_wave_file(LPWAVEHDR hdr, LPWAVEFORMATEX format);
+
+// +--------------------------------------------------------------------+
+
+#endif
diff --git a/nGenEx/MachineInfo.cpp b/nGenEx/MachineInfo.cpp
new file mode 100644
index 0000000..89e6a47
--- /dev/null
+++ b/nGenEx/MachineInfo.cpp
@@ -0,0 +1,795 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: MachineInfo.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Collect and Display Machine, OS, and Driver Information
+*/
+
+#include "MemDebug.h"
+#include "MachineInfo.h"
+#include "Timesnap.h"
+
+#define DIRECTINPUT_VERSION 0x0700
+
+#include <stdio.h>
+#include <ddraw.h>
+#include <d3d9.h>
+#include <dinput.h>
+#include <winver.h>
+
+// +--------------------------------------------------------------------+
+
+void Print(const char* fmt, ...);
+
+static int cpu_class=-1;
+static int cpu_speed=-1;
+static int platform=-1;
+static int dx_version=-1;
+static int total_ram=-1;
+
+static OSVERSIONINFO os_ver = { sizeof(OSVERSIONINFO) };
+static SYSTEM_INFO cpu_info;
+static MEMORYSTATUS mem_info = { sizeof(MEMORYSTATUS) };
+
+// +--------------------------------------------------------------------+
+
+const char*
+MachineInfo::GetShortDescription()
+{
+ static char desc[256];
+
+ static const char* cpu_names[] = {
+ "8088",
+ "8086",
+ "80286",
+ "80386",
+ "80486",
+ "Pentium",
+ "Pentium II",
+ "Pentium 3",
+ "Pentium 4"
+ };
+
+ static const char* os_names[] = {
+ "DOS",
+ "Windows 95",
+ "Windows 98",
+ "Windows NT",
+ "Windows 2000",
+ "Windows XP"
+ };
+
+ int cpu_index = GetCpuClass();
+ if (cpu_index < 0) cpu_index = 0;
+ else if (cpu_index > 8) cpu_index = 8;
+
+ int os_index = GetPlatform();
+ if (os_index < 0) os_index = 0;
+ else if (os_index > 5) os_index = 5;
+
+ sprintf(desc, "%s %d MHz %d MB RAM %s",
+ cpu_names[cpu_index],
+ GetCpuSpeed(),
+ GetTotalRam(),
+ os_names[os_index]);
+
+ return desc;
+}
+
+// +--------------------------------------------------------------------+
+
+static void DescribeCpuMake();
+static void DescribeOwner95();
+static void DescribeOwnerNT();
+static void DescribeDrivers95(const char* sType);
+static void DescribeDriversNT(const char* sType);
+static void DescribeDriverVersion(const char* file);
+static void DescribeDXVersion(const char* component);
+
+// wait for at least target_time,
+// return the exact amount of time actually waited:
+
+static double SpinWait(double target_time)
+{
+ double actual_time = 0;
+
+ LARGE_INTEGER ifreq;
+ LARGE_INTEGER cnt1;
+ LARGE_INTEGER cnt2;
+
+ QueryPerformanceFrequency(&ifreq);
+ double freq = (double) ifreq.QuadPart;
+
+ QueryPerformanceCounter(&cnt1);
+
+ do {
+ QueryPerformanceCounter(&cnt2);
+
+ double delta = (double) (cnt2.QuadPart - cnt1.QuadPart);
+ actual_time = delta / freq;
+ }
+ while (actual_time < target_time);
+
+ return actual_time;
+}
+
+static double CalcCpuSpeed()
+{
+ DWORD clock1 = 0;
+ DWORD clock2 = 0;
+
+ TIMESNAP(clock1);
+
+ double seconds = SpinWait(0.1);
+
+ TIMESNAP(clock2);
+
+ double clocks = clock2 - clock1;
+
+ return (clocks/seconds);
+}
+
+/****************************************************************************
+ *
+ * GetDXVersion
+ *
+ * This function returns
+ * 0 Insufficient DirectX installed
+ * 9 At least DirectX 9 installed.
+ *
+ ****************************************************************************/
+
+DWORD GetDXVersion()
+{
+ HRESULT hr = 0;
+ HINSTANCE DDHinst = 0;
+ LPDIRECT3D9 d3d9 = 0;
+ OSVERSIONINFO osVer = { sizeof(OSVERSIONINFO) };
+
+ // First get the windows platform
+
+ if (!GetVersionEx(&osVer))
+ return 0;
+
+ // NT versions do not support DirectX 9
+ if (osVer.dwPlatformId == VER_PLATFORM_WIN32_NT) {
+ if (osVer.dwMajorVersion <= 4)
+ return 0;
+ }
+
+ DDHinst = LoadLibrary("D3D9.DLL");
+ if (DDHinst == 0) {
+ return 0;
+ }
+
+ FreeLibrary(DDHinst);
+ return 9;
+}
+
+
+// +--------------------------------------------------------------------+
+
+int
+MachineInfo::GetCpuClass()
+{
+ if (cpu_class < 0) {
+ GetSystemInfo(&cpu_info);
+
+ if (cpu_info.wProcessorArchitecture != PROCESSOR_ARCHITECTURE_INTEL ||
+ cpu_info.dwProcessorType < PROCESSOR_INTEL_PENTIUM)
+ Print("INCOMPATIBLE CPU TYPE!\n");
+
+ if (GetPlatform() < OS_WINNT)
+ cpu_class = CPU_P5;
+
+ else
+ cpu_class = cpu_info.wProcessorLevel;
+ }
+
+ return cpu_class;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+MachineInfo::GetCpuSpeed()
+{
+ if (cpu_speed < 0) {
+ cpu_speed = (int) (CalcCpuSpeed() / 1e6);
+ }
+
+ return cpu_speed;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+MachineInfo::GetTotalRam()
+{
+ if (total_ram < 0) {
+ GlobalMemoryStatus(&mem_info);
+ total_ram = (int) (mem_info.dwTotalPhys/(1024*1024)) + 1;
+ }
+
+ return total_ram;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+MachineInfo::GetPlatform()
+{
+ if (platform < 0) {
+ GetVersionEx(&os_ver);
+
+ switch (os_ver.dwPlatformId) {
+ default:
+ case VER_PLATFORM_WIN32s: {
+ char msg[256];
+ sprintf(msg, "Invalid Operating System Platform: %d\n", os_ver.dwPlatformId);
+ Print(msg);
+ }
+ break;
+
+ case VER_PLATFORM_WIN32_WINDOWS:
+ if (os_ver.dwMajorVersion == 4 && os_ver.dwMinorVersion == 0)
+ platform = OS_WIN95;
+ else
+ platform = OS_WIN98;
+ break;
+
+ case VER_PLATFORM_WIN32_NT:
+ if (os_ver.dwMajorVersion == 4)
+ platform = OS_WINNT;
+ else if (os_ver.dwMajorVersion == 5 && os_ver.dwMinorVersion == 0)
+ platform = OS_WIN2K;
+ else if (os_ver.dwMajorVersion >= 5)
+ platform = OS_WINXP;
+
+ else {
+ platform = OS_INVALID;
+
+ Print("Invalid Operating System Platform (NT-series): %d.%d\n", os_ver.dwMajorVersion, os_ver.dwMinorVersion);
+ }
+
+ break;
+ }
+ }
+
+ return platform;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+MachineInfo::GetDirectXVersion()
+{
+ if (dx_version < 0) {
+ dx_version = GetDXVersion();
+ }
+
+ return dx_version;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MachineInfo::DescribeMachine()
+{
+ GetPlatform();
+ GetCpuClass();
+
+ Print("+====================================================================+\n");
+ Print("| |\n");
+
+ char txt[256];
+
+ switch (platform) {
+ case OS_WIN95:
+ sprintf(txt, "Windows 95 version %d.%d.%d %s",
+ os_ver.dwMajorVersion,
+ os_ver.dwMinorVersion,
+ LOWORD(os_ver.dwBuildNumber),
+ os_ver.szCSDVersion);
+ break;
+
+ case OS_WIN98:
+ sprintf(txt, "Windows 98 version %d.%d.%d %s",
+ os_ver.dwMajorVersion,
+ os_ver.dwMinorVersion,
+ LOWORD(os_ver.dwBuildNumber),
+ os_ver.szCSDVersion);
+ break;
+
+ case OS_WINNT:
+ sprintf(txt, "Windows NT %d.%d (Build %d) %s",
+ os_ver.dwMajorVersion,
+ os_ver.dwMinorVersion,
+ os_ver.dwBuildNumber,
+ os_ver.szCSDVersion);
+ break;
+
+ case OS_WIN2K:
+ sprintf(txt, "Windows 2000 %d.%d (Build %d) %s",
+ os_ver.dwMajorVersion,
+ os_ver.dwMinorVersion,
+ os_ver.dwBuildNumber,
+ os_ver.szCSDVersion);
+
+ case OS_WINXP:
+ sprintf(txt, "Windows XP %d.%d (Build %d) %s",
+ os_ver.dwMajorVersion,
+ os_ver.dwMinorVersion,
+ os_ver.dwBuildNumber,
+ os_ver.szCSDVersion);
+ break;
+
+ default:
+ sprintf(txt, "Unknown Operating System Platform");
+ break;
+ }
+
+ Print("| %-66s |\n", txt);
+ Print("| |\n");
+
+ if (platform == OS_WIN95 || platform == OS_WIN98)
+ DescribeOwner95();
+ else
+ DescribeOwnerNT();
+
+ sprintf(txt, "CPUs Detected: %d CPU Level: %d.%d.%d CPU Speed: %d",
+ cpu_info.dwNumberOfProcessors,
+ cpu_info.wProcessorLevel,
+ cpu_info.wProcessorRevision >> 8,
+ cpu_info.wProcessorRevision & 0xff,
+ GetCpuSpeed() + 1);
+
+ Print("| %-66s |\n", txt);
+ DescribeCpuMake();
+
+ GlobalMemoryStatus(&mem_info);
+ total_ram = (int) (mem_info.dwTotalPhys/(1024*1024)) + 1;
+ int swap_max = (int) (mem_info.dwTotalPageFile/(1024*1024));
+ int swap_avail = (int) (mem_info.dwAvailPageFile/(1024*1024));
+
+ sprintf(txt, "%d MB RAM %d MB Max Swap %d MB Avail Swap",
+ total_ram, swap_max, swap_avail);
+
+
+ Print("| %-66s |\n", txt);
+
+ Print("| |\n");
+ Print("| DirectX %d installed. |\n",
+ GetDirectXVersion());
+ DescribeDXVersion("DDRAW");
+ DescribeDXVersion("D3DIM");
+ DescribeDXVersion("DINPUT");
+ DescribeDXVersion("DPLAY");
+ DescribeDXVersion("DSOUND");
+ DescribeDXVersion("DMUSIC");
+ DescribeDXVersion("DSHOW");
+ Print("| |\n");
+
+
+ if (platform == OS_WIN95 || platform == OS_WIN98) {
+ DescribeDrivers95("Display");
+ DescribeDrivers95("Media");
+ DescribeDrivers95("Monitor");
+ DescribeDrivers95("Multimedia");
+ }
+ else {
+ DescribeDriversNT("");
+ }
+
+ Print("+====================================================================+\n");
+ Print("\n");
+}
+
+// +--------------------------------------------------------------------+
+
+static void DescribeCpuMake()
+{
+ HKEY hkWin;
+ char sProcessor[256] = "";
+ char sMMXInfo[256] = "";
+ char sVendor[256] = "";
+ DWORD dwSize;
+
+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ "Hardware\\Description\\System\\CentralProcessor\\0",
+ 0,
+ KEY_READ,
+ &hkWin) == ERROR_SUCCESS) {
+
+ dwSize = 256;
+ RegQueryValueEx(hkWin,
+ "Identifier",
+ NULL,
+ NULL,
+ (LPBYTE) sProcessor,
+ &dwSize);
+
+ dwSize = 256;
+ RegQueryValueEx(hkWin,
+ "MMXIdentifier",
+ NULL,
+ NULL,
+ (LPBYTE) sMMXInfo,
+ &dwSize);
+
+ dwSize = 256;
+ RegQueryValueEx(hkWin,
+ "VendorIdentifier",
+ NULL,
+ NULL,
+ (LPBYTE) sVendor,
+ &dwSize);
+
+ RegCloseKey(hkWin);
+ }
+
+ if (sProcessor[0]) Print("| %-66s |\n", sProcessor);
+ if (sMMXInfo[0]) Print("| %-66s |\n", sMMXInfo);
+ if (sVendor[0]) Print("| %-66s |\n", sVendor);
+
+ if (sProcessor[0] || sMMXInfo[0] || sVendor[0])
+ Print("| |\n");
+}
+
+// +--------------------------------------------------------------------+
+
+static void DescribeOwner95()
+{
+ HKEY hkWin;
+ char sRegisteredOwner[256] = "";
+ char sRegisteredOrganization[256] = "";
+ DWORD dwSize;
+
+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ "SOFTWARE\\Microsoft\\Windows\\CurrentVersion",
+ 0,
+ KEY_READ,
+ &hkWin) == ERROR_SUCCESS) {
+
+ dwSize = 256;
+ RegQueryValueEx(hkWin,
+ "RegisteredOwner",
+ NULL,
+ NULL,
+ (LPBYTE) sRegisteredOwner,
+ &dwSize);
+
+ dwSize = 256;
+ RegQueryValueEx(hkWin,
+ "RegisteredOrganization",
+ NULL,
+ NULL,
+ (LPBYTE) sRegisteredOrganization,
+ &dwSize);
+
+ RegCloseKey(hkWin);
+ }
+ else {
+ Print("Could not access registered owner\n");
+ }
+
+ if (sRegisteredOwner[0]) {
+ char txt[256];
+ sprintf(txt, "Registered Owner: %s, %s", sRegisteredOwner, sRegisteredOrganization);
+ Print("| %-66s |\n", txt);
+ Print("| |\n");
+ }
+}
+
+static void DescribeOwnerNT()
+{
+ HKEY hkWin;
+ char sRegisteredOwner[256] = "";
+ char sRegisteredOrganization[256] = "";
+ DWORD dwSize;
+
+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
+ 0,
+ KEY_READ,
+ &hkWin) == ERROR_SUCCESS) {
+
+ dwSize = 256;
+ RegQueryValueEx(hkWin,
+ "RegisteredOwner",
+ NULL,
+ NULL,
+ (LPBYTE) sRegisteredOwner,
+ &dwSize);
+
+ dwSize = 256;
+ RegQueryValueEx(hkWin,
+ "RegisteredOrganization",
+ NULL,
+ NULL,
+ (LPBYTE) sRegisteredOrganization,
+ &dwSize);
+
+ RegCloseKey(hkWin);
+ }
+ else {
+ Print("Could not access registered owner\n");
+ }
+
+ if (sRegisteredOwner[0]) {
+ char txt[256];
+ sprintf(txt, "Registered Owner: %s, %s", sRegisteredOwner, sRegisteredOrganization);
+ Print("| %-66s |\n", txt);
+ Print("| |\n");
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+static void DescribeDrivers95(const char* sType)
+{
+ HKEY hkWin, hkSub;
+ int nKey = 0;
+ char sKey[256];
+ char sSub[256];
+ char sDriver[256];
+ char txt[256];
+ DWORD dwSize;
+ int worked;
+
+ // describe the video driver(s):
+ do {
+ worked = 0;
+
+ sprintf(sKey, "System\\CurrentControlSet\\Services\\Class\\%s\\%04X", sType, nKey);
+ sprintf(sSub, "System\\CurrentControlSet\\Services\\Class\\%s\\%04X\\DEFAULT", sType, nKey);
+
+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ sKey,
+ 0,
+ KEY_READ,
+ &hkWin) == ERROR_SUCCESS) {
+
+ dwSize = 256;
+ RegQueryValueEx(hkWin,
+ "DriverDesc",
+ NULL,
+ NULL,
+ (LPBYTE) sDriver,
+ &dwSize);
+
+ if (sDriver[0]) {
+ sprintf(txt, "* %s", sDriver);
+ Print("| %-66s |\n", txt);
+ worked = 1;
+ }
+
+ // try to find the driver file name:
+ if (worked) {
+ ZeroMemory(sDriver, sizeof(sDriver));
+
+ dwSize = 256;
+ DWORD err = RegQueryValueEx(hkWin, "Driver", NULL, NULL, (LPBYTE) sDriver, &dwSize);
+
+ if (err != ERROR_SUCCESS) {
+ dwSize = 256;
+ err = RegQueryValueEx(hkWin, "DeviceDriver", NULL, NULL, (LPBYTE) sDriver, &dwSize);
+ }
+
+ if (err != ERROR_SUCCESS) {
+ dwSize = 256;
+ err = RegQueryValueEx(hkWin, "drv", NULL, NULL, (LPBYTE) sDriver, &dwSize);
+ }
+
+ if (err != ERROR_SUCCESS) {
+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ sSub,
+ 0,
+ KEY_READ,
+ &hkSub) == ERROR_SUCCESS) {
+
+ dwSize = 256;
+ err = RegQueryValueEx(hkSub, "drv", NULL, NULL, (LPBYTE) sDriver, &dwSize);
+
+ RegCloseKey(hkSub);
+ }
+ }
+
+ // if we found it, try to display version info:
+ if (err == ERROR_SUCCESS) {
+ DescribeDriverVersion(sDriver);
+ }
+
+ Print("| |\n");
+ }
+
+ RegCloseKey(hkWin);
+ }
+
+ nKey++;
+ }
+ while (worked);
+}
+
+static void DescribeDriversNT(const char* sType)
+{
+ Print("| |\n");
+
+ HKEY hkWin;
+ char sVideo[256] = "";
+ char sDriver[256] = "";
+ DWORD dwSize;
+
+ // find the pointer to the video driver:
+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ "HARDWARE\\DEVICEMAP\\VIDEO",
+ 0,
+ KEY_READ,
+ &hkWin) == ERROR_SUCCESS) {
+
+ dwSize = 256;
+ RegQueryValueEx(hkWin,
+ "\\Device\\Video0",
+ NULL,
+ NULL,
+ (LPBYTE) sVideo,
+ &dwSize);
+
+ RegCloseKey(hkWin);
+ }
+
+ // follow the pointer and get the driver description:
+ if (dwSize && sVideo[0]) {
+ const char* sLeader = "\\REGISTRY\\Machine\\";
+ int nLeader = strlen(sLeader);
+
+ if (strnicmp(sVideo, sLeader, nLeader) == 0) {
+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ sVideo + nLeader,
+ 0,
+ KEY_READ,
+ &hkWin) == ERROR_SUCCESS) {
+
+ dwSize = 256;
+ RegQueryValueEx(hkWin,
+ "Device Description",
+ NULL,
+ NULL,
+ (LPBYTE) sDriver,
+ &dwSize);
+
+ RegCloseKey(hkWin);
+ }
+ }
+ }
+
+ if (sDriver[0]) {
+ Print("| %-66s |\n", sDriver);
+ Print("| |\n");
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+static char sTranslation[16];
+
+static void GetTranslation(const LPBYTE pBlock)
+{
+ LPBYTE sData = NULL;
+ UINT lenData = 0;
+
+ if (VerQueryValue(pBlock, "\\VarFileInfo\\Translation",
+ (LPVOID*) &sData, &lenData)) {
+
+ if (lenData && sData) {
+ sprintf(sTranslation, "%02X%02X%02X%02X", sData[1], sData[0], sData[3], sData[2]);
+ }
+ }
+}
+
+void DisplayVersionString(const LPBYTE pBlock, LPTSTR sSection)
+{
+ char txt[256];
+ char sFullSection[256];
+
+ sprintf(sFullSection, "\\StringFileInfo\\%s\\%s", sTranslation, sSection);
+
+ LPBYTE sData = NULL;
+ UINT lenData = 0;
+ DWORD dwErr = 0;
+
+ if (VerQueryValue(pBlock, sFullSection, (LPVOID*) &sData, &lenData)) {
+ if (lenData && sData) {
+ sprintf(txt, "%-16s %s", sSection, sData);
+ Print("| %-60s |\n", txt);
+ }
+ }
+}
+
+static void DescribeDriverVersion(const char* file)
+{
+ DWORD dwHandle = 0;
+ TCHAR szFile[512];
+
+ strcpy(szFile, file);
+
+ int nBytes = GetFileVersionInfoSize(szFile, &dwHandle);
+
+ if (nBytes <= 0) {
+ char szWinDir[256];
+ GetSystemDirectory(szWinDir, 256);
+ sprintf(szFile, "%s\\%s", szWinDir, file);
+
+ nBytes = GetFileVersionInfoSize(szFile, &dwHandle);
+
+ if (nBytes <= 0)
+ return;
+ }
+
+ LPBYTE pBlock = new(__FILE__,__LINE__) BYTE[nBytes];
+
+ if (pBlock && GetFileVersionInfo(szFile, dwHandle, nBytes, (LPVOID) pBlock)) {
+ GetTranslation(pBlock);
+ DisplayVersionString(pBlock, "CompanyName");
+// DisplayVersionString(pBlock, "FileDescription");
+ DisplayVersionString(pBlock, "FileVersion");
+// DisplayVersionString(pBlock, "InternalName");
+// DisplayVersionString(pBlock, "LegalCopyright");
+// DisplayVersionString(pBlock, "OriginalFilename");
+// DisplayVersionString(pBlock, "ProductName");
+// DisplayVersionString(pBlock, "ProductVersion");
+// DisplayVersionString(pBlock, "Comments");
+// DisplayVersionString(pBlock, "LegalTrademarks");
+// DisplayVersionString(pBlock, "PrivateBuild");
+// DisplayVersionString(pBlock, "SpecialBuild");
+ }
+
+ delete [] pBlock;
+}
+
+static void DescribeDXVersion(const char* component)
+{
+ DWORD dwHandle = 0;
+ char szFile[512];
+ char szWinDir[512];
+
+ GetSystemDirectory(szWinDir, 512);
+
+ sprintf(szFile, "%s\\%s.dll", szWinDir, component);
+
+ int nBytes = GetFileVersionInfoSize(szFile, &dwHandle);
+
+ if (nBytes <= 0) {
+ return;
+ }
+
+ LPBYTE pBlock = new(__FILE__,__LINE__) BYTE[nBytes];
+
+ if (pBlock && GetFileVersionInfo(szFile, dwHandle, nBytes, (LPVOID) pBlock)) {
+ GetTranslation(pBlock);
+
+ char txt[256];
+ char sFullSection[256];
+ LPBYTE sData = NULL;
+ UINT lenData = 0;
+ DWORD dwErr = 0;
+
+ sprintf(sFullSection, "\\StringFileInfo\\%s\\FileVersion", sTranslation);
+
+ if (VerQueryValue(pBlock, sFullSection, (LPVOID*) &sData, &lenData)) {
+ if (lenData && sData) {
+ sprintf(txt, "%-8s%s", component, sData);
+ Print("| %-64s |\n", txt);
+ }
+ }
+ }
+
+ delete [] pBlock;
+} \ No newline at end of file
diff --git a/nGenEx/MachineInfo.h b/nGenEx/MachineInfo.h
new file mode 100644
index 0000000..58f26f4
--- /dev/null
+++ b/nGenEx/MachineInfo.h
@@ -0,0 +1,42 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: MachineInfo.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Collect and Display Machine, OS, and Driver Information
+*/
+
+#ifndef MachineInfo_h
+#define MachineInfo_h
+
+#include "Types.h"
+
+// +--------------------------------------------------------------------+
+
+class MachineInfo
+{
+public:
+ enum { CPU_INVALID, CPU_P5=5, CPU_P6=6, CPU_P7=7, CPU_PLUS };
+ enum { OS_INVALID, OS_WIN95, OS_WIN98, OS_WINNT, OS_WIN2K, OS_WINXP };
+ enum { DX_NONE, DX_3=3, DX_5=5, DX_6=6, DX_7=7, DX_8=8, DX_9=9, DX_PLUS };
+
+ static int GetCpuClass();
+ static int GetCpuSpeed();
+ static int GetTotalRam();
+ static int GetPlatform();
+ static int GetDirectXVersion();
+
+ static void DescribeMachine();
+
+ static const char* GetShortDescription();
+};
+
+// +--------------------------------------------------------------------+
+
+#endif MachineInfo_h \ No newline at end of file
diff --git a/nGenEx/Menu.cpp b/nGenEx/Menu.cpp
new file mode 100644
index 0000000..9daced4
--- /dev/null
+++ b/nGenEx/Menu.cpp
@@ -0,0 +1,143 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Menu.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Simple menu hierarchy class
+*/
+
+#include "MemDebug.h"
+#include "Menu.h"
+
+// +--------------------------------------------------------------------+
+
+void
+Menu::AddItem(Text label, DWORD value, bool enabled)
+{
+ MenuItem* item = new(__FILE__,__LINE__) MenuItem(label, value, enabled);
+
+ if (item) {
+ item->menu = this;
+ items.append(item);
+ }
+}
+
+void
+Menu::AddItem(MenuItem* item)
+{
+ if (item->submenu)
+ item->submenu->SetParent(this);
+ item->menu = this;
+ items.append(item);
+}
+
+void
+Menu::AddMenu(Text label, Menu* menu, DWORD value)
+{
+ MenuItem* item = new(__FILE__,__LINE__) MenuItem(label, value);
+
+ if (item) {
+ item->menu = this;
+ item->submenu = menu;
+ menu->parent = this;
+
+ items.append(item);
+ }
+}
+
+MenuItem*
+Menu::GetItem(int index)
+{
+ if (index >= 0 && index < items.size())
+ return items[index];
+
+ return 0;
+}
+
+void
+Menu::SetItem(int index, MenuItem* item)
+{
+ if (item && index >= 0 && index < items.size())
+ items[index] = item;
+}
+
+int
+Menu::NumItems() const
+{
+ return items.size();
+}
+
+void
+Menu::ClearItems()
+{
+ items.destroy();
+}
+
+
+// +--------------------------------------------------------------------+
+
+MenuItem::MenuItem(Text label, DWORD value, bool e)
+ : text(label), data(value), enabled(e), submenu(0), selected(0)
+{ }
+
+MenuItem::~MenuItem()
+{ }
+
+// +--------------------------------------------------------------------+
+
+Menu*
+MenuHistory::GetCurrent()
+{
+ int n = history.size();
+
+ if (n)
+ return history[n-1];
+
+ return 0;
+}
+
+Menu*
+MenuHistory::GetLevel(int n)
+{
+ if (n >= 0 && n < history.size())
+ return history[n];
+
+ return 0;
+}
+
+Menu*
+MenuHistory::Find(const char* title)
+{
+ for (int i = 0; i < history.size(); i++)
+ if (history[i]->GetTitle() == title)
+ return history[i];
+
+ return 0;
+}
+
+void
+MenuHistory::Pop()
+{
+ int n = history.size();
+
+ if (n)
+ history.removeIndex(n-1);
+}
+
+void
+MenuHistory::Push(Menu* menu)
+{
+ history.append(menu);
+}
+
+void
+MenuHistory::Clear()
+{
+ history.clear();
+}
diff --git a/nGenEx/Menu.h b/nGenEx/Menu.h
new file mode 100644
index 0000000..649784b
--- /dev/null
+++ b/nGenEx/Menu.h
@@ -0,0 +1,124 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Menu.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Simple menu hierarchy class
+*/
+
+#ifndef Menu_h
+#define Menu_h
+
+#include "Types.h"
+#include "List.h"
+#include "Text.h"
+
+// +-------------------------------------------------------------------+
+
+class Menu;
+class MenuItem;
+class MenuHistory;
+
+// +-------------------------------------------------------------------+
+
+class Menu
+{
+public:
+ static const char* TYPENAME() { return "Menu"; }
+
+ Menu() { }
+ Menu(Text t) : title(t) { }
+ virtual ~Menu() { items.destroy(); }
+
+ virtual Text GetTitle() const { return title; }
+ virtual void SetTitle(Text t) { title = t; }
+ virtual Menu* GetParent() const { return parent; }
+ virtual void SetParent(Menu* p) { parent = p; }
+
+ virtual void AddItem(Text label, DWORD value=0, bool enabled=true);
+ virtual void AddItem(MenuItem* item);
+ virtual void AddMenu(Text label, Menu* menu, DWORD value=0);
+ virtual MenuItem* GetItem(int index);
+ virtual void SetItem(int index, MenuItem* item);
+ virtual int NumItems() const;
+ virtual void ClearItems();
+
+ ListIter<MenuItem> GetItems() { return items; }
+
+protected:
+ Text title;
+ List<MenuItem> items;
+ Menu* parent;
+
+ friend class MenuItem;
+};
+
+// +-------------------------------------------------------------------+
+
+class MenuItem
+{
+public:
+ static const char* TYPENAME() { return "MenuItem"; }
+
+ MenuItem(Text label, DWORD value=0, bool enabled=true);
+ virtual ~MenuItem();
+
+ virtual Text GetText() const { return text; }
+ virtual void SetText(Text t) { text = t; }
+
+ virtual DWORD GetData() const { return data; }
+ virtual void SetData(DWORD d) { data = d; }
+
+ virtual int GetEnabled() const { return enabled; }
+ virtual void SetEnabled(int e) { enabled = e; }
+
+ virtual int GetSelected() const { return selected; }
+ virtual void SetSelected(int s) { selected = s; }
+
+ virtual Menu* GetMenu() const { return menu; }
+ virtual void SetMenu(Menu* m) { menu = m; }
+
+ virtual Menu* GetSubmenu() const { return submenu; }
+ virtual void SetSubmenu(Menu* s) { submenu = s; }
+
+protected:
+ Text text;
+ DWORD data;
+ int enabled;
+ int selected;
+
+ Menu* menu;
+ Menu* submenu;
+
+ friend class Menu;
+};
+
+// +-------------------------------------------------------------------+
+
+class MenuHistory
+{
+public:
+ static const char* TYPENAME() { return "MenuHistory"; }
+
+ MenuHistory() { }
+ virtual ~MenuHistory() { history.clear(); }
+
+ virtual Menu* GetCurrent();
+ virtual Menu* GetLevel(int n);
+ virtual Menu* Find(const char* title);
+ virtual void Pop();
+ virtual void Push(Menu* menu);
+ virtual void Clear();
+
+private:
+ List<Menu> history;
+};
+
+#endif Menu_h
+
diff --git a/nGenEx/MotionController.h b/nGenEx/MotionController.h
new file mode 100644
index 0000000..b6eab6f
--- /dev/null
+++ b/nGenEx/MotionController.h
@@ -0,0 +1,231 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: MotionController.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Abstract MotionController class (hides details of Joystick, Keyboard, etc.)
+*/
+
+#ifndef MoCon_h
+#define MoCon_h
+
+// +--------------------------------------------------------------------+
+
+struct KeyMapEntry
+{
+ static const char* TYPENAME() { return "KeyMapEntry"; }
+
+ KeyMapEntry() : act(0), key(0), alt(0), joy(0) { }
+ KeyMapEntry(int a, int k, int s=0, int j=0) : act(a), key(k), alt(s), joy(j) { }
+
+ int operator==(const KeyMapEntry& k) const { return act==k.act && key==k.key && alt==k.alt && joy==k.joy; }
+ int operator!=(const KeyMapEntry& k) const { return !(*this==k); }
+
+ int act;
+ int key;
+ int alt;
+ int joy;
+};
+
+// +--------------------------------------------------------------------+
+
+const int KEY_MAP_SIZE = 256;
+const int KEY_BASE_SIZE = 64;
+const int KEY_USER_SIZE = KEY_MAP_SIZE - KEY_BASE_SIZE;
+
+const int KEY_MAP_BASE = 0;
+const int KEY_MAP_END = KEY_MAP_BASE + KEY_BASE_SIZE - 1;
+
+const int KEY_USER_BASE = KEY_MAP_END + 1;
+const int KEY_USER_END = KEY_USER_BASE + KEY_USER_SIZE - 1;
+
+const int KEY_MAP_FIRST = KEY_MAP_BASE;
+const int KEY_MAP_LAST = KEY_MAP_BASE + KEY_MAP_SIZE - 1;
+
+// MAP NAMES:
+
+const int KEY_PLUS_X = 1;
+const int KEY_MINUS_X = 2;
+const int KEY_PLUS_Y = 3;
+const int KEY_MINUS_Y = 4;
+const int KEY_PLUS_Z = 5;
+const int KEY_MINUS_Z = 6;
+
+const int KEY_PITCH_UP = 7;
+const int KEY_PITCH_DOWN = 8;
+const int KEY_YAW_LEFT = 9;
+const int KEY_YAW_RIGHT = 10;
+const int KEY_ROLL_LEFT = 11;
+const int KEY_ROLL_RIGHT = 12;
+const int KEY_CENTER = 13;
+const int KEY_ROLL_ENABLE = 14;
+
+const int KEY_ACTION_0 = 15;
+const int KEY_ACTION_1 = 16;
+const int KEY_ACTION_2 = 17;
+const int KEY_ACTION_3 = 18;
+
+const int KEY_CONTROL_MODEL = 19;
+const int KEY_MOUSE_SELECT = 20;
+const int KEY_MOUSE_SENSE = 21;
+const int KEY_MOUSE_SWAP = 22;
+const int KEY_MOUSE_INVERT = 23;
+const int KEY_MOUSE_ACTIVE = 24;
+
+const int KEY_JOY_SELECT = 25;
+const int KEY_JOY_THROTTLE = 26;
+const int KEY_JOY_RUDDER = 27;
+const int KEY_JOY_SENSE = 28;
+const int KEY_JOY_DEAD_ZONE = 29;
+const int KEY_JOY_SWAP = 30;
+
+const int KEY_AXIS_YAW = 32;
+const int KEY_AXIS_PITCH = 33;
+const int KEY_AXIS_ROLL = 34;
+const int KEY_AXIS_THROTTLE = 35;
+
+const int KEY_AXIS_YAW_INVERT = 38;
+const int KEY_AXIS_PITCH_INVERT = 39;
+const int KEY_AXIS_ROLL_INVERT = 40;
+const int KEY_AXIS_THROTTLE_INVERT = 41;
+
+
+// CONTROL VALUES:
+
+// joystick buttons and switches must use
+// ids greater than 255 so they don't interfere
+// with extended ascii numbers for keyboard keys
+
+const int KEY_JOY_AXIS_X = 0x1A0;
+const int KEY_JOY_AXIS_Y = 0x1A1;
+const int KEY_JOY_AXIS_Z = 0x1A2;
+const int KEY_JOY_AXIS_RX = 0x1A3;
+const int KEY_JOY_AXIS_RY = 0x1A4;
+const int KEY_JOY_AXIS_RZ = 0x1A5;
+const int KEY_JOY_AXIS_S0 = 0x1A6;
+const int KEY_JOY_AXIS_S1 = 0x1A7;
+
+const int KEY_JOY_1 = 0x1C1;
+const int KEY_JOY_2 = 0x1C2;
+const int KEY_JOY_3 = 0x1C3;
+const int KEY_JOY_4 = 0x1C4;
+const int KEY_JOY_5 = 0x1C5;
+const int KEY_JOY_6 = 0x1C6;
+const int KEY_JOY_7 = 0x1C7;
+const int KEY_JOY_8 = 0x1C8;
+const int KEY_JOY_9 = 0x1C9;
+const int KEY_JOY_10 = 0x1CA;
+const int KEY_JOY_11 = 0x1CB;
+const int KEY_JOY_12 = 0x1CC;
+const int KEY_JOY_13 = 0x1CD;
+const int KEY_JOY_14 = 0x1CE;
+const int KEY_JOY_15 = 0x1CF;
+const int KEY_JOY_16 = 0x1D0;
+
+const int KEY_JOY_32 = 0x1E0;
+
+const int KEY_POV_0_UP = 0x1F0;
+const int KEY_POV_0_DOWN = 0x1F1;
+const int KEY_POV_0_LEFT = 0x1F2;
+const int KEY_POV_0_RIGHT = 0x1F3;
+
+const int KEY_POV_1_UP = 0x1F4;
+const int KEY_POV_1_DOWN = 0x1F5;
+const int KEY_POV_1_LEFT = 0x1F6;
+const int KEY_POV_1_RIGHT = 0x1F7;
+
+const int KEY_POV_2_UP = 0x1F8;
+const int KEY_POV_2_DOWN = 0x1F9;
+const int KEY_POV_2_LEFT = 0x1FA;
+const int KEY_POV_2_RIGHT = 0x1FB;
+
+const int KEY_POV_3_UP = 0x1FC;
+const int KEY_POV_3_DOWN = 0x1FD;
+const int KEY_POV_3_LEFT = 0x1FE;
+const int KEY_POV_3_RIGHT = 0x1FF;
+
+// +--------------------------------------------------------------------+
+
+class MotionController
+{
+public:
+ static const char* TYPENAME() { return "MotionController"; }
+
+ MotionController()
+ : status(StatusOK), sensitivity(1), dead_zone(0),
+ swapped(0), inverted(0), rudder(0), throttle(0), select(0) { }
+
+ virtual ~MotionController() { }
+
+ enum StatusValue { StatusOK, StatusErr, StatusBadParm };
+ enum ActionValue { MaxActions = 32 };
+
+ StatusValue Status() const { return status; }
+ int Sensitivity() const { return sensitivity; }
+ int DeadZone() const { return dead_zone; }
+ int Swapped() const { return swapped; }
+ int Inverted() const { return inverted; }
+ int RudderEnabled() const { return rudder; }
+ int ThrottleEnabled() const { return throttle; }
+ int Selector() const { return select; }
+
+
+ // setup:
+ virtual void SetSensitivity(int sense, int dead)
+ {
+ if (sense > 0) sensitivity = sense;
+ if (dead > 0) dead_zone = dead;
+ }
+
+ virtual void SetSelector(int sel) { select = sel; }
+ virtual void SetRudderEnabled(int rud) { rudder = rud; }
+ virtual void SetThrottleEnabled(int t) { throttle = t; }
+
+ virtual void SwapYawRoll(int swap) { swapped = swap; }
+ virtual int GetSwapYawRoll() { return swapped; }
+ virtual void InvertPitch(int inv) { inverted = inv; }
+ virtual int GetInverted() { return inverted; }
+
+ virtual void MapKeys(KeyMapEntry* mapping, int nkeys) { }
+
+ // sample the physical device
+ virtual void Acquire() { }
+
+ // translations
+ virtual double X() { return 0; }
+ virtual double Y() { return 0; }
+ virtual double Z() { return 0; }
+
+ // rotations
+ virtual double Pitch() { return 0; }
+ virtual double Roll() { return 0; }
+ virtual double Yaw() { return 0; }
+ virtual int Center() { return 0; }
+
+ // throttle
+ virtual double Throttle() { return 0; }
+ virtual void SetThrottle(double t) { }
+
+ // actions
+ virtual int Action(int n) { return 0; }
+ virtual int ActionMap(int n) { return 0; }
+
+protected:
+ StatusValue status;
+ int sensitivity;
+ int dead_zone;
+ int swapped;
+ int inverted;
+ int rudder;
+ int throttle;
+ int select;
+};
+
+#endif MoCon_h
+
diff --git a/nGenEx/Mouse.cpp b/nGenEx/Mouse.cpp
new file mode 100644
index 0000000..30c0c05
--- /dev/null
+++ b/nGenEx/Mouse.cpp
@@ -0,0 +1,192 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Mouse.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Mouse class
+*/
+
+#include "MemDebug.h"
+#include "Mouse.h"
+#include "DataLoader.h"
+#include "Window.h"
+#include "Screen.h"
+#include "Bitmap.h"
+#include "Video.h"
+
+// +--------------------------------------------------------------------+
+
+int Mouse::show = 1;
+int Mouse::cursor = Mouse::ARROW;
+
+int Mouse::x = 320;
+int Mouse::y = 240;
+int Mouse::l = 0;
+int Mouse::m = 0;
+int Mouse::r = 0;
+int Mouse::w = 0;
+
+Bitmap* Mouse::image[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+int Mouse::hotspot[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+Window* Mouse::window = 0;
+
+// +--------------------------------------------------------------------+
+
+void Mouse::Create(Screen* screen)
+{
+ if (screen) {
+ delete window;
+ window = new(__FILE__,__LINE__) Window(screen, 0, 0, screen->Width(), screen->Height());
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void Mouse::Resize(Screen* screen)
+{
+ if (screen) {
+ delete window;
+ window = new(__FILE__,__LINE__) Window(screen, 0, 0, screen->Width(), screen->Height());
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void Mouse::Close()
+{
+ for (int i = 0; i < 8; i++) {
+ delete image[i];
+ image[i] = 0;
+ }
+
+ delete window;
+ window = 0;
+
+ show = 0;
+ cursor = ARROW;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Mouse::SetCursorPos(int ax, int ay)
+{
+ POINT p;
+ int dx = 0;
+ int dy = 0;
+
+ ::GetCursorPos(&p);
+
+ dx = p.x - x;
+ dy = p.y - y;
+
+ x = ax;
+ y = ay;
+
+ ::SetCursorPos(x+dx,y+dy);
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Mouse::SetCursor(CURSOR c)
+{
+ int old = cursor;
+ cursor = c;
+ return old;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Mouse::LoadCursor(CURSOR c, const char* name, HOTSPOT hs)
+{
+ int result = 0;
+
+ delete image[c];
+ image[c] = 0;
+
+ if (name && *name) {
+ image[c] = new(__FILE__,__LINE__) Bitmap;
+ result = DataLoader::GetLoader()->LoadBitmap(name, *image[c], Bitmap::BMP_TRANSPARENT);
+
+ if (result) {
+ Bitmap* bmp = image[c];
+
+ if (bmp && bmp->HiPixels())
+ image[c]->CopyAlphaRedChannel(image[c]->Width(), image[c]->Height(), (LPDWORD) image[c]->HiPixels());
+
+ hotspot[c] = hs;
+ }
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Mouse::Show(int s)
+{
+ show = s;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Mouse::Paint()
+{
+ if (!show || !window) return;
+
+ // draw using bitmap:
+ if (image[cursor]) {
+ int w2 = image[cursor]->Width()/2;
+ int h2 = image[cursor]->Height()/2;
+
+ if (hotspot[cursor] == HOTSPOT_NW)
+ window->DrawBitmap(x, y, x+w2*2, y+h2*2, image[cursor], Video::BLEND_ALPHA);
+ else
+ window->DrawBitmap(x-w2, y-h2, x+w2, y+h2, image[cursor], Video::BLEND_ALPHA);
+ }
+
+ // draw using primitives:
+ /***
+ else {
+ switch (cursor) {
+ case ARROW:
+ case CROSS:
+ case USER1:
+ case USER2:
+ case USER3:
+ default:
+ window->DrawLine(x-7, y, x+8, y, Color::White);
+ window->DrawLine(x, y-7, x, y+8, Color::White);
+ break;
+
+ case WAIT:
+ window->DrawLine(x-7, y-7, x+8, y-7, Color::White);
+ window->DrawLine(x-7, y-7, x+8, y+8, Color::White);
+ window->DrawLine(x-7, y+8, x+8, y-7, Color::White);
+ window->DrawLine(x-7, y+8, x+8, y+8, Color::White);
+ break;
+
+ case NOT:
+ window->DrawEllipse(x-7,y-7,x+8,y+8, Color::White);
+ window->DrawLine( x-7,y-7,x+8,y+8, Color::White);
+ break;
+
+ case DRAG:
+ window->DrawRect(x-7, y-6, x+8, y-3, Color::White);
+ window->DrawRect(x-7, y-1, x+8, y+2, Color::White);
+ window->DrawRect(x-7, y+4, x+8, y+7, Color::White);
+ break;
+ }
+ }
+ ***/
+}
diff --git a/nGenEx/Mouse.h b/nGenEx/Mouse.h
new file mode 100644
index 0000000..3f6e4ee
--- /dev/null
+++ b/nGenEx/Mouse.h
@@ -0,0 +1,75 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Mouse.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Mouse class
+*/
+
+#ifndef Mouse_h
+#define Mouse_h
+
+#include "Types.h"
+
+// +--------------------------------------------------------------------+
+
+class Bitmap;
+class Screen;
+class Window;
+
+// +--------------------------------------------------------------------+
+
+class Mouse
+{
+ friend LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
+ friend class Game;
+
+public:
+ static const char* TYPENAME() { return "Mouse"; }
+
+ enum CURSOR { ARROW, CROSS, WAIT, NOT, DRAG, USER1, USER2, USER3 };
+ enum HOTSPOT { HOTSPOT_CTR, HOTSPOT_NW };
+
+ static int X() { return x; }
+ static int Y() { return y; }
+ static int LButton() { return l; }
+ static int MButton() { return m; }
+ static int RButton() { return r; }
+ static int Wheel() { return w; }
+
+ static void Paint();
+
+ static void SetCursorPos(int x, int y);
+ static void Show(int s=1);
+ static int SetCursor(CURSOR c);
+ static int LoadCursor(CURSOR c, const char* name, HOTSPOT hs = HOTSPOT_CTR);
+
+ static void Create(Screen* screen);
+ static void Resize(Screen* screen);
+ static void Close();
+
+private:
+ static int show;
+ static int cursor;
+
+ static int x;
+ static int y;
+ static int l;
+ static int m;
+ static int r;
+ static int w;
+
+ static Bitmap* image[8];
+ static int hotspot[8];
+
+ static Window* window;
+};
+
+#endif Mouse_h
+
diff --git a/nGenEx/MouseController.cpp b/nGenEx/MouseController.cpp
new file mode 100644
index 0000000..ecdf5ad
--- /dev/null
+++ b/nGenEx/MouseController.cpp
@@ -0,0 +1,247 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: MouseController.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ MouseController Input class
+*/
+
+#include "MemDebug.h"
+#include "MouseController.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+#include "Game.h"
+#include "Video.h"
+
+// +--------------------------------------------------------------------+
+
+static MouseController* instance = 0;
+static DWORD rbutton_latch = 0;
+static DWORD mbutton_latch = 0;
+static DWORD active_latch = 0;
+
+// +--------------------------------------------------------------------+
+
+MouseController::MouseController()
+ : p(0), r(0), w(0), dx(0), dy(0), t(0)
+{
+ instance = this;
+ select = 0;
+ sensitivity = 10;
+ swapped = 0;
+ active = false;
+
+ active_key = 20; // caps lock
+
+ rbutton_latch = 0;
+
+ for (int i = 0; i < MotionController::MaxActions; i++)
+ action[i] = 0;
+}
+
+MouseController::~MouseController()
+{
+ instance = 0;
+}
+
+MouseController*
+MouseController::GetInstance()
+{
+ return instance;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MouseController::MapKeys(KeyMapEntry* mapping, int nkeys)
+{
+ for (int i = 0; i < nkeys; i++) {
+ KeyMapEntry k = mapping[i];
+
+ if (k.act >= KEY_MAP_FIRST && k.act <= KEY_MAP_LAST) {
+
+ if (k.act == KEY_MOUSE_SENSE)
+ sensitivity = k.key;
+
+ else if (k.act == KEY_MOUSE_SWAP)
+ swapped = k.key;
+
+ else if (k.act == KEY_MOUSE_INVERT)
+ inverted = k.key;
+
+ else if (k.act == KEY_MOUSE_SELECT)
+ select = k.key;
+
+ else if (k.act == KEY_MOUSE_ACTIVE)
+ active_key = k.key;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+static inline double sqr(double a) { return a*a; }
+
+void
+MouseController::Acquire()
+{
+ p = r = w = 0;
+ action[0] = 0;
+ action[1] = 0;
+ action[2] = 0;
+ action[3] = 0;
+
+ if (active_key && Keyboard::KeyDown(active_key)) {
+ active_latch = 1;
+ }
+ else {
+ if (active_latch) {
+ active_latch = 0;
+ active = !active;
+ }
+ }
+
+ if (!select || !active)
+ return;
+
+ action[0] = Mouse::LButton();
+
+ int roll_enable = 0;
+
+ if (Mouse::RButton()) {
+ roll_enable = 1;
+
+ if (!rbutton_latch)
+ rbutton_latch = Game::RealTime();
+ }
+ else {
+ if (rbutton_latch) {
+ rbutton_latch = Game::RealTime() - rbutton_latch;
+ if (rbutton_latch < 250)
+ action[1] = 1;
+ }
+
+ rbutton_latch = 0;
+ }
+
+ if (Mouse::MButton()) {
+ if (!mbutton_latch)
+ mbutton_latch = Game::RealTime();
+ }
+ else {
+ if (mbutton_latch) {
+ action[3] = 1;
+ }
+
+ mbutton_latch = 0;
+ }
+
+ double step = 0;
+ int cx = Video::GetInstance()->Width()/2;
+ int cy = Video::GetInstance()->Height()/2;
+
+ dx += Mouse::X() - cx;
+ dy += Mouse::Y() - cy;
+
+ step = fabs(dx)/cx;
+
+ if (roll_enable || select == 1)
+ step *= 3 * sensitivity;
+ else
+ step *= step * sensitivity/4;
+
+ if (roll_enable) {
+ if (dx > 0)
+ r = -step;
+ else if (dx < 0)
+ r = step;
+ }
+ else {
+ if (dx > 0)
+ w = step;
+ else if (dx < 0)
+ w = -step;
+ }
+
+ step = fabs(dy)/cy;
+
+ if (select == 1)
+ step *= 2 * sensitivity;
+ else
+ step *= step * sensitivity/4;
+
+ if (inverted) {
+ step *= -1;
+ }
+
+ if (dy > 0)
+ p = step;
+ else if (dy < 0)
+ p = -step;
+
+ if (select == 1) {
+ ::SetCursorPos(cx, cy);
+
+ double drain = cx * 4 * Game::FrameTime();
+
+ if (dx > drain) {
+ dx -= drain;
+ }
+ else if (dx < -drain) {
+ dx += drain;
+ }
+ else {
+ dx = 0;
+ }
+
+ if (dy > drain) {
+ dy -= drain;
+ }
+ else if (dy < -drain) {
+ dy += drain;
+ }
+ else {
+ dy = 0;
+ }
+ }
+ else {
+ dx = 0;
+ dy = 0;
+ }
+
+ if (Mouse::Wheel() > 0) {
+ if (t < 0.25)
+ t += 0.025;
+ else
+ t += 0.1;
+
+ if (t > 1) t = 1;
+ }
+
+ else if (Mouse::Wheel() < 0) {
+ if (t < 0.25)
+ t -= 0.025;
+ else
+ t -= 0.1;
+
+ if (t < 0) t = 0;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+int
+MouseController::ActionMap(int n)
+{
+ if (n >= KEY_ACTION_0 && n <= KEY_ACTION_3)
+ return action[n - KEY_ACTION_0];
+
+ return 0;
+}
+
diff --git a/nGenEx/MouseController.h b/nGenEx/MouseController.h
new file mode 100644
index 0000000..c9ed6bb
--- /dev/null
+++ b/nGenEx/MouseController.h
@@ -0,0 +1,70 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: MouseController.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Joystick Input class
+*/
+
+#ifndef MouseController_h
+#define MouseController_h
+
+#include "MotionController.h"
+
+// +--------------------------------------------------------------------+
+
+class MouseController : public MotionController
+{
+public:
+ static const char* TYPENAME() { return "MouseController"; }
+
+ MouseController();
+ virtual ~MouseController();
+
+ // setup
+ virtual void MapKeys(KeyMapEntry* mapping, int nkeys);
+
+ // sample the physical device
+ virtual void Acquire();
+
+ // translations
+ virtual double X() { return 0; }
+ virtual double Y() { return 0; }
+ virtual double Z() { return 0; }
+
+ // rotations
+ virtual double Pitch() { if (active) return p; return 0; }
+ virtual double Roll() { if (active) return r; return 0; }
+ virtual double Yaw() { if (active) return w; return 0; }
+ virtual int Center() { return 0; }
+
+ // throttle
+ virtual double Throttle() { if (active) return t; return 0; }
+ virtual void SetThrottle(double throttle) { t = throttle; }
+
+ // actions
+ virtual int Action(int n) { return action[n]; }
+ virtual int ActionMap(int n);
+
+ // actively sampling?
+ virtual bool Active() { return active; }
+ virtual void SetActive(bool a) { active = a; }
+
+ static MouseController* GetInstance();
+
+protected:
+ double p,r,w, dx, dy, t;
+ int action[MotionController::MaxActions];
+ int map[32];
+ bool active;
+ int active_key;
+};
+
+#endif MouseController_h
+
diff --git a/nGenEx/MultiController.cpp b/nGenEx/MultiController.cpp
new file mode 100644
index 0000000..7631ac5
--- /dev/null
+++ b/nGenEx/MultiController.cpp
@@ -0,0 +1,130 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: MultiController.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ MultiController Input class
+*/
+
+#include "MemDebug.h"
+#include "MultiController.h"
+
+// +--------------------------------------------------------------------+
+
+MultiController::MultiController()
+ : x(0), y(0), z(0), p(0), r(0), w(0), c(0), p1(0), r1(0), w1(0), t(0)
+{
+ for (int i = 0; i < MotionController::MaxActions; i++)
+ action[i] = 0;
+
+ nctrl = 0;
+ for (i = 0; i < 4; i++)
+ ctrl[i] = 0;
+}
+
+MultiController::~MultiController()
+{
+ for (int i = 0; i < 4; i++)
+ delete ctrl[i];
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MultiController::AddController(MotionController* c)
+{
+ if (nctrl < 4 && c)
+ ctrl[nctrl++] = c;
+}
+
+void
+MultiController::MapKeys(KeyMapEntry* mapping, int nkeys)
+{
+ for (int i = 0; i < nctrl; i++)
+ ctrl[i]->MapKeys(mapping, nkeys);
+}
+
+int
+MultiController::GetSwapYawRoll() const
+{
+ if (nctrl)
+ return ctrl[0]->GetSwapYawRoll();
+
+ return 0;
+}
+
+void
+MultiController::SwapYawRoll(int swap)
+{
+ for (int i = 0; i < nctrl; i++)
+ ctrl[i]->SwapYawRoll(swap);
+}
+
+// +--------------------------------------------------------------------+
+
+inline void clamp(double& x) { if (x<-1)x=-1; else if (x>1)x=1; }
+
+void
+MultiController::Acquire()
+{
+ t = x = y = z = p = r = w = c = 0;
+
+ for (int i = 0; i < MotionController::MaxActions; i++)
+ action[i] = 0;
+
+ for (i = 0; i < nctrl; i++) {
+ ctrl[i]->Acquire();
+
+ x += ctrl[i]->X();
+ y += ctrl[i]->Y();
+ z += ctrl[i]->Z();
+
+ r += ctrl[i]->Roll();
+ p += ctrl[i]->Pitch();
+ w += ctrl[i]->Yaw();
+ c += ctrl[i]->Center();
+ t += ctrl[i]->Throttle();
+
+ for (int a = 0; a < MotionController::MaxActions; a++)
+ action[a] += ctrl[i]->Action(a);
+ }
+
+ clamp(x);
+ clamp(y);
+ clamp(z);
+ clamp(r);
+ clamp(p);
+ clamp(w);
+ clamp(t);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+MultiController::SetThrottle(double throttle)
+{
+ for (int i = 0; i < nctrl; i++)
+ ctrl[i]->SetThrottle(throttle);
+}
+
+// +--------------------------------------------------------------------+
+
+int
+MultiController::ActionMap(int key)
+{
+ for (int i = 0; i < nctrl; i++) {
+ int result = ctrl[i]->ActionMap(key);
+
+ if (result)
+ return result;
+ }
+
+ return 0;
+}
+
diff --git a/nGenEx/MultiController.h b/nGenEx/MultiController.h
new file mode 100644
index 0000000..a65ae85
--- /dev/null
+++ b/nGenEx/MultiController.h
@@ -0,0 +1,68 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: MultiController.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ ComboController Motion Controller class
+*/
+
+#ifndef MultiController_h
+#define MultiController_h
+
+#include "MotionController.h"
+
+// +--------------------------------------------------------------------+
+
+class MultiController : public MotionController
+{
+public:
+ static const char* TYPENAME() { return "MultiController"; }
+
+ MultiController();
+ virtual ~MultiController();
+
+ virtual void AddController(MotionController* c);
+ virtual void MapKeys(KeyMapEntry* mapping, int nkeys);
+ virtual void SwapYawRoll(int swap);
+ virtual int GetSwapYawRoll() const;
+
+ // sample the physical device
+ virtual void Acquire();
+
+ // translations
+ virtual double X() { return x; }
+ virtual double Y() { return y; }
+ virtual double Z() { return z; }
+
+ // rotations
+ virtual double Pitch() { return p; }
+ virtual double Roll() { return r; }
+ virtual double Yaw() { return w; }
+ virtual int Center() { return c; }
+
+ // throttle
+ virtual double Throttle() { return t; }
+ virtual void SetThrottle(double throttle);
+
+ // actions
+ virtual int Action(int n) { return action[n]; }
+ virtual int ActionMap(int n);
+
+protected:
+ int nctrl;
+ MotionController* ctrl[4];
+
+ double x,y,z,p,r,w,t;
+ double p1, r1, w1;
+ int c;
+ int action[MotionController::MaxActions];
+};
+
+#endif // MultiController_h
+
diff --git a/nGenEx/PCX.CPP b/nGenEx/PCX.CPP
new file mode 100644
index 0000000..42b34b2
--- /dev/null
+++ b/nGenEx/PCX.CPP
@@ -0,0 +1,516 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: PCX.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ PCX image file loader
+*/
+
+
+#include "MemDebug.h"
+#include "PCX.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+// +--------------------------------------------------------------------+
+
+#define MAX_SIZE (4*1024*1024)
+
+enum { BYTEMODE, RUNMODE };
+
+// +--------------------------------------------------------------------+
+
+PcxImage::PcxImage()
+ : width(0), height(0), bitmap(0), himap(0), imagebytes(0)
+{ }
+
+PcxImage::PcxImage(short w, short h, unsigned char* bits, unsigned char* colors)
+ : bitmap(0), himap(0)
+{
+ hdr.manufacturer = 10; // Always set to 10
+ hdr.version = 5; // Always 5 for 256-color files
+ hdr.encoding = 1; // Always set to 1
+ hdr.bits_per_pixel = 8; // Should be 8 for 256-color files
+ hdr.xmin = 0;
+ hdr.xmax = w-1;
+ hdr.ymin = 0;
+ hdr.ymax = h-1;
+ hdr.hres = 0x48;
+ hdr.vres = 0x48;
+
+ hdr.palette16[ 0] = (unsigned char) 0x00;
+ hdr.palette16[ 1] = (unsigned char) 0x00;
+ hdr.palette16[ 2] = (unsigned char) 0x00;
+ hdr.palette16[ 3] = (unsigned char) 0x80;
+ hdr.palette16[ 4] = (unsigned char) 0x00;
+ hdr.palette16[ 5] = (unsigned char) 0x00;
+ hdr.palette16[ 6] = (unsigned char) 0x00;
+ hdr.palette16[ 7] = (unsigned char) 0x80;
+ hdr.palette16[ 8] = (unsigned char) 0x00;
+ hdr.palette16[ 9] = (unsigned char) 0x80;
+ hdr.palette16[10] = (unsigned char) 0x80;
+ hdr.palette16[11] = (unsigned char) 0x00;
+ hdr.palette16[12] = (unsigned char) 0x00;
+ hdr.palette16[13] = (unsigned char) 0x00;
+ hdr.palette16[14] = (unsigned char) 0x80;
+ hdr.palette16[15] = (unsigned char) 0x80;
+
+ hdr.palette16[16] = (unsigned char) 0x00;
+ hdr.palette16[17] = (unsigned char) 0x80;
+ hdr.palette16[18] = (unsigned char) 0x00;
+ hdr.palette16[19] = (unsigned char) 0x80;
+ hdr.palette16[20] = (unsigned char) 0x80;
+ hdr.palette16[21] = (unsigned char) 0xC0;
+ hdr.palette16[22] = (unsigned char) 0xC0;
+ hdr.palette16[23] = (unsigned char) 0xC0;
+ hdr.palette16[24] = (unsigned char) 0xC0;
+ hdr.palette16[25] = (unsigned char) 0xDC;
+ hdr.palette16[26] = (unsigned char) 0xC0;
+ hdr.palette16[27] = (unsigned char) 0xA6;
+ hdr.palette16[28] = (unsigned char) 0xCA;
+ hdr.palette16[29] = (unsigned char) 0xF0;
+ hdr.palette16[30] = (unsigned char) 0x33;
+ hdr.palette16[31] = (unsigned char) 0x2B;
+
+ hdr.palette16[32] = (unsigned char) 0x1F;
+ hdr.palette16[33] = (unsigned char) 0x2B;
+ hdr.palette16[34] = (unsigned char) 0x23;
+ hdr.palette16[35] = (unsigned char) 0x1B;
+ hdr.palette16[36] = (unsigned char) 0x5F;
+ hdr.palette16[37] = (unsigned char) 0x5F;
+ hdr.palette16[38] = (unsigned char) 0x5F;
+ hdr.palette16[39] = (unsigned char) 0x2F;
+ hdr.palette16[40] = (unsigned char) 0x2F;
+ hdr.palette16[41] = (unsigned char) 0x2F;
+ hdr.palette16[42] = (unsigned char) 0x27;
+ hdr.palette16[43] = (unsigned char) 0x27;
+ hdr.palette16[44] = (unsigned char) 0x27;
+ hdr.palette16[45] = (unsigned char) 0x1F;
+ hdr.palette16[46] = (unsigned char) 0x1F;
+ hdr.palette16[47] = (unsigned char) 0x1F;
+
+ hdr.reserved = 0; // Reserved for future use
+ hdr.color_planes = 1; // Color planes
+ hdr.bytes_per_line = w; // Number of bytes in 1 line of pixels
+ hdr.palette_type = 1; // Should be 1 for color palette
+
+ for (unsigned int i = 0; i < 58; i++)
+ hdr.filler[i] = 0;
+
+ width = w;
+ height = h;
+ imagebytes = width * height;
+
+ bitmap = new(__FILE__,__LINE__) unsigned char [imagebytes];
+
+ if (bitmap) {
+ for (i = 0; i < imagebytes; i++)
+ bitmap[i] = bits[i];
+
+ unsigned char* p = pal;
+ for (i = 0; i < 256; i++) {
+ *p++ = *colors++;
+ *p++ = *colors++;
+ *p++ = *colors++;
+ colors++;
+ }
+ }
+}
+
+PcxImage::PcxImage(short w, short h, unsigned long* hibits)
+ : bitmap(0), himap(0)
+{
+ hdr.manufacturer = 10; // Always set to 10
+ hdr.version = 5; // Always 5 for true color files
+ hdr.encoding = 1; // Always set to 1
+ hdr.bits_per_pixel = 8; // Should be 8 for true color files
+ hdr.xmin = 0;
+ hdr.xmax = w-1;
+ hdr.ymin = 0;
+ hdr.ymax = h-1;
+ hdr.hres = 0x48;
+ hdr.vres = 0x48;
+
+ hdr.palette16[ 0] = (unsigned char) 0x00;
+ hdr.palette16[ 1] = (unsigned char) 0x00;
+ hdr.palette16[ 2] = (unsigned char) 0x00;
+ hdr.palette16[ 3] = (unsigned char) 0x80;
+ hdr.palette16[ 4] = (unsigned char) 0x00;
+ hdr.palette16[ 5] = (unsigned char) 0x00;
+ hdr.palette16[ 6] = (unsigned char) 0x00;
+ hdr.palette16[ 7] = (unsigned char) 0x80;
+ hdr.palette16[ 8] = (unsigned char) 0x00;
+ hdr.palette16[ 9] = (unsigned char) 0x80;
+ hdr.palette16[10] = (unsigned char) 0x80;
+ hdr.palette16[11] = (unsigned char) 0x00;
+ hdr.palette16[12] = (unsigned char) 0x00;
+ hdr.palette16[13] = (unsigned char) 0x00;
+ hdr.palette16[14] = (unsigned char) 0x80;
+ hdr.palette16[15] = (unsigned char) 0x80;
+
+ hdr.palette16[16] = (unsigned char) 0x00;
+ hdr.palette16[17] = (unsigned char) 0x80;
+ hdr.palette16[18] = (unsigned char) 0x00;
+ hdr.palette16[19] = (unsigned char) 0x80;
+ hdr.palette16[20] = (unsigned char) 0x80;
+ hdr.palette16[21] = (unsigned char) 0xC0;
+ hdr.palette16[22] = (unsigned char) 0xC0;
+ hdr.palette16[23] = (unsigned char) 0xC0;
+ hdr.palette16[24] = (unsigned char) 0xC0;
+ hdr.palette16[25] = (unsigned char) 0xDC;
+ hdr.palette16[26] = (unsigned char) 0xC0;
+ hdr.palette16[27] = (unsigned char) 0xA6;
+ hdr.palette16[28] = (unsigned char) 0xCA;
+ hdr.palette16[29] = (unsigned char) 0xF0;
+ hdr.palette16[30] = (unsigned char) 0x33;
+ hdr.palette16[31] = (unsigned char) 0x2B;
+
+ hdr.palette16[32] = (unsigned char) 0x1F;
+ hdr.palette16[33] = (unsigned char) 0x2B;
+ hdr.palette16[34] = (unsigned char) 0x23;
+ hdr.palette16[35] = (unsigned char) 0x1B;
+ hdr.palette16[36] = (unsigned char) 0x5F;
+ hdr.palette16[37] = (unsigned char) 0x5F;
+ hdr.palette16[38] = (unsigned char) 0x5F;
+ hdr.palette16[39] = (unsigned char) 0x2F;
+ hdr.palette16[40] = (unsigned char) 0x2F;
+ hdr.palette16[41] = (unsigned char) 0x2F;
+ hdr.palette16[42] = (unsigned char) 0x27;
+ hdr.palette16[43] = (unsigned char) 0x27;
+ hdr.palette16[44] = (unsigned char) 0x27;
+ hdr.palette16[45] = (unsigned char) 0x1F;
+ hdr.palette16[46] = (unsigned char) 0x1F;
+ hdr.palette16[47] = (unsigned char) 0x1F;
+
+ hdr.reserved = 0; // Reserved for future use
+ hdr.color_planes = 3; // Color planes
+ hdr.bytes_per_line = w; // Number of bytes in 1 line of pixels
+ hdr.palette_type = 1; // Should be 1 for color palette
+
+ for (unsigned int i = 0; i < 58; i++)
+ hdr.filler[i] = 0;
+
+ width = w;
+ height = h;
+ imagebytes = width * height;
+
+ himap = new(__FILE__,__LINE__) unsigned long [imagebytes];
+
+ if (himap) {
+ for (i = 0; i < imagebytes; i++)
+ himap[i] = hibits[i];
+ }
+}
+
+PcxImage::~PcxImage()
+{
+ delete [] bitmap;
+ delete [] himap;
+}
+
+// +--------------------------------------------------------------------+
+
+int PcxImage::Load(char *filename)
+{
+ unsigned long i;
+ short mode=BYTEMODE, bytecount;
+ unsigned char abyte, *p;
+ FILE *f;
+
+ f = fopen(filename,"rb");
+ if (f == NULL)
+ return PCX_NOFILE;
+
+ fread(&hdr, sizeof(PcxHeader), 1, f);
+
+ // read 256 color PCX file
+ if (hdr.color_planes == 1) {
+ width = 1 + hdr.xmax - hdr.xmin;
+ height = 1 + hdr.ymax - hdr.ymin;
+ imagebytes = width * height;
+
+ if (imagebytes > MAX_SIZE)
+ return PCX_TOOBIG;
+
+ // get palette from pcx file
+ fseek(f,-768L,SEEK_END);
+ fread(pal,768,1,f);
+
+ // now go back and read the pixel data:
+ fseek(f,sizeof(PcxHeader),SEEK_SET);
+
+ delete [] himap; himap = 0;
+ delete [] bitmap; bitmap = 0;
+
+ himap = new(__FILE__,__LINE__) unsigned long [imagebytes];
+ if (himap == NULL)
+ return PCX_NOMEM;
+
+ // force alpha channel to 255
+ memset(himap, 0xff, imagebytes*4);
+
+ unsigned long* pix = himap;
+ for (i=0; i<imagebytes; i++) {
+ if (mode == BYTEMODE) {
+ abyte = fgetc(f);
+
+ if (abyte > 0xbf) {
+ bytecount = abyte & 0x3f;
+ abyte = fgetc(f);
+ if (--bytecount > 0)
+ mode = RUNMODE;
+ }
+ }
+ else if (--bytecount == 0) {
+ mode = BYTEMODE;
+ }
+
+ *pix++ = 0xff000000 | (pal[3*abyte] << 16) | (pal[3*abyte+1] << 8) | (pal[3*abyte+2]);
+ }
+ }
+
+ // read 24-bit (true COLOR) PCX file
+ else {
+ width = 1 + hdr.xmax - hdr.xmin;
+ height = 1 + hdr.ymax - hdr.ymin;
+ imagebytes = width * height;
+
+ if (imagebytes > MAX_SIZE)
+ return PCX_TOOBIG;
+
+ delete [] himap; himap = 0;
+ delete [] bitmap; bitmap = 0;
+
+ himap = new(__FILE__,__LINE__) unsigned long [imagebytes];
+ if (himap == NULL)
+ return PCX_NOMEM;
+
+ // force alpha channel to 255
+ memset(himap, 0xff, imagebytes*4);
+
+ for (int row = 0; row < height; row++) {
+ // RED, GREEN, BLUE
+ for (int plane = 2; plane >= 0; plane--) {
+ p = ((unsigned char*) himap) + width*row*4 + plane;
+ for (int col = 0; col < width; col++) {
+ if (mode == BYTEMODE) {
+ abyte = fgetc(f);
+
+ if (abyte > 0xbf) {
+ bytecount = abyte & 0x3f;
+ abyte = fgetc(f);
+ if (--bytecount > 0)
+ mode = RUNMODE;
+ }
+ }
+ else if (--bytecount == 0) {
+ mode = BYTEMODE;
+ }
+
+ *p = abyte;
+ p += 4;
+ }
+ }
+ }
+ }
+
+ fclose(f);
+ return PCX_OK;
+}
+
+// +--------------------------------------------------------------------+
+
+int PcxImage::LoadBuffer(unsigned char* buf, int len)
+{
+ unsigned long i;
+ short mode=BYTEMODE, bytecount;
+ unsigned char abyte, *p;
+ unsigned char* fp;
+
+ if (buf == NULL)
+ return PCX_NOFILE;
+
+ fp = buf;
+ memcpy(&hdr, buf, sizeof(PcxHeader));
+ fp += sizeof(PcxHeader);
+
+ // read 256 color PCX file
+ if (hdr.color_planes == 1) {
+ width = 1 + hdr.xmax - hdr.xmin;
+ height = 1 + hdr.ymax - hdr.ymin;
+ imagebytes = width * height;
+
+ if (imagebytes > MAX_SIZE)
+ return PCX_TOOBIG;
+
+ // get palette from end of pcx file
+ memcpy(pal,buf+len-768,768);
+
+ delete [] himap; himap = 0;
+ delete [] bitmap; bitmap = 0;
+
+ himap = new(__FILE__,__LINE__) unsigned long [imagebytes];
+ if (himap == NULL)
+ return PCX_NOMEM;
+
+ memset(himap, 0, imagebytes*4);
+
+ unsigned long* pix = himap;
+ for (i=0; i<imagebytes; i++) {
+ if (mode == BYTEMODE) {
+ abyte = *fp++;
+
+ if (abyte > 0xbf) {
+ bytecount = abyte & 0x3f;
+ abyte = *fp++;
+ if (--bytecount > 0)
+ mode = RUNMODE;
+ }
+ }
+ else if (--bytecount == 0) {
+ mode = BYTEMODE;
+ }
+
+ *pix++ = 0xff000000 | (pal[3*abyte] << 16) | (pal[3*abyte+1] << 8) | (pal[3*abyte+2]);
+ }
+ }
+
+ // read 24-bit (true COLOR) PCX file
+ else {
+ width = 1 + hdr.xmax - hdr.xmin;
+ height = 1 + hdr.ymax - hdr.ymin;
+ imagebytes = width * height;
+
+ if (imagebytes > MAX_SIZE)
+ return PCX_TOOBIG;
+
+ delete [] himap; himap = 0;
+ delete [] bitmap; bitmap = 0;
+
+ himap = new(__FILE__,__LINE__) unsigned long [imagebytes];
+ if (himap == NULL)
+ return PCX_NOMEM;
+
+ memset(himap, 0, imagebytes*4);
+
+ for (int row = 0; row < height; row++) {
+ // RED, GREEN, BLUE
+ for (int plane = 2; plane >= 0; plane--) {
+ p = ((unsigned char*) himap) + width*row*4 + plane;
+ for (int col = 0; col < width; col++) {
+ if (mode == BYTEMODE) {
+ abyte = *fp++;
+
+ if (abyte > 0xbf) {
+ bytecount = abyte & 0x3f;
+ abyte = *fp++;
+ if (--bytecount > 0)
+ mode = RUNMODE;
+ }
+ }
+ else if (--bytecount == 0) {
+ mode = BYTEMODE;
+ }
+
+ *p = abyte;
+ p += 4;
+ }
+ }
+ }
+ }
+
+ return PCX_OK;
+}
+
+// +--------------------------------------------------------------------+
+
+int PcxImage::Save(char *filename)
+{
+ short mode=BYTEMODE;
+ FILE *f;
+
+ f = fopen(filename,"wb");
+ if (f == NULL)
+ return PCX_NOFILE;
+
+ fwrite(&hdr, sizeof(PcxHeader), 1, f);
+
+ if (hdr.color_planes == 1) {
+ unsigned char *p = bitmap;
+ unsigned long total = imagebytes;
+ unsigned long row = 0;
+ unsigned char palette_marker = 12;
+
+ while (total) {
+ unsigned char* start = p;
+ unsigned char count = 0;
+
+ while (*start == *p && count < 0x3f && row < width) {
+ p++;
+ count++;
+ row++;
+ }
+
+ if (count > 1 || *start > 0xbf) {
+ unsigned char b[2];
+ b[0] = 0xc0 | count;
+ b[1] = *start;
+ fwrite(b, 2, 1, f);
+ }
+ else {
+ fwrite(start, 1, 1, f);
+ }
+
+ total -= count;
+
+ if (row == width)
+ row = 0;
+ }
+
+ fwrite(&palette_marker,1,1,f);
+ fwrite(pal,768,1,f);
+ }
+
+ // write 24-bit (TRUE COLOR) PCX file
+ else {
+ for (int row = 0; row < height; row++) {
+ for (int plane = 2; plane >= 0; plane--) {
+ unsigned long col = 0;
+ unsigned char* p = ((unsigned char*) himap) + width*row*4 + plane;
+
+ while (col < width) {
+ unsigned char* start = p;
+ unsigned char count = 0;
+
+ while (*start == *p && count < 0x3f && col < width) {
+ p += 4;
+ count++;
+ col++;
+ }
+
+ if (count > 1 || *start > 0xbf) {
+ unsigned char b[2];
+ b[0] = 0xc0 | count;
+ b[1] = *start;
+ fwrite(b, 2, 1, f);
+ }
+ else {
+ fwrite(start, 1, 1, f);
+ }
+ }
+ }
+ }
+ }
+
+ fclose(f);
+ return PCX_OK; // return success
+}
+
diff --git a/nGenEx/ParseUtil.cpp b/nGenEx/ParseUtil.cpp
new file mode 100644
index 0000000..77a0427
--- /dev/null
+++ b/nGenEx/ParseUtil.cpp
@@ -0,0 +1,481 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: ParseUtil.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Parser utility functions
+*/
+
+#include "MemDebug.h"
+#include "ParseUtil.h"
+#include "DataLoader.h"
+
+#include "stdio.h"
+
+// +--------------------------------------------------------------------+
+
+bool GetDefBool(bool& dst, TermDef* def, const char* file)
+{
+ if (!def || !def->term()) {
+ Print("WARNING: missing BOOL TermDef in '%s'\n", file);
+ return false;
+ }
+
+ TermBool* tn = def->term()->isBool();
+ if (tn) {
+ dst = tn->value();
+ return true;
+ }
+ else {
+ Print("WARNING: invalid bool %s in '%s'. value = ", def->name()->value().data(), file);
+ def->term()->print(10);
+ Print("\n");
+ }
+
+ return false;
+}
+
+bool GetDefText(Text& dst, TermDef* def, const char* file)
+{
+ if (!def || !def->term()) {
+ Print("WARNING: missing TEXT TermDef in '%s'\n", file);
+ return false;
+ }
+
+ TermText* tn = def->term()->isText();
+ if (tn) {
+ dst = tn->value();
+ return true;
+ }
+ else
+ Print("WARNING: invalid TEXT %s in '%s'\n", def->name()->value().data(), file);
+
+ return false;
+}
+
+bool GetDefText(char* dst, TermDef* def, const char* file)
+{
+ if (!def || !def->term()) {
+ Print("WARNING: missing TEXT TermDef in '%s'\n", file);
+ return false;
+ }
+
+ TermText* tn = def->term()->isText();
+ if (tn) {
+ strcpy(dst, tn->value());
+ return true;
+ }
+ else
+ Print("WARNING: invalid TEXT %s in '%s'\n", def->name()->value().data(), file);
+
+ return false;
+}
+
+bool GetDefNumber(int& dst, TermDef* def, const char* file)
+{
+ if (!def || !def->term()) {
+ Print("WARNING: missing NUMBER TermDef in '%s'\n", file);
+ return false;
+ }
+
+ TermNumber* tr = def->term()->isNumber();
+ if (tr) {
+ dst = (int) tr->value();
+ return true;
+ }
+ else
+ Print("WARNING: invalid NUMBER %s in '%s'\n", def->name()->value().data(), file);
+
+ return false;
+}
+
+bool GetDefNumber(DWORD& dst, TermDef* def, const char* file)
+{
+ if (!def || !def->term()) {
+ Print("WARNING: missing NUMBER TermDef in '%s'\n", file);
+ return false;
+ }
+
+ TermNumber* tr = def->term()->isNumber();
+ if (tr) {
+ dst = (DWORD) tr->value();
+ return true;
+ }
+ else
+ Print("WARNING: invalid NUMBER %s in '%s'\n", def->name()->value().data(), file);
+
+ return false;
+}
+
+bool GetDefNumber(float& dst, TermDef* def, const char* file)
+{
+ if (!def || !def->term()) {
+ Print("WARNING: missing NUMBER TermDef in '%s'\n", file);
+ return false;
+ }
+
+ TermNumber* tr = def->term()->isNumber();
+ if (tr) {
+ dst = (float) tr->value();
+ return true;
+ }
+ else
+ Print("WARNING: invalid NUMBER %s in '%s'\n", def->name()->value().data(), file);
+
+ return false;
+}
+
+bool GetDefNumber(double& dst, TermDef* def, const char* file)
+{
+ if (!def || !def->term()) {
+ Print("WARNING: missing NUMBER TermDef in '%s'\n", file);
+ return false;
+ }
+
+ TermNumber* tr = def->term()->isNumber();
+ if (tr) {
+ dst = (double) tr->value();
+ return true;
+ }
+ else
+ Print("WARNING: invalid NUMBER %s in '%s'\n", def->name()->value().data(), file);
+
+ return false;
+}
+
+bool GetDefVec(Vec3& dst, TermDef* def, const char* file)
+{
+ if (!def || !def->term()) {
+ Print("WARNING: missing VEC3 TermDef in '%s'\n", file);
+ return false;
+ }
+
+ TermArray* val = def->term()->isArray();
+ if (val) {
+ if (val->elements()->size() != 3) {
+ Print("WARNING: malformed vector in '%s'\n", file);
+ }
+ else {
+ dst.x = (float) (val->elements()->at(0)->isNumber()->value());
+ dst.y = (float) (val->elements()->at(1)->isNumber()->value());
+ dst.z = (float) (val->elements()->at(2)->isNumber()->value());
+
+ return true;
+ }
+ }
+ else {
+ Print("WARNING: vector expected in '%s'\n", file);
+ }
+
+ return false;
+}
+
+bool GetDefRect(Rect& dst, TermDef* def, const char* file)
+{
+ if (!def || !def->term()) {
+ Print("WARNING: missing RECT TermDef in '%s'\n", file);
+ return false;
+ }
+
+ TermArray* val = def->term()->isArray();
+ if (val) {
+ if (val->elements()->size() != 4) {
+ Print("WARNING: malformed rect in '%s'\n", file);
+ }
+ else {
+ dst.x = (int) (val->elements()->at(0)->isNumber()->value());
+ dst.y = (int) (val->elements()->at(1)->isNumber()->value());
+ dst.w = (int) (val->elements()->at(2)->isNumber()->value());
+ dst.h = (int) (val->elements()->at(3)->isNumber()->value());
+
+ return true;
+ }
+ }
+ else {
+ Print("WARNING: rect expected in '%s'\n", file);
+ }
+
+ return false;
+}
+
+bool GetDefInsets(Insets& dst, TermDef* def, const char* file)
+{
+ if (!def || !def->term()) {
+ Print("WARNING: missing Insets TermDef in '%s'\n", file);
+ return false;
+ }
+
+ TermArray* val = def->term()->isArray();
+ if (val) {
+ if (val->elements()->size() != 4) {
+ Print("WARNING: malformed Insets in '%s'\n", file);
+ }
+ else {
+ dst.left = (WORD) (val->elements()->at(0)->isNumber()->value());
+ dst.right = (WORD) (val->elements()->at(1)->isNumber()->value());
+ dst.top = (WORD) (val->elements()->at(2)->isNumber()->value());
+ dst.bottom = (WORD) (val->elements()->at(3)->isNumber()->value());
+
+ return true;
+ }
+ }
+ else {
+ Print("WARNING: Insets expected in '%s'\n", file);
+ }
+
+ return false;
+}
+
+bool GetDefColor(Color& dst, TermDef* def, const char* file)
+{
+ if (!def || !def->term()) {
+ Print("WARNING: missing COLOR TermDef in '%s'\n", file);
+ return false;
+ }
+
+ TermArray* val = def->term()->isArray();
+ if (val) {
+ if (val->elements()->size() != 3) {
+ Print("WARNING: malformed color in '%s'\n", file);
+ }
+ else {
+ BYTE r, g, b;
+ double v0 = (val->elements()->at(0)->isNumber()->value());
+ double v1 = (val->elements()->at(1)->isNumber()->value());
+ double v2 = (val->elements()->at(2)->isNumber()->value());
+
+ if (v0 >= 0 && v0 <= 1 &&
+ v1 >= 0 && v1 <= 1 &&
+ v2 >= 0 && v2 <= 1) {
+
+ r = (BYTE) (v0 * 255);
+ g = (BYTE) (v1 * 255);
+ b = (BYTE) (v2 * 255);
+
+ }
+ else {
+ r = (BYTE) v0;
+ g = (BYTE) v1;
+ b = (BYTE) v2;
+ }
+
+ dst = Color(r,g,b);
+ return true;
+ }
+ }
+ else {
+ Print("WARNING: color expected in '%s'\n", file);
+ }
+
+ return false;
+}
+
+bool GetDefColor(ColorValue& dst, TermDef* def, const char* file)
+{
+ if (!def || !def->term()) {
+ Print("WARNING: missing COLOR TermDef in '%s'\n", file);
+ return false;
+ }
+
+ TermArray* val = def->term()->isArray();
+ if (val) {
+ if (val->elements()->size() < 3 || val->elements()->size() > 4) {
+ Print("WARNING: malformed color in '%s'\n", file);
+ }
+ else {
+ double r = (val->elements()->at(0)->isNumber()->value());
+ double g = (val->elements()->at(1)->isNumber()->value());
+ double b = (val->elements()->at(2)->isNumber()->value());
+ double a = 1;
+
+ if (val->elements()->size() == 4)
+ a = (val->elements()->at(3)->isNumber()->value());
+
+ dst.Set((float) r, (float) g, (float) b, (float) a);
+ return true;
+ }
+ }
+ else {
+ Print("WARNING: color expected in '%s'\n", file);
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool GetDefArray(int* dst, int size, TermDef* def, const char* file)
+{
+ if (!def || !def->term()) {
+ Print("WARNING: missing ARRAY TermDef in '%s'\n", file);
+ return false;
+ }
+
+ TermArray* val = def->term()->isArray();
+ if (val) {
+ int nelem = val->elements()->size();
+
+ if (nelem > size)
+ nelem = size;
+
+ for (int i = 0; i < nelem; i++)
+ *dst++ = (int) (val->elements()->at(i)->isNumber()->value());
+
+ return true;
+ }
+ else {
+ Print("WARNING: array expected in '%s'\n", file);
+ }
+
+ return false;
+}
+
+bool GetDefArray(float* dst, int size, TermDef* def, const char* file)
+{
+ if (!def || !def->term()) {
+ Print("WARNING: missing ARRAY TermDef in '%s'\n", file);
+ return false;
+ }
+
+ TermArray* val = def->term()->isArray();
+ if (val) {
+ int nelem = val->elements()->size();
+
+ if (nelem > size)
+ nelem = size;
+
+ for (int i = 0; i < nelem; i++)
+ *dst++ = (float) (val->elements()->at(i)->isNumber()->value());
+
+ return true;
+ }
+ else {
+ Print("WARNING: array expected in '%s'\n", file);
+ }
+
+ return false;
+}
+
+bool GetDefArray(double* dst, int size, TermDef* def, const char* file)
+{
+ if (!def || !def->term()) {
+ Print("WARNING: missing ARRAY TermDef in '%s'\n", file);
+ return false;
+ }
+
+ TermArray* val = def->term()->isArray();
+ if (val) {
+ int nelem = val->elements()->size();
+
+ if (nelem > size)
+ nelem = size;
+
+ for (int i = 0; i < nelem; i++)
+ *dst++ = (double) (val->elements()->at(i)->isNumber()->value());
+
+ return true;
+ }
+ else {
+ Print("WARNING: array expected in '%s'\n", file);
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool GetDefArray(ArrayList& array, TermDef* def, const char* file)
+{
+ if (!def || !def->term()) {
+ Print("WARNING: missing ARRAY TermDef in '%s'\n", file);
+ return false;
+ }
+
+ TermArray* val = def->term()->isArray();
+ if (val) {
+ int nelem = val->elements()->size();
+
+ array.clear();
+
+ for (int i = 0; i < nelem; i++)
+ array.append((DWORD) (val->elements()->at(i)->isNumber()->value()));
+
+ return true;
+ }
+ else {
+ Print("WARNING: integer array expected in '%s'\n", file);
+ }
+
+ return false;
+}
+
+bool GetDefArray(FloatList& array, TermDef* def, const char* file)
+{
+ if (!def || !def->term()) {
+ Print("WARNING: missing ARRAY TermDef in '%s'\n", file);
+ return false;
+ }
+
+ TermArray* val = def->term()->isArray();
+ if (val) {
+ int nelem = val->elements()->size();
+
+ array.clear();
+
+ for (int i = 0; i < nelem; i++)
+ array.append((float) (val->elements()->at(i)->isNumber()->value()));
+
+ return true;
+ }
+ else {
+ Print("WARNING: float array expected in '%s'\n", file);
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool GetDefTime(int& dst, TermDef* def, const char* file)
+{
+ if (!def || !def->term()) {
+ Print("WARNING: missing TIME TermDef in '%s'\n", file);
+ return false;
+ }
+
+ TermText* tn = def->term()->isText();
+
+ if (tn) {
+ int d = 0;
+ int h = 0;
+ int m = 0;
+ int s = 0;
+
+ char buf[64];
+ strcpy(buf, tn->value());
+
+ if (strchr(buf, '/'))
+ sscanf(buf, "%d/%d:%d:%d", &d, &h, &m, &s);
+ else
+ sscanf(buf, "%d:%d:%d", &h, &m, &s);
+
+ dst = d * 24 * 60 * 60 +
+ h * 60 * 60 +
+ m * 60 +
+ s;
+
+ return true;
+ }
+ else
+ Print("WARNING: invalid TIME %s in '%s'\n", def->name()->value().data(), file);
+
+ return false;
+}
+
+
diff --git a/nGenEx/ParseUtil.h b/nGenEx/ParseUtil.h
new file mode 100644
index 0000000..19352f5
--- /dev/null
+++ b/nGenEx/ParseUtil.h
@@ -0,0 +1,52 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: ParseUtil.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Parser utility functions
+*/
+
+#ifndef ParseUtil_h
+#define ParseUtil_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "Color.h"
+#include "ArrayList.h"
+
+#include "Text.h"
+#include "Parser.h"
+#include "Reader.h"
+#include "Token.h"
+
+// +--------------------------------------------------------------------+
+
+bool GetDefBool(bool& dst, TermDef* def, const char* file);
+bool GetDefText(Text& dst, TermDef* def, const char* file);
+bool GetDefText(char* dst, TermDef* def, const char* file);
+bool GetDefNumber(int& dst, TermDef* def, const char* file);
+bool GetDefNumber(DWORD& dst, TermDef* def, const char* file);
+bool GetDefNumber(float& dst, TermDef* def, const char* file);
+bool GetDefNumber(double& dst, TermDef* def, const char* file);
+bool GetDefVec(Vec3& dst, TermDef* def, const char* file);
+bool GetDefColor(Color& dst, TermDef* def, const char* file);
+bool GetDefColor(ColorValue& dst, TermDef* def, const char* file);
+bool GetDefRect(Rect& dst, TermDef* def, const char* file);
+bool GetDefInsets(Insets& dst, TermDef* def, const char* file);
+bool GetDefTime(int& dst, TermDef* def, const char* file);
+
+bool GetDefArray(int* dst, int size, TermDef* def, const char* file);
+bool GetDefArray(float* dst, int size, TermDef* def, const char* file);
+bool GetDefArray(double* dst, int size, TermDef* def, const char* file);
+bool GetDefArray(ArrayList& array, TermDef* def, const char* file);
+bool GetDefArray(FloatList& array, TermDef* def, const char* file);
+
+// +--------------------------------------------------------------------+
+
+#endif ParseUtil_h
diff --git a/nGenEx/Particles.cpp b/nGenEx/Particles.cpp
new file mode 100644
index 0000000..d774bb0
--- /dev/null
+++ b/nGenEx/Particles.cpp
@@ -0,0 +1,264 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Particles.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Particle Burst class
+*/
+
+#include "MemDebug.h"
+#include "Particles.h"
+#include "Projector.h"
+#include "Light.h"
+#include "Bitmap.h"
+#include "Game.h"
+#include "Random.h"
+
+// +--------------------------------------------------------------------+
+
+inline float randf() { return (rand()-16384.0f)/32768.0f; }
+
+// +--------------------------------------------------------------------+
+
+Particles::Particles(Bitmap* bitmap, int np, const Vec3& base_loc, const Vec3& vel,
+ float bspeed, float dr, float s, float bloom, float dec, float rate,
+ bool cont, bool trail, bool rise, int a, int nframes)
+ : nparts(np), base_speed(bspeed), max_speed(bspeed*3.0f),
+ drag(dr), min_scale(s), max_scale(bloom), decay(dec),
+ release_rate(rate), continuous(cont), trailing(trail), rising(rise),
+ blend(a), extra(0.0f), point_sprite(0), emitting(true)
+{
+ MoveTo(base_loc);
+ ref_loc = base_loc;
+
+ trans = true;
+ luminous = true;
+ shadow = false;
+ nverts = nparts;
+
+ if (max_scale < min_scale)
+ max_scale = min_scale;
+
+ velocity = new(__FILE__,__LINE__) Point[nverts];
+ part_loc = new(__FILE__,__LINE__) Point[nverts];
+ release = new(__FILE__,__LINE__) Point[nverts];
+ intensity = new(__FILE__,__LINE__) float[nverts];
+ timestamp = new(__FILE__,__LINE__) float[nverts];
+ scale = new(__FILE__,__LINE__) float[nverts];
+ angle = new(__FILE__,__LINE__) float[nverts];
+ frame = new(__FILE__,__LINE__) BYTE[nverts];
+
+ float speed = base_speed;
+
+ for (int i = 0; i < nverts; i++) {
+ intensity[i] = 1.0f;
+ timestamp[i] = (float) (Game::GameTime() / 1000.0);
+ scale[i] = (float) (min_scale);
+ angle[i] = (float) (Random(0, 2*PI));
+ frame[i] = 0;
+
+ part_loc[i] = Point();
+ release[i] = ref_loc;
+ velocity[i] = RandomVector(speed);
+ velocity[i] += vel;
+
+ if (speed < max_speed)
+ speed += (float) Random(max_speed/15.0, max_speed/5.0);
+ else
+ speed = base_speed;
+ }
+
+ radius = 15000.0f;
+
+ if (decay > 2)
+ decay /= 256.0f;
+
+ if (nparts < 8) {
+ nverts = 1;
+ }
+
+ else if (nparts > 50 || continuous) {
+ nverts = (int) (nparts * 0.125 * release_rate);
+ }
+
+ point_sprite = new(__FILE__,__LINE__) Sprite(bitmap, nframes);
+ point_sprite->Scale(s);
+ point_sprite->SetBlendMode(blend);
+ point_sprite->SetFrameRate(nframes * decay);
+}
+
+Particles::~Particles()
+{
+ delete point_sprite;
+ delete [] velocity;
+ delete [] part_loc;
+ delete [] release;
+ delete [] timestamp;
+ delete [] intensity;
+ delete [] scale;
+ delete [] angle;
+ delete [] frame;
+}
+
+// +--------------------------------------------------------------------+
+
+void Particles::ExecFrame(double seconds)
+{
+ point_sprite->Update();
+
+ ref_loc = loc;
+ radius += max_speed * (float) seconds;
+
+ float scaled_drag = (float) exp(-drag * seconds);
+ float scale_inc = (float) ((max_scale-min_scale)*seconds*2);
+
+ for (int i = 0; i < nverts; i++) {
+ part_loc[i] += velocity[i] * (float) seconds;
+
+ if (rising) {
+ part_loc[i].y += (float) ((randf() + 1) * scale[i] * 80 * seconds);
+ }
+
+ // do the (chunky) blooming effect:
+ if (max_scale > 0 && scale[i] < max_scale) {
+ scale[i] += scale_inc * (float) ((i%3)/3.0);
+ }
+
+ double rho = angle[i];
+ int rot = i%4;
+ switch (rot) {
+ case 0: rho += seconds * 0.13; break;
+ case 1: rho -= seconds * 0.11; break;
+ case 2: rho += seconds * 0.09; break;
+ case 3: rho -= seconds * 0.07; break;
+ default: break;
+ }
+
+ angle[i] = (float) rho;
+ intensity[i] -= (float) (decay * seconds);
+
+ if (point_sprite->NumFrames() > 1) {
+ double age = Game::GameTime()/1000.0 - timestamp[i];
+ int n = (int) (age * point_sprite->FrameRate());
+
+ if (n >= point_sprite->NumFrames())
+ n = point_sprite->NumFrames() - 1;
+
+ frame[i] = n;
+ }
+
+ velocity[i] *= scaled_drag;
+ }
+
+ if (nverts < nparts && emitting) {
+ int nv = nverts;
+ double delta = nparts * release_rate * seconds;
+ int new_parts = (int) (delta + extra);
+ extra = (float) (delta + extra - new_parts);
+ nverts += new_parts;
+
+ if (nverts > nparts)
+ nverts = nparts;
+
+ for (int i = nv; i < nverts; i++) {
+ intensity[i] = 1;
+ timestamp[i] = (float) (Game::GameTime() / 1000.0);
+ scale[i] = (float) (min_scale);
+ angle[i] = (float) (Random(0, 2*PI));
+ frame[i] = 0;
+ part_loc[i] = Point();
+ release[i] = ref_loc;
+ }
+ }
+
+ if (nverts > nparts)
+ nverts = nparts;
+
+ // recycle dead particles:
+ if (continuous) {
+ float speed = base_speed;
+
+ for (int i = 0; i < nverts; i++) {
+ if (intensity[i] <= 0) {
+ part_loc[i] = Point();
+ release[i] = ref_loc;
+
+ intensity[i] = 1;
+ timestamp[i] = (float) (Game::GameTime() / 1000.0);
+ scale[i] = (float) (min_scale);
+ angle[i] = (float) (PI * rand() / 16384.0);
+ frame[i] = 0;
+ velocity[i] = RandomVector(speed);
+
+ if (speed < max_speed)
+ speed += (float) Random(max_speed/25.0, max_speed/18.0);
+ else
+ speed = base_speed;
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Particles::CheckVisibility(Projector& projector)
+{
+ float base = 256;
+
+ if (point_sprite && point_sprite->Frame())
+ base = (float) point_sprite->Frame()->Width();
+
+ float particle_radius = base * max_scale;
+
+ if (projector.IsVisible( Location(), Radius()) &&
+ projector.ApparentRadius(Location(), particle_radius) > 1) {
+
+ visible = true;
+ }
+ else {
+ visible = false;
+ }
+
+ return visible;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Particles::Render(Video* video, DWORD flags)
+{
+ if (hidden || !visible || !video || !point_sprite)
+ return;
+
+ if (blend == 2 && !(flags & Graphic::RENDER_ALPHA))
+ return;
+
+ if (blend == 4 && !(flags & Graphic::RENDER_ADDITIVE))
+ return;
+
+ for (int i = 0; i < nverts; i++) {
+ Point vloc;
+
+ if (trailing) {
+ vloc = part_loc[i] + release[i] - offset;
+ }
+ else {
+ vloc = part_loc[i] + loc;
+ }
+
+ point_sprite->MoveTo(vloc);
+ point_sprite->SetShade(intensity[i]);
+ point_sprite->Rescale(scale[i]);
+ point_sprite->SetAngle(angle[i]);
+ point_sprite->SetFrameIndex(frame[i]);
+
+ point_sprite->Render(video, flags);
+ }
+}
diff --git a/nGenEx/Particles.h b/nGenEx/Particles.h
new file mode 100644
index 0000000..ad473ee
--- /dev/null
+++ b/nGenEx/Particles.h
@@ -0,0 +1,86 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Particles.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Particle burst class
+*/
+
+#ifndef Particles_h
+#define Particles_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "Graphic.h"
+#include "Sprite.h"
+
+// +--------------------------------------------------------------------+
+
+class Particles : public Graphic
+{
+public:
+ Particles(Bitmap* bitmap,
+ int np,
+ const Vec3& base_loc,
+ const Vec3& vel,
+ float base_speed = 500.0f,
+ float drag = 1.0f,
+ float scale = 1.0f,
+ float bloom = 0.0f,
+ float decay = 100.0f,
+ float release = 1.0f,
+ bool cont = false,
+ bool trail = true,
+ bool rise = false,
+ int blend = 3,
+ int nframes = 1);
+
+ virtual ~Particles();
+
+ virtual void Render(Video* video, DWORD flags);
+ virtual void ExecFrame(double seconds);
+ virtual void TranslateBy(const Point& ref) { offset = ref; loc = loc - ref; }
+ virtual bool CheckVisibility(Projector& projector);
+
+ virtual bool IsEmitting() const { return emitting; }
+ virtual void StopEmitting() { emitting = false; }
+
+protected:
+ int nparts;
+ int nverts;
+ int blend;
+ bool continuous;
+ bool trailing;
+ bool rising;
+ bool emitting;
+
+ float base_speed;
+ float max_speed;
+ float drag;
+ float release_rate;
+ float decay;
+ float min_scale;
+ float max_scale;
+ float extra;
+
+ Point ref_loc;
+ Point offset;
+ Point* velocity;
+ Point* part_loc;
+ Point* release;
+ float* timestamp;
+ float* intensity;
+ float* scale;
+ float* angle;
+ BYTE* frame;
+
+ Sprite* point_sprite;
+};
+
+#endif Particles_h
diff --git a/nGenEx/Pcx.h b/nGenEx/Pcx.h
new file mode 100644
index 0000000..4a209c5
--- /dev/null
+++ b/nGenEx/Pcx.h
@@ -0,0 +1,68 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: PCX.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ PCX image file loader
+*/
+
+#ifndef PCX_H
+#define PCX_H
+
+// +--------------------------------------------------------------------+
+
+enum { PCX_OK, PCX_NOMEM, PCX_TOOBIG, PCX_NOFILE };
+
+struct PcxHeader
+{
+ char manufacturer; // Always set to 10
+ char version; // Always 5 for 256-color files
+ char encoding; // Always set to 1
+ char bits_per_pixel; // Should be 8 for 256-color files
+ short xmin,ymin; // Coordinates for top left corner
+ short xmax,ymax; // Width and height of image
+ short hres; // Horizontal resolution of image
+ short vres; // Vertical resolution of image
+ char palette16[48]; // EGA palette; not used for 256-color files
+ char reserved; // Reserved for future use
+ char color_planes; // Color planes
+ short bytes_per_line; // Number of bytes in 1 line of pixels
+ short palette_type; // Should be 2 for color palette
+ char filler[58]; // Nothing but junk
+};
+
+// +--------------------------------------------------------------------+
+
+struct PcxImage
+{
+ static const char* TYPENAME() { return "PcxImage"; }
+
+ PcxImage(short w, short h, unsigned long* hibits);
+ PcxImage(short w, short h, unsigned char* bits, unsigned char* colors);
+
+ PcxImage();
+ ~PcxImage();
+
+ int Load(char *filename);
+ int Save(char *filename);
+
+ int LoadBuffer(unsigned char* buf, int len);
+
+ PcxHeader hdr;
+ unsigned char* bitmap;
+ unsigned long* himap;
+ unsigned char pal[768];
+ unsigned long imagebytes;
+ unsigned short width, height;
+};
+
+// +--------------------------------------------------------------------+
+
+
+#endif
diff --git a/nGenEx/Physical.cpp b/nGenEx/Physical.cpp
new file mode 100644
index 0000000..b6c132c
--- /dev/null
+++ b/nGenEx/Physical.cpp
@@ -0,0 +1,775 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Physical.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Abstract Physical Object
+*/
+
+#include "MemDebug.h"
+#include "Physical.h"
+#include "Graphic.h"
+#include "Light.h"
+#include "Director.h"
+
+// +--------------------------------------------------------------------+
+
+int Physical::id_key = 1;
+double Physical::sub_frame = 1.0 / 60.0;
+
+static const double GRAV = 6.673e-11;
+
+// +--------------------------------------------------------------------+
+
+Physical::Physical()
+ : id(id_key++), obj_type(0), rep(0), light(0),
+ thrust(0.0f), drag(0.0f), lat_thrust(false),
+ trans_x(0.0f), trans_y(0.0f), trans_z(0.0f), straight(false),
+ roll(0.0f), pitch(0.0f), yaw(0.0f), dr(0.0f), dp(0.0f), dy(0.0f),
+ dr_acc(0.0f), dp_acc(0.0f), dy_acc(0.0f),
+ dr_drg(0.0f), dp_drg(0.0f), dy_drg(0.0f),
+ flight_path_yaw(0.0f), flight_path_pitch(0.0f), primary_mass(0),
+ roll_rate(1.0f), pitch_rate(1.0f), yaw_rate(1.0f), shake(0.0f),
+ radius(0.0f), mass(1.0f), integrity(1.0f), life(-1), dir(0),
+ g_accel(0.0f), Do(0.0f), CL(0.0f), CD(0.0f), alpha(0.0f), stall(0.0f)
+{
+ strcpy(name, "unknown object");
+}
+
+// +--------------------------------------------------------------------+
+
+Physical::Physical(const char* n, int t)
+ : id(id_key++), obj_type(t), rep(0), light(0),
+ thrust(0.0f), drag(0.0f), lat_thrust(false),
+ trans_x(0.0f), trans_y(0.0f), trans_z(0.0f), straight(false),
+ roll(0.0f), pitch(0.0f), yaw(0.0f), dr(0.0f), dp(0.0f), dy(0.0f),
+ dr_acc(0.0f), dp_acc(0.0f), dy_acc(0.0f),
+ dr_drg(0.0f), dp_drg(0.0f), dy_drg(0.0f),
+ flight_path_yaw(0.0f), flight_path_pitch(0.0f), primary_mass(0),
+ roll_rate(1.0f), pitch_rate(1.0f), yaw_rate(1.0f), shake(0.0f),
+ radius(0.0f), mass(1.0f), integrity(1.0f), life(-1), dir(0),
+ g_accel(0.0f), Do(0.0f), CL(0.0f), CD(0.0f), alpha(0.0f), stall(0.0f)
+{
+ strncpy(name, n, NAMELEN-1);
+ name[NAMELEN-1] = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+Physical::~Physical()
+{
+ // inform graphic rep and light that we are leaving:
+ GRAPHIC_DESTROY(rep);
+ LIGHT_DESTROY(light);
+
+ // we own the director
+ delete dir;
+ dir = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+inline double random() { return rand()-16384; }
+
+void
+Physical::ExecFrame(double s)
+{
+ Point orig_velocity = Velocity();
+ arcade_velocity = Point();
+
+ // if this object is under direction,
+ // but doesn't need subframe accuracy,
+ // update the control parameters:
+ if (dir && !dir->Subframe())
+ dir->ExecFrame(s);
+
+ // decrement life before destroying the frame time:
+ if (life > 0)
+ life -= s;
+
+ // integrate equations
+ // using slices no larger
+ // than sub_frame:
+
+ double seconds = s;
+
+ while (s > 0.0) {
+ if (s > sub_frame)
+ seconds = sub_frame;
+ else
+ seconds = s;
+
+ // if the director needs subframe accuracy, run it now:
+ if (dir && dir->Subframe())
+ dir->ExecFrame(seconds);
+
+ if (!straight)
+ AngularFrame(seconds);
+
+ // LINEAR MOVEMENT ----------------------------
+ Point pos = cam.Pos();
+
+ // if the object is thrusting,
+ // accelerate along the camera normal:
+ if (thrust) {
+ Point thrustvec = cam.vpn();
+ thrustvec *= ((thrust/mass) * seconds);
+ velocity += thrustvec;
+ }
+
+ LinearFrame(seconds);
+
+ // move the position by the (time-frame scaled) velocity:
+ pos += velocity * seconds;
+ cam.MoveTo(pos);
+
+ s -= seconds;
+ }
+
+ alpha = 0.0f;
+
+ // now update the graphic rep and light sources:
+ if (rep) {
+ rep->MoveTo(cam.Pos());
+ rep->SetOrientation(cam.Orientation());
+ }
+
+ if (light) {
+ light->MoveTo(cam.Pos());
+ }
+
+ if (!straight)
+ CalcFlightPath();
+
+ accel = (Velocity() - orig_velocity) * (1/seconds);
+ if (!_finite(accel.x) || !_finite(accel.y) || !_finite(accel.z))
+ accel = Point();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Physical::AeroFrame(double s)
+{
+ arcade_velocity = Point();
+
+ // if this object is under direction,
+ // but doesn't need subframe accuracy,
+ // update the control parameters:
+ if (dir && !dir->Subframe())
+ dir->ExecFrame(s);
+
+ // decrement life before destroying the frame time:
+ if (life > 0)
+ life -= s;
+
+ // integrate equations
+ // using slices no larger
+ // than sub_frame:
+
+ double seconds = s;
+
+ while (s > 0.0) {
+ if (s > sub_frame)
+ seconds = sub_frame;
+ else
+ seconds = s;
+
+ // if the director needs subframe accuracy, run it now:
+ if (dir && dir->Subframe())
+ dir->ExecFrame(seconds);
+
+ AngularFrame(seconds);
+
+ // LINEAR MOVEMENT ----------------------------
+ Point pos = cam.Pos();
+
+ // if the object is thrusting,
+ // accelerate along the camera normal:
+ if (thrust) {
+ Point thrustvec = cam.vpn();
+ thrustvec *= ((thrust/mass) * seconds);
+ velocity += thrustvec;
+ }
+
+ // AERODYNAMICS ------------------------------
+
+ if (lat_thrust)
+ LinearFrame(seconds);
+
+ // if no thrusters, do constant gravity:
+ else if (g_accel > 0)
+ velocity += Point(0, -g_accel, 0) * seconds;
+
+ // compute alpha, rho, drag, and lift:
+
+ Point vfp = velocity;
+ double v = vfp.Normalize();
+ double v_2 = 0;
+ double rho = GetDensity();
+ double lift = 0;
+
+ if (v > 150) {
+ v_2 = (v-150) * (v-150);
+
+ Point vfp1 = vfp - cam.vrt() * (vfp * cam.vrt());
+ vfp1.Normalize();
+
+ double cos_alpha = vfp1 * cam.vpn();
+
+ if (cos_alpha >= 1) {
+ alpha = 0.0f;
+ }
+ else {
+ alpha = (float) acos(cos_alpha);
+ }
+
+ // if flight path is above nose, alpha is negative:
+ if (vfp1 * cam.vup() > 0)
+ alpha = -alpha;
+
+ if (alpha <= stall) {
+ lift = CL * alpha * rho * v_2;
+ }
+ else {
+ lift = CL * (2*stall - alpha) * rho * v_2;
+ }
+
+ // add lift to velocity:
+ if (_finite(lift))
+ velocity += cam.vup() * lift * seconds;
+ else
+ lift = 0;
+
+ // if drag applies, decellerate:
+ double alpha_2 = alpha*alpha;
+ double drag_eff = (drag + (CD * alpha_2)) * rho * v_2;
+
+ Point vn = velocity;
+ vn.Normalize();
+
+ velocity += vn * -drag_eff * seconds;
+ }
+ else {
+ velocity *= exp(-drag * seconds);
+ }
+
+ // move the position by the (time-frame scaled) velocity:
+ pos += velocity * seconds;
+ cam.MoveTo(pos);
+
+ s -= seconds;
+ }
+
+ // now update the graphic rep and light sources:
+ if (rep) {
+ rep->MoveTo(cam.Pos());
+ rep->SetOrientation(cam.Orientation());
+ }
+
+ if (light) {
+ light->MoveTo(cam.Pos());
+ }
+}
+
+double
+Physical::GetDensity() const
+{
+ double alt = cam.Pos().y;
+ double rho = 0.75 * Do * (250e3-alt)/250e3;
+
+ return rho;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Physical::ArcadeFrame(double s)
+{
+ // if this object is under direction,
+ // but doesn't need subframe accuracy,
+ // update the control parameters:
+ if (dir && !dir->Subframe())
+ dir->ExecFrame(s);
+
+ // decrement life before destroying the frame time:
+ if (life > 0)
+ life -= s;
+
+ // integrate equations
+ // using slices no larger
+ // than sub_frame:
+
+ double seconds = s;
+
+ while (s > 0.0) {
+ if (s > sub_frame)
+ seconds = sub_frame;
+ else
+ seconds = s;
+
+ // if the director needs subframe accuracy, run it now:
+ if (dir && dir->Subframe())
+ dir->ExecFrame(seconds);
+
+ if (!straight)
+ AngularFrame(seconds);
+
+ Point pos = cam.Pos();
+
+ // ARCADE FLIGHT MODEL:
+ // arcade_velocity vector is always in line with heading
+
+ double speed = arcade_velocity.Normalize();
+ double bleed = arcade_velocity * cam.vpn();
+
+ speed *= pow(bleed, 30);
+ arcade_velocity = cam.vpn() * speed;
+
+ if (thrust) {
+ Point thrustvec = cam.vpn();
+ thrustvec *= ((thrust/mass) * seconds);
+ arcade_velocity += thrustvec;
+ }
+
+ if (drag)
+ arcade_velocity *= exp(-drag * seconds);
+
+ LinearFrame(seconds);
+
+ // move the position by the (time-frame scaled) velocity:
+ pos += arcade_velocity * seconds +
+ velocity * seconds;
+
+ cam.MoveTo(pos);
+
+ s -= seconds;
+ }
+
+ alpha = 0.0f;
+
+ // now update the graphic rep and light sources:
+ if (rep) {
+ rep->MoveTo(cam.Pos());
+ rep->SetOrientation(cam.Orientation());
+ }
+
+ if (light) {
+ light->MoveTo(cam.Pos());
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Physical::AngularFrame(double seconds)
+{
+ if (!straight) {
+ dr += (float) (dr_acc * seconds);
+ dy += (float) (dy_acc * seconds);
+ dp += (float) (dp_acc * seconds);
+
+ dr *= (float) exp(-dr_drg * seconds);
+ dy *= (float) exp(-dy_drg * seconds);
+ dp *= (float) exp(-dp_drg * seconds);
+
+ roll = (float) (dr * seconds);
+ pitch = (float) (dp * seconds);
+ yaw = (float) (dy * seconds);
+
+ if (shake > 0.01) {
+ vibration = Point(random(), random(), random());
+ vibration.Normalize();
+ vibration *= (float) (shake * seconds);
+
+ shake *= (float) exp(-1.5 * seconds);
+ }
+ else {
+ vibration.x = vibration.y = vibration.z = 0.0f;
+ shake = 0.0f;
+ }
+
+ cam.Aim(roll, pitch, yaw);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Physical::LinearFrame(double seconds)
+{
+ // deal with lateral thrusters:
+
+ if (trans_x) { // side-to-side
+ Point transvec = cam.vrt();
+ transvec *= ((trans_x/mass) * seconds);
+
+ velocity += transvec;
+ }
+
+ if (trans_y) { // fore-and-aft
+ Point transvec = cam.vpn();
+ transvec *= ((trans_y/mass) * seconds);
+
+ velocity += transvec;
+ }
+
+ if (trans_z) { // up-and-down
+ Point transvec = cam.vup();
+ transvec *= ((trans_z/mass) * seconds);
+
+ velocity += transvec;
+ }
+
+ // if gravity applies, attract:
+ if (primary_mass > 0) {
+ Point g = primary_loc - cam.Pos();
+ double r = g.Normalize();
+
+ g *= GRAV * primary_mass / (r*r);
+
+ velocity += g * seconds;
+ }
+
+ // constant gravity:
+ else if (g_accel > 0)
+ velocity += Point(0, -g_accel, 0) * seconds;
+
+ // if drag applies, decellerate:
+ if (drag)
+ velocity *= exp(-drag * seconds);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Physical::CalcFlightPath()
+{
+ flight_path_yaw = 0.0f;
+ flight_path_pitch = 0.0f;
+
+ // transform flight path into camera frame:
+ Point flight_path = velocity;
+ if (flight_path.Normalize() < 1)
+ return;
+
+ Point tmp = flight_path;
+ flight_path.x = tmp * cam.vrt();
+ flight_path.y = tmp * cam.vup();
+ flight_path.z = tmp * cam.vpn();
+
+ if (flight_path.z < 0.1)
+ return;
+
+ // first, compute azimuth:
+ flight_path_yaw = (float) atan(flight_path.x / flight_path.z);
+ if (flight_path.z < 0) flight_path_yaw -= (float) PI;
+ if (flight_path_yaw < -PI) flight_path_yaw += (float) (2*PI);
+
+ // then, rotate path into azimuth frame to compute elevation:
+ Camera yaw_cam;
+ yaw_cam.Clone(cam);
+ yaw_cam.Yaw(flight_path_yaw);
+
+ flight_path.x = tmp * yaw_cam.vrt();
+ flight_path.y = tmp * yaw_cam.vup();
+ flight_path.z = tmp * yaw_cam.vpn();
+
+ flight_path_pitch = (float) atan(flight_path.y / flight_path.z);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Physical::MoveTo(const Point& new_loc)
+{
+ cam.MoveTo(new_loc);
+}
+
+void
+Physical::TranslateBy(const Point& ref)
+{
+ Point new_loc = cam.Pos() - ref;
+ cam.MoveTo(new_loc);
+}
+
+void
+Physical::ApplyForce(const Point& force)
+{
+ velocity += force/mass;
+}
+
+void
+Physical::ApplyTorque(const Point& torque)
+{
+ dr += (float) (torque.x/mass);
+ dp += (float) (torque.y/mass);
+ dy += (float) (torque.z/mass);
+}
+
+void
+Physical::SetThrust(double t)
+{
+ thrust = (float) t;
+}
+
+void
+Physical::SetTransX(double t)
+{
+ trans_x = (float) t;
+}
+
+void
+Physical::SetTransY(double t)
+{
+ trans_y = (float) t;
+}
+
+void
+Physical::SetTransZ(double t)
+{
+ trans_z = (float) t;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Physical::SetHeading(double r, double p, double y)
+{
+ roll = (float) r;
+ pitch = (float) p;
+ yaw = (float) y;
+
+ cam.Aim(roll, pitch, yaw);
+}
+
+void
+Physical::LookAt(const Point& dst)
+{
+ cam.LookAt(dst);
+}
+
+void
+Physical::CloneCam(const Camera& c)
+{
+ cam.Clone(c);
+}
+
+void
+Physical::SetAbsoluteOrientation(double r, double p, double y)
+{
+ roll = (float) r;
+ pitch = (float) p;
+ yaw = (float) y;
+
+ Camera work(Location().x, Location().y, Location().z);
+ work.Aim(r,p,y);
+ cam.Clone(work);
+}
+
+void
+Physical::ApplyRoll(double r)
+{
+ if (r > 1) r = 1;
+ else if (r < -1) r = -1;
+
+ dr_acc = (float) r * roll_rate;
+}
+
+void
+Physical::ApplyPitch(double p)
+{
+ if (p > 1) p = 1;
+ else if (p < -1) p = -1;
+
+ dp_acc = (float) p * pitch_rate;
+}
+
+void
+Physical::ApplyYaw(double y)
+{
+ if (y > 1) y = 1;
+ else if (y < -1) y = -1;
+
+ dy_acc = (float) y * yaw_rate;
+}
+
+void
+Physical::SetAngularRates(double r, double p, double y)
+{
+ roll_rate = (float) r;
+ pitch_rate = (float) p;
+ yaw_rate = (float) y;
+}
+
+void
+Physical::GetAngularRates(double& r, double& p, double& y)
+{
+ r = roll_rate;
+ p = pitch_rate;
+ y = yaw_rate;
+}
+
+void
+Physical::SetAngularDrag(double r, double p, double y)
+{
+ dr_drg = (float) r;
+ dp_drg = (float) p;
+ dy_drg = (float) y;
+}
+
+void
+Physical::GetAngularDrag(double& r, double& p, double& y)
+{
+ r = dr_drg;
+ p = dp_drg;
+ y = dy_drg;
+}
+
+void
+Physical::GetAngularThrust(double& r, double& p, double& y)
+{
+ r = 0;
+ p = 0;
+ y = 0;
+
+ if (dr_acc > 0.05 * roll_rate) r = 1;
+ else if (dr_acc < -0.05 * roll_rate) r = -1;
+ else if (dr > 0.01 * roll_rate) r = -1;
+ else if (dr < -0.01 * roll_rate) r = 1;
+
+ if (dy_acc > 0.05 * yaw_rate) y = 1;
+ else if (dy_acc < -0.05 * yaw_rate) y = -1;
+ else if (dy > 0.01 * yaw_rate) y = -1;
+ else if (dy < -0.01 * yaw_rate) y = 1;
+
+ if (dp_acc > 0.05 * pitch_rate) p = 1;
+ else if (dp_acc < -0.05 * pitch_rate) p = -1;
+ else if (dp > 0.01 * pitch_rate) p = -1;
+ else if (dp < -0.01 * pitch_rate) p = 1;
+}
+
+
+void
+Physical::SetPrimary(const Point& l, double m)
+{
+ primary_loc = l;
+ primary_mass = m;
+}
+
+void
+Physical::SetGravity(double g)
+{
+ if (g >= 0)
+ g_accel = (float) g;
+}
+
+void
+Physical::SetBaseDensity(double d)
+{
+ if (d >= 0)
+ Do = (float) d;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Physical::InflictDamage(double damage, int /*type*/)
+{
+ integrity -= (float) damage;
+
+ if (integrity < 1.0f)
+ integrity = 0.0f;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Physical::CollidesWith(Physical& o)
+{
+ // representation collision test (will do bounding spheres first):
+ if (rep && o.rep)
+ return rep->CollidesWith(*o.rep);
+
+ Point delta_loc = Location() - o.Location();
+
+ // bounding spheres test:
+ if (delta_loc.length() > radius + o.radius)
+ return 0;
+
+ // assume collision:
+ return 1;
+}
+
+
+// +--------------------------------------------------------------------+
+
+void
+Physical::ElasticCollision(Physical& a, Physical& b)
+{
+ double mass_sum = a.mass + b.mass;
+ double mass_delta = a.mass - b.mass;
+
+ Point vel_a = (Point(b.velocity) * (2 * b.mass) + Point(a.velocity) * mass_delta) * (1/mass_sum);
+ Point vel_b = (Point(a.velocity) * (2 * a.mass) - Point(b.velocity) * mass_delta) * (1/mass_sum);
+
+ a.velocity = vel_a;
+ b.velocity = vel_b;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Physical::InelasticCollision(Physical& a, Physical& b)
+{
+ double mass_sum = a.mass + b.mass;
+
+ Point vel_a = (Point(a.velocity) * a.mass + Point(b.velocity) * b.mass) * (1/mass_sum);
+
+ a.velocity = vel_a;
+ b.velocity = vel_a;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Physical::SemiElasticCollision(Physical& a, Physical& b)
+{
+ double mass_sum = a.mass + b.mass;
+ double mass_delta = a.mass - b.mass;
+
+ Point avel = a.Velocity();
+ Point bvel = b.Velocity();
+ Point dv = avel - bvel;
+
+ // low delta-v: stick
+ if (dv.length() < 20) {
+ if (a.mass > b.mass) {
+ b.velocity = a.velocity;
+ }
+
+ else {
+ a.velocity = b.velocity;
+ }
+ }
+
+ // high delta-v: bounce
+ else {
+ Point Ve_a = (bvel * (2 * b.mass) + avel * mass_delta) * (1/mass_sum) * 0.65;
+ Point Ve_b = (avel * (2 * a.mass) - bvel * mass_delta) * (1/mass_sum) * 0.65;
+ Point Vi_ab = (avel * a.mass + bvel * b.mass) * (1/mass_sum) * 0.35;
+
+ a.arcade_velocity = Point();
+ b.arcade_velocity = Point();
+
+ a.velocity = Ve_a + Vi_ab;
+ b.velocity = Ve_b + Vi_ab;
+ }
+}
+
diff --git a/nGenEx/Physical.h b/nGenEx/Physical.h
new file mode 100644
index 0000000..fb202da
--- /dev/null
+++ b/nGenEx/Physical.h
@@ -0,0 +1,205 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Physical.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Abstract Physical Object
+*/
+
+#ifndef Physical_h
+#define Physical_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "Camera.h"
+
+// +--------------------------------------------------------------------+
+
+class Director;
+class Graphic;
+class Light;
+
+// +--------------------------------------------------------------------+
+
+class Physical
+{
+public:
+ static const char* TYPENAME() { return "Physical"; }
+
+ Physical();
+ Physical(const char* n, int t=0);
+ virtual ~Physical();
+
+ int operator == (const Physical& p) const { return id == p.id; }
+
+ // Integration Loop Control:
+ static void SetSubFrameLength(double seconds) { sub_frame = seconds; }
+ static double GetSubFrameLength() { return sub_frame; }
+
+ // operations
+ virtual void ExecFrame(double seconds);
+ virtual void AeroFrame(double seconds);
+ virtual void ArcadeFrame(double seconds);
+
+ virtual void AngularFrame(double seconds);
+ virtual void LinearFrame(double seconds);
+
+ virtual void CalcFlightPath();
+
+ virtual void MoveTo(const Point& new_loc);
+ virtual void TranslateBy(const Point& ref);
+ virtual void ApplyForce(const Point& force);
+ virtual void ApplyTorque(const Point& torque);
+ virtual void SetThrust(double t);
+ virtual void SetTransX(double t);
+ virtual void SetTransY(double t);
+ virtual void SetTransZ(double t);
+ virtual void SetHeading(double r, double p, double y);
+ virtual void LookAt(const Point& dst);
+ virtual void ApplyRoll(double roll_acc);
+ virtual void ApplyPitch(double pitch_acc);
+ virtual void ApplyYaw(double yaw_acc);
+
+ virtual int CollidesWith(Physical& o);
+ static void ElasticCollision(Physical& a, Physical& b);
+ static void InelasticCollision(Physical& a, Physical& b);
+ static void SemiElasticCollision(Physical& a, Physical& b);
+ virtual void InflictDamage(double damage, int type = 0);
+
+ // accessors:
+ int Identity() const { return id; }
+ int Type() const { return obj_type; }
+ const char* Name() const { return name; }
+
+ Point Location() const { return cam.Pos(); }
+ Point Heading() const { return cam.vpn(); }
+ Point LiftLine() const { return cam.vup(); }
+ Point BeamLine() const { return cam.vrt(); }
+ Point Velocity() const { return velocity + arcade_velocity; }
+ Point Acceleration()
+ const { return accel; }
+ double Thrust() const { return thrust; }
+ double TransX() const { return trans_x; }
+ double TransY() const { return trans_y; }
+ double TransZ() const { return trans_z; }
+ double Drag() const { return drag; }
+
+ double Roll() const { return roll; }
+ double Pitch() const { return pitch; }
+ double Yaw() const { return yaw; }
+ Point Rotation() const { return Point(dp,dr,dy); }
+
+ double Alpha() const { return alpha; }
+
+ double FlightPathYawAngle() const { return flight_path_yaw; }
+ double FlightPathPitchAngle() const { return flight_path_pitch; }
+
+ double Radius() const { return radius; }
+ double Mass() const { return mass; }
+ double Integrity() const { return integrity; }
+ double Life() const { return life; }
+
+ double Shake() const { return shake; }
+ const Point& Vibration() const { return vibration; }
+
+ const Camera& Cam() const { return cam; }
+ Graphic* Rep() const { return rep; }
+ Light* LightSrc() const { return light; }
+
+ Director* GetDirector() const { return dir; }
+
+ // mutators:
+ virtual void SetAngularRates(double r, double p, double y);
+ virtual void GetAngularRates(double& r, double& p, double& y);
+ virtual void SetAngularDrag(double r, double p, double y);
+ virtual void GetAngularDrag(double& r, double& p, double& y);
+ virtual void GetAngularThrust(double& r, double& p, double& y);
+ virtual void SetVelocity(const Point& v) { velocity = v; }
+ virtual void SetAbsoluteOrientation(double roll, double pitch, double yaw);
+ virtual void CloneCam(const Camera& cam);
+ virtual void SetDrag(double d) { drag = (float) d; }
+
+ virtual void SetPrimary(const Point& loc, double mass);
+ virtual void SetGravity(double g);
+ virtual void SetBaseDensity(double d);
+
+ virtual double GetBaseDensity() const { return Do; }
+ virtual double GetDensity() const;
+
+ enum { NAMELEN = 48 };
+
+protected:
+ static int id_key;
+
+ // identification:
+ int id;
+ int obj_type;
+ char name[NAMELEN];
+
+ // position, velocity, and acceleration:
+ Camera cam;
+ Point velocity;
+ Point arcade_velocity;
+ Point accel;
+ float thrust;
+ float trans_x;
+ float trans_y;
+ float trans_z;
+ float drag;
+
+ // attitude and angular velocity:
+ float roll, pitch, yaw;
+ float dr, dp, dy;
+ float dr_acc, dp_acc, dy_acc;
+ float dr_drg, dp_drg, dy_drg;
+
+ float flight_path_yaw;
+ float flight_path_pitch;
+
+ // gravitation:
+ Point primary_loc;
+ double primary_mass;
+
+ // aerodynamics:
+ float g_accel; // acceleration due to gravity (constant)
+ float Do; // atmospheric density at sea level
+ float CL; // base coefficient of lift
+ float CD; // base coefficient of drag
+ float alpha; // current angle of attack (radians)
+ float stall; // stall angle of attack (radians)
+ bool lat_thrust; // lateral thrusters enabled in aero mode?
+ bool straight;
+
+ // vibration:
+ float shake;
+ Point vibration;
+
+ // scale factors for ApplyXxx():
+ float roll_rate, pitch_rate, yaw_rate;
+
+ // physical properties:
+ double life;
+ float radius;
+ float mass;
+ float integrity;
+
+ // graphic representation:
+ Graphic* rep;
+ Light* light;
+
+ // AI or human controller:
+ Director* dir; // null implies an autonomous object
+
+ static double sub_frame;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif Physical_h
+
diff --git a/nGenEx/PngImage.cpp b/nGenEx/PngImage.cpp
new file mode 100644
index 0000000..0fedc6e
--- /dev/null
+++ b/nGenEx/PngImage.cpp
@@ -0,0 +1,246 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: PngImage.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ BMP image file loader
+*/
+
+
+#include "MemDebug.h"
+#include "PngImage.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "png.h"
+
+// +--------------------------------------------------------------------+
+
+PngImage::PngImage()
+ : width(0), height(0), bpp(0), alpha_loaded(false), image(0)
+{ }
+
+PngImage::~PngImage()
+{
+ delete [] image;
+}
+
+// +--------------------------------------------------------------------+
+
+int PngImage::Load(char *filename)
+{
+ int status = PNG_INVALID;
+ FILE* f;
+
+ f = fopen(filename,"rb");
+ if (f == NULL)
+ return PNG_NOFILE;
+
+ BYTE buf[12];
+ fread(buf, 8, 1, f);
+ fseek(f, 0, SEEK_SET);
+
+ if (png_sig_cmp(buf, (png_size_t)0, 8))
+ return PNG_INVALID;
+
+ png_structp png_ptr;
+ png_infop info_ptr;
+
+ /* Create and initialize the png_struct with the desired error handler
+ * functions. If you want to use the default stderr and longjump method,
+ * you can supply NULL for the last three parameters. We also supply the
+ * the compiler header file version, so that we know if the application
+ * was compiled with a compatible version of the library. REQUIRED
+ */
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
+
+ if (!png_ptr) {
+ return PNG_NOMEM;
+ }
+
+ /* Allocate/initialize the memory for image information. REQUIRED. */
+ info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr) {
+ png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);
+ return PNG_NOMEM;
+ }
+
+ png_init_io(png_ptr, f);
+ png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, png_voidp_NULL);
+
+ status = CreateImage((void*) png_ptr, (void*) info_ptr);
+
+ png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
+
+ fclose(f);
+ return status;
+}
+
+// +--------------------------------------------------------------------+
+
+struct PngIOStruct
+{
+ BYTE* fp;
+ BYTE* buffer;
+ DWORD length;
+};
+
+static void png_user_read_data(png_structp read_ptr, png_bytep data, png_size_t length)
+{
+ PngIOStruct* read_io_ptr = (PngIOStruct*) png_get_io_ptr(read_ptr);
+
+ if (!read_io_ptr)
+ return;
+
+ if (read_io_ptr->fp + length < read_io_ptr->buffer + read_io_ptr->length) {
+ memcpy(data, read_io_ptr->fp, length);
+ read_io_ptr->fp += length;
+ }
+}
+
+static void png_user_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+}
+
+static void png_user_flush_data(png_structp png_ptr)
+{
+}
+
+int PngImage::LoadBuffer(unsigned char* buf, int len)
+{
+ int status = PNG_INVALID;
+ PngIOStruct io;
+
+ if (buf == NULL)
+ return PNG_NOFILE;
+
+ if (png_sig_cmp(buf, (png_size_t)0, 8))
+ return PNG_INVALID;
+
+ io.buffer = buf;
+ io.fp = buf;
+ io.length = len;
+
+ png_structp png_ptr;
+ png_infop info_ptr;
+
+ /* Create and initialize the png_struct with the desired error handler
+ * functions. If you want to use the default stderr and longjump method,
+ * you can supply NULL for the last three parameters. We also supply the
+ * the compiler header file version, so that we know if the application
+ * was compiled with a compatible version of the library. REQUIRED
+ */
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
+
+ if (!png_ptr) {
+ return PNG_NOMEM;
+ }
+
+ /* Allocate/initialize the memory for image information. REQUIRED. */
+ info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr) {
+ png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);
+ return PNG_NOMEM;
+ }
+
+ png_set_read_fn(png_ptr, (void *) (&io), png_user_read_data);
+ png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, png_voidp_NULL);
+
+ status = CreateImage((void*) png_ptr, (void*) info_ptr);
+
+ png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
+
+ return status;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+PngImage::CreateImage(void* pptr, void* iptr)
+{
+ int status = PNG_INVALID;
+
+ png_structp png_ptr = (png_structp) pptr;
+ png_infop info_ptr = (png_infop) iptr;
+
+ width = info_ptr->width;
+ height = info_ptr->height;
+ bpp = info_ptr->pixel_depth;
+
+ if (width > 0 && width < 32768 && height > 0 && height < 32768) {
+ // true-color:
+ if (bpp >= 24) {
+ status = PNG_OK;
+
+ if (info_ptr->channels == 4)
+ alpha_loaded = true;
+
+ image = new DWORD[width*height];
+ BYTE** rows = png_get_rows(png_ptr, info_ptr);
+
+ for (DWORD row = 0; row < height; row++) {
+ BYTE* p = rows[row];
+
+ for (DWORD col = 0; col < width; col++) {
+ DWORD red = *p++;
+ DWORD green = *p++;
+ DWORD blue = *p++;
+ DWORD alpha = 0xff;
+
+ if (info_ptr->channels == 4)
+ alpha = *p++;
+
+ image[row*width+col] = (alpha << 24) | (red << 16) | (green << 8) | blue;
+ }
+ }
+ }
+
+ // paletted:
+ else if (bpp == 8 && info_ptr->num_palette > 0) {
+ DWORD pal[256];
+
+ if (info_ptr->num_trans > 0)
+ alpha_loaded = true;
+
+ for (int i = 0; i < 256; i++) {
+ if (i < info_ptr->num_palette) {
+ DWORD red = info_ptr->palette[i].red;
+ DWORD green = info_ptr->palette[i].green;
+ DWORD blue = info_ptr->palette[i].blue;
+ DWORD alpha = 0xff;
+
+ if (i < info_ptr->num_trans)
+ alpha = info_ptr->trans[i];
+
+ pal[i] = (alpha << 24) | (red << 16) | (green << 8) | blue;
+ }
+
+ else {
+ pal[i] = 0;
+ }
+ }
+
+ image = new DWORD[width*height];
+ BYTE** rows = png_get_rows(png_ptr, info_ptr);
+
+ for (DWORD row = 0; row < height; row++) {
+ BYTE* p = rows[row];
+
+ for (DWORD col = 0; col < width; col++) {
+ BYTE index = *p++;
+ image[row*width+col] = pal[index];
+ }
+ }
+ }
+ }
+
+ return status;
+} \ No newline at end of file
diff --git a/nGenEx/PngImage.h b/nGenEx/PngImage.h
new file mode 100644
index 0000000..dc03d27
--- /dev/null
+++ b/nGenEx/PngImage.h
@@ -0,0 +1,44 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: PngImage.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ PNG image file loader
+*/
+
+#ifndef PngImage_h
+#define PngImage_h
+
+// +--------------------------------------------------------------------+
+
+enum { PNG_OK, PNG_NOMEM, PNG_INVALID, PNG_NOFILE };
+
+// +--------------------------------------------------------------------+
+
+struct PngImage
+{
+ static const char* TYPENAME() { return "PngImage"; }
+
+ PngImage();
+ ~PngImage();
+
+ int Load(char *filename);
+ int LoadBuffer(unsigned char* buf, int len);
+ int CreateImage(void* png_ptr, void* info_ptr);
+
+ DWORD* image;
+ DWORD width;
+ DWORD height;
+ DWORD bpp;
+ bool alpha_loaded;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif PngImage_h
diff --git a/nGenEx/Polygon.cpp b/nGenEx/Polygon.cpp
new file mode 100644
index 0000000..419d3a5
--- /dev/null
+++ b/nGenEx/Polygon.cpp
@@ -0,0 +1,745 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Polygon.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Polygon and VertexSet structures for 3D rendering
+*/
+
+#include "MemDebug.h"
+#include "Polygon.h"
+#include "Bitmap.h"
+
+// +--------------------------------------------------------------------+
+
+VertexSet::VertexSet(int m)
+ : nverts(0), space(OBJECT_SPACE),
+ tu1(0), tv1(0), tangent(0), binormal(0)
+{
+ Resize(m);
+}
+
+// +--------------------------------------------------------------------+
+
+VertexSet::~VertexSet()
+{
+ Delete();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+VertexSet::Resize(int m, bool preserve)
+{
+ // easy cases (no data will be preserved):
+ if (!m || !nverts || !preserve) {
+ bool additional_tex_coords = (tu1 != 0);
+
+ Delete();
+
+ nverts = m;
+
+ if (nverts <= 0) {
+ ZeroMemory(this, sizeof(VertexSet));
+ }
+
+ else {
+ loc = new(__FILE__,__LINE__) Vec3[nverts];
+ nrm = new(__FILE__,__LINE__) Vec3[nverts];
+ s_loc = new(__FILE__,__LINE__) Vec3[nverts];
+ tu = new(__FILE__,__LINE__) float[nverts];
+ tv = new(__FILE__,__LINE__) float[nverts];
+ rw = new(__FILE__,__LINE__) float[nverts];
+ diffuse = new(__FILE__,__LINE__) DWORD[nverts];
+ specular = new(__FILE__,__LINE__) DWORD[nverts];
+
+ if (additional_tex_coords)
+ CreateAdditionalTexCoords();
+
+ if (!loc || !nrm || !s_loc || !rw || !tu || !tv || !diffuse || !specular) {
+ nverts = 0;
+ nverts = 0;
+
+ delete [] loc;
+ delete [] nrm;
+ delete [] s_loc;
+ delete [] rw;
+ delete [] tu;
+ delete [] tv;
+ delete [] tu1;
+ delete [] tv1;
+ delete [] diffuse;
+ delete [] specular;
+
+ ZeroMemory(this, sizeof(VertexSet));
+ }
+ }
+ }
+
+ // actually need to copy data:
+ else {
+ int np = nverts;
+
+ nverts = m;
+
+ if (nverts < np)
+ np = nverts;
+
+ Vec3* new_loc = new(__FILE__,__LINE__) Vec3[nverts];
+ Vec3* new_nrm = new(__FILE__,__LINE__) Vec3[nverts];
+ Vec3* new_s_loc = new(__FILE__,__LINE__) Vec3[nverts];
+ float* new_rw = new(__FILE__,__LINE__) float[nverts];
+ float* new_tu = new(__FILE__,__LINE__) float[nverts];
+ float* new_tv = new(__FILE__,__LINE__) float[nverts];
+ float* new_tu1 = 0;
+ float* new_tv1 = 0;
+ DWORD* new_diffuse = new(__FILE__,__LINE__) DWORD[nverts];
+ DWORD* new_specular = new(__FILE__,__LINE__) DWORD[nverts];
+
+ if (tu1)
+ new_tu1 = new(__FILE__,__LINE__) float[nverts];
+
+ if (tv1)
+ new_tv1 = new(__FILE__,__LINE__) float[nverts];
+
+ if (new_loc) {
+ CopyMemory(new_loc, loc, np * sizeof(Vec3));
+ delete [] loc;
+ loc = new_loc;
+ }
+
+ if (new_nrm) {
+ CopyMemory(new_nrm, nrm, np * sizeof(Vec3));
+ delete [] nrm;
+ nrm = new_nrm;
+ }
+
+ if (new_s_loc) {
+ CopyMemory(new_s_loc, s_loc, np * sizeof(Vec3));
+ delete [] s_loc;
+ s_loc = new_s_loc;
+ }
+
+ if (new_tu) {
+ CopyMemory(new_tu, tu, np * sizeof(float));
+ delete [] tu;
+ tu = new_tu;
+ }
+
+ if (new_tv) {
+ CopyMemory(new_tv, tv, np * sizeof(float));
+ delete [] tv;
+ tv = new_tv;
+ }
+
+ if (new_tu1) {
+ CopyMemory(new_tu1, tu1, np * sizeof(float));
+ delete [] tu1;
+ tu = new_tu1;
+ }
+
+ if (new_tv1) {
+ CopyMemory(new_tv1, tv1, np * sizeof(float));
+ delete [] tv1;
+ tv = new_tv1;
+ }
+
+ if (new_diffuse) {
+ CopyMemory(new_diffuse, diffuse, np * sizeof(DWORD));
+ delete [] diffuse;
+ diffuse = new_diffuse;
+ }
+
+ if (new_specular) {
+ CopyMemory(new_specular, specular, np * sizeof(DWORD));
+ delete [] specular;
+ specular = new_specular;
+ }
+
+ if (!loc || !nrm || !s_loc || !rw || !tu || !tv || !diffuse || !specular) {
+ Delete();
+ ZeroMemory(this, sizeof(VertexSet));
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+VertexSet::Delete()
+{
+ if (nverts) {
+ delete [] loc;
+ delete [] nrm;
+ delete [] s_loc;
+ delete [] rw;
+ delete [] tu;
+ delete [] tv;
+ delete [] tu1;
+ delete [] tv1;
+ delete [] diffuse;
+ delete [] specular;
+ delete [] tangent;
+ delete [] binormal;
+
+ tangent = 0;
+ binormal = 0;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+VertexSet::Clear()
+{
+ if (nverts) {
+ ZeroMemory(loc, sizeof(Vec3) * nverts);
+ ZeroMemory(nrm, sizeof(Vec3) * nverts);
+ ZeroMemory(s_loc, sizeof(Vec3) * nverts);
+ ZeroMemory(tu, sizeof(float) * nverts);
+ ZeroMemory(tv, sizeof(float) * nverts);
+ ZeroMemory(rw, sizeof(float) * nverts);
+ ZeroMemory(diffuse, sizeof(DWORD) * nverts);
+ ZeroMemory(specular, sizeof(DWORD) * nverts);
+
+ if (tu1)
+ ZeroMemory(tu1, sizeof(float) * nverts);
+
+ if (tv1)
+ ZeroMemory(tv1, sizeof(float) * nverts);
+
+ if (tangent)
+ ZeroMemory(tangent, sizeof(Vec3) * nverts);
+
+ if (binormal)
+ ZeroMemory(binormal, sizeof(Vec3) * nverts);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+VertexSet::CreateTangents()
+{
+ if (tangent) delete [] tangent;
+ if (binormal) delete [] binormal;
+
+ tangent = 0;
+ binormal = 0;
+
+ if (nverts) {
+ tangent = new(__FILE__,__LINE__) Vec3[nverts];
+ binormal = new(__FILE__,__LINE__) Vec3[nverts];
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+VertexSet::CreateAdditionalTexCoords()
+{
+ if (tu1) delete [] tu1;
+ if (tv1) delete [] tv1;
+
+ tu1 = 0;
+ tv1 = 0;
+
+ if (nverts) {
+ tu1 = new(__FILE__,__LINE__) float[nverts];
+ tv1 = new(__FILE__,__LINE__) float[nverts];
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VertexSet::CopyVertex(int dst, int src)
+{
+ if (src >= 0 && src < nverts && dst >= 0 && dst < nverts) {
+ loc[dst] = loc[src];
+ nrm[dst] = nrm[src];
+ s_loc[dst] = s_loc[src];
+ tu[dst] = tu[src];
+ tv[dst] = tv[src];
+ diffuse[dst] = diffuse[src];
+ specular[dst] = specular[src];
+
+ if (tu1)
+ tu1[dst] = tu1[src];
+
+ if (tv1)
+ tv1[dst] = tv1[src];
+
+ if (tangent)
+ tangent[dst] = tangent[src];
+
+ if (binormal)
+ binormal[dst] = binormal[src];
+
+ return true;
+ }
+
+ return false;
+}
+
+VertexSet*
+VertexSet::Clone() const
+{
+ VertexSet* result = new(__FILE__,__LINE__) VertexSet(nverts);
+
+ CopyMemory(result->loc, loc, nverts * sizeof(Vec3));
+ CopyMemory(result->nrm, nrm, nverts * sizeof(Vec3));
+ CopyMemory(result->s_loc, s_loc, nverts * sizeof(Vec3));
+ CopyMemory(result->rw, rw, nverts * sizeof(float));
+ CopyMemory(result->tu, tu, nverts * sizeof(float));
+ CopyMemory(result->tv, tv, nverts * sizeof(float));
+ CopyMemory(result->diffuse, diffuse, nverts * sizeof(DWORD));
+ CopyMemory(result->specular, specular, nverts * sizeof(DWORD));
+
+ if (tu1) {
+ if (!result->tu1)
+ result->tu1 = new(__FILE__,__LINE__) float[nverts];
+
+ CopyMemory(result->tu1, tu1, nverts * sizeof(float));
+ }
+
+ if (tv1) {
+ if (!result->tv1)
+ result->tv1 = new(__FILE__,__LINE__) float[nverts];
+
+ CopyMemory(result->tv1, tv1, nverts * sizeof(float));
+ }
+
+ if (tangent) {
+ if (!result->tangent)
+ result->tangent = new(__FILE__,__LINE__) Vec3[nverts];
+
+ CopyMemory(result->tangent, tangent, nverts * sizeof(Vec3));
+ }
+
+ if (binormal) {
+ if (!result->binormal)
+ result->binormal = new(__FILE__,__LINE__) Vec3[nverts];
+
+ CopyMemory(result->binormal, binormal, nverts * sizeof(Vec3));
+ }
+
+ return result;
+}
+
+void
+VertexSet::CalcExtents(Point& plus, Point& minus)
+{
+ plus = Point(-1e6, -1e6, -1e6);
+ minus = Point( 1e6, 1e6, 1e6);
+
+ for (int i = 0; i < nverts; i++) {
+ if (loc[i].x > plus.x) plus.x = loc[i].x;
+ if (loc[i].x < minus.x) minus.x = loc[i].x;
+ if (loc[i].y > plus.y) plus.y = loc[i].y;
+ if (loc[i].y < minus.y) minus.y = loc[i].y;
+ if (loc[i].z > plus.z) plus.z = loc[i].z;
+ if (loc[i].z < minus.z) minus.z = loc[i].z;
+ }
+}
+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+
+Poly::Poly(int init)
+ : nverts(0), visible(1), material(0), vertex_set(0), sortval(0), flatness(0)
+{ }
+
+// +--------------------------------------------------------------------+
+// Check to see if a test point is within the bounds of the poly.
+// The point is assumed to be coplanar with the poly. Return 1 if
+// the point is inside, 0 if the point is outside.
+
+Vec2 projverts[Poly::MAX_VERTS];
+
+static inline double extent3(double a, double b, double c)
+{
+ double d1 = fabs(a-b);
+ double d2 = fabs(a-c);
+ double d3 = fabs(b-c);
+
+ if (d1 > d2) {
+ if (d1 > d3)
+ return d1;
+ else
+ return d3;
+ }
+ else {
+ if (d2 > d3)
+ return d2;
+ else
+ return d3;
+ }
+}
+
+int Poly::Contains(const Vec3& pt) const
+{
+ // find largest 2d projection of this 3d Poly:
+ int projaxis;
+
+ double pnx = fabs(plane.normal.x);
+ double pny = fabs(plane.normal.y);
+ double pnz = fabs(plane.normal.z);
+
+ if (pnx > pny)
+ if (pnx > pnz)
+ if (plane.normal.x > 0)
+ projaxis = 1;
+ else
+ projaxis = -1;
+ else
+ if (plane.normal.z > 0)
+ projaxis = 3;
+ else
+ projaxis = -3;
+ else
+ if (pny > pnz)
+ if (plane.normal.y > 0)
+ projaxis = 2;
+ else
+ projaxis = -2;
+ else
+ if (plane.normal.z > 0)
+ projaxis = 3;
+ else
+ projaxis = -3;
+
+ int i;
+
+ for (i = 0; i < nverts; i++) {
+ Vec3 loc = vertex_set->loc[verts[i]];
+ switch (projaxis) {
+ case 1: projverts[i] = Vec2(loc.y, loc.z); break;
+ case -1: projverts[i] = Vec2(loc.z, loc.y); break;
+ case 2: projverts[i] = Vec2(loc.z, loc.x); break;
+ case -2: projverts[i] = Vec2(loc.x, loc.z); break;
+ case 3: projverts[i] = Vec2(loc.x, loc.y); break;
+ case -3: projverts[i] = Vec2(loc.y, loc.x); break;
+ }
+ }
+
+ // now project the test point into the same plane:
+ Vec2 test;
+ switch (projaxis) {
+ case 1: test.x = pt.y; test.y = pt.z; break;
+ case -1: test.x = pt.z; test.y = pt.y; break;
+ case 2: test.x = pt.z; test.y = pt.x; break;
+ case -2: test.x = pt.x; test.y = pt.z; break;
+ case 3: test.x = pt.x; test.y = pt.y; break;
+ case -3: test.x = pt.y; test.y = pt.x; break;
+ }
+
+ const float INSIDE_EPSILON = -0.01f;
+
+ // if the test point is outside of any segment,
+ // it is outside the entire convex Poly.
+ for (i = 0; i < nverts-1; i++) {
+ if (verts[i] != verts[i+1]) {
+ Vec2 segment = projverts[i+1] - projverts[i];
+ Vec2 segnorm = segment.normal();
+ Vec2 tdelta = projverts[i] - test;
+ float inside = segnorm * tdelta;
+ if (inside < INSIDE_EPSILON)
+ return 0;
+ }
+ }
+
+ // check last segment, too:
+ if (verts[0] != verts[nverts-1]) {
+ Vec2 segment = projverts[0] - projverts[nverts-1];
+ float inside = segment.normal() * (projverts[0] - test);
+ if (inside < INSIDE_EPSILON)
+ return 0;
+ }
+
+ // still here? must be inside:
+ return 1;
+}
+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+
+Material::Material()
+ : power(1.0f), brilliance(1.0f), bump(0.0f), blend(MTL_SOLID),
+ shadow(true), luminous(false),
+ tex_diffuse(0), tex_specular(0), tex_bumpmap(0), tex_emissive(0),
+ tex_alternate(0), tex_detail(0), thumbnail(0)
+{
+ ZeroMemory(name, sizeof(name));
+ ZeroMemory(shader, sizeof(shader));
+
+ ambient_value = 0.2f;
+ diffuse_value = 1.0f;
+ specular_value = 0.0f;
+ emissive_value = 0.0f;
+}
+
+// +--------------------------------------------------------------------+
+
+Material::~Material()
+{
+ // these objects are owned by the shared
+ // bitmap cache, so don't delete them now:
+ tex_diffuse = 0;
+ tex_specular = 0;
+ tex_bumpmap = 0;
+ tex_emissive = 0;
+ tex_alternate = 0;
+ tex_detail = 0;
+
+ // the thumbnail is unique to the material,
+ // so it is never cached:
+ if (thumbnail)
+ delete thumbnail;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Material::operator == (const Material& m) const
+{
+ if (this == &m) return 1;
+
+ if (Ka != m.Ka) return 0;
+ if (Kd != m.Kd) return 0;
+ if (Ks != m.Ks) return 0;
+ if (Ke != m.Ke) return 0;
+ if (power != m.power) return 0;
+ if (brilliance != m.brilliance) return 0;
+ if (bump != m.bump) return 0;
+ if (blend != m.blend) return 0;
+ if (shadow != m.shadow) return 0;
+ if (tex_diffuse != m.tex_diffuse) return 0;
+ if (tex_specular != m.tex_specular) return 0;
+ if (tex_bumpmap != m.tex_bumpmap) return 0;
+ if (tex_emissive != m.tex_emissive) return 0;
+ if (tex_alternate != m.tex_alternate) return 0;
+ if (tex_detail != m.tex_detail) return 0;
+
+ return !strcmp(name, m.name);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Material::Clear()
+{
+ Ka = ColorValue();
+ Kd = ColorValue();
+ Ks = ColorValue();
+ Ke = ColorValue();
+
+ power = 1.0f;
+ bump = 0.0f;
+ blend = MTL_SOLID;
+ shadow = true;
+
+ tex_diffuse = 0;
+ tex_specular = 0;
+ tex_bumpmap = 0;
+ tex_emissive = 0;
+ tex_alternate = 0;
+ tex_detail = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+static char shader_name[Material::NAMELEN];
+
+const char*
+Material::GetShader(int pass) const
+{
+ int level = 0;
+ if (pass > 1) pass--;
+
+ for (int i = 0; i < NAMELEN; i++) {
+ if (shader[i] == '/') {
+ level++;
+
+ if (level > pass)
+ return 0;
+ }
+
+ else if (shader[i] != 0) {
+ if (level == pass) {
+ ZeroMemory(shader_name, NAMELEN);
+
+ char* s = shader_name;
+ while (i < NAMELEN && shader[i] != 0 && shader[i] != '/') {
+ *s++ = shader[i++];
+ }
+
+ return shader_name;
+ }
+ }
+
+ else {
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Material::CreateThumbnail(int size)
+{
+ if (!thumbnail) {
+ thumbnail = new(__FILE__,__LINE__) Bitmap(size, size);
+ }
+
+ if (!thumbnail || thumbnail->Width() != thumbnail->Height())
+ return;
+
+ size = thumbnail->Width();
+
+ DWORD* image = new(__FILE__,__LINE__) DWORD[size*size];
+ DWORD* dst = image;
+
+ for (int j = 0; j < size; j++) {
+ for (int i = 0; i < size; i++) {
+ *dst++ = GetThumbColor(i, j, size);
+ }
+ }
+
+ thumbnail->CopyHighColorImage(size, size, image, Bitmap::BMP_SOLID);
+}
+
+DWORD
+Material::GetThumbColor(int i, int j, int size)
+{
+ Color result = Color::LightGray;
+
+ double x = i - size/2;
+ double y = j - size/2;
+ double r = 0.9 * size/2;
+ double d = sqrt(x*x + y*y);
+
+ if (d <= r) {
+ double z = sqrt(r*r - x*x - y*y);
+
+ Point loc(x,y,z);
+ Point nrm = loc; nrm.Normalize();
+ Point light(1,-1,1); light.Normalize();
+ Point eye(0,0,1);
+
+ ColorValue c = Ka * ColorValue(0.25f, 0.25f, 0.25f); // ambient light
+ ColorValue white(1,1,1);
+
+ double diffuse = nrm*light;
+ double v = 1 - (acos(nrm.y)/PI);
+ double u = asin(nrm.x / sin(acos(nrm.y))) / PI + 0.5;
+
+ ColorValue cd = Kd;
+ ColorValue cs = Ks;
+ ColorValue ce = Ke;
+
+ if (tex_diffuse) {
+ int tu = (int) (u * tex_diffuse->Width());
+ int tv = (int) (v * tex_diffuse->Height());
+ cd = Kd * tex_diffuse->GetColor(tu,tv);
+ }
+
+ if (tex_emissive) {
+ int tu = (int) (u * tex_emissive->Width());
+ int tv = (int) (v * tex_emissive->Height());
+ ce = Ke * tex_emissive->GetColor(tu,tv);
+ }
+
+ if (tex_bumpmap && bump != 0 && nrm.z > 0) {
+ // compute derivatives B(u,v)
+ int tu = (int) (u * tex_bumpmap->Width());
+ int tv = (int) (v * tex_bumpmap->Height());
+
+ double du1 = tex_bumpmap->GetColor(tu,tv).Red() -
+ tex_bumpmap->GetColor(tu-1,tv).Red();
+ double du2 = tex_bumpmap->GetColor(tu+1,tv).Red() -
+ tex_bumpmap->GetColor(tu,tv).Red();
+
+ double dv1 = tex_bumpmap->GetColor(tu,tv).Red() -
+ tex_bumpmap->GetColor(tu,tv-1).Red();
+ double dv2 = tex_bumpmap->GetColor(tu,tv+1).Red() -
+ tex_bumpmap->GetColor(tu,tv).Red();
+
+ double du = (du1 + du2) / 512 * 1e-8;
+ double dv = (dv1 + dv2) / 512 * 1e-8;
+
+ if (du || dv) {
+ Point Nu = nrm.cross(Point(0,-1,0)); Nu.Normalize();
+ Point Nv = nrm.cross(Point(1, 0,0)); Nv.Normalize();
+
+ nrm += (Nu*du*bump);
+ nrm += (Nv*dv*bump);
+ nrm.Normalize();
+
+ diffuse = nrm*light;
+ v = 1 - (acos(nrm.y)/PI);
+ u = asin(nrm.x / sin(acos(nrm.y))) / PI + 0.5;
+ }
+ }
+
+ if (tex_specular) {
+ int tu = (int) (u * tex_specular->Width());
+ int tv = (int) (v * tex_specular->Height());
+ cs = Ks * tex_specular->GetColor(tu,tv);
+ }
+
+ // anisotropic diffuse lighting
+ if (brilliance >= 0) {
+ diffuse = pow(diffuse, brilliance);
+ }
+
+ // forward lighting
+ if (diffuse > 0) {
+ // diffuse
+ c += cd * (white * diffuse);
+
+ // specular
+ if (power > 0) {
+ double spec = ((nrm * 2*(nrm*light) - light) * eye);
+ if (spec > 0.01) {
+ spec = pow(spec, power);
+ c += cs * (white * spec);
+ }
+ }
+ }
+
+ // back lighting
+ else {
+ diffuse *= -0.5;
+ c += cd * (white * diffuse);
+
+ // specular
+ if (power > 0) {
+ light *= -1;
+
+ double spec = ((nrm * 2*(nrm*light) - light) * eye);
+ if (spec > 0.01) {
+ spec = pow(spec, power);
+ c += cs * (white * spec) * 0.7;
+ }
+ }
+ }
+
+ c += ce;
+
+ result = c.ToColor();
+ }
+
+ return result.Value();
+} \ No newline at end of file
diff --git a/nGenEx/Polygon.h b/nGenEx/Polygon.h
new file mode 100644
index 0000000..ebb7bfb
--- /dev/null
+++ b/nGenEx/Polygon.h
@@ -0,0 +1,159 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Polygon.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Polygon structures: VertexSet, Poly, Material
+*/
+
+#ifndef Polygon_h
+#define Polygon_h
+
+#include "Geometry.h"
+#include "Color.h"
+
+// +--------------------------------------------------------------------+
+
+class Bitmap;
+struct Poly;
+struct Material;
+struct VertexSet;
+
+// +--------------------------------------------------------------------+
+
+struct Poly
+{
+ static const char* TYPENAME() { return "Poly"; }
+
+ enum { MAX_VERTS = 4 };
+
+ Poly() { }
+ Poly(int init);
+ ~Poly() { }
+
+ int operator < (const Poly& p) const { return sortval < p.sortval; }
+ int operator == (const Poly& p) const { return this == &p; }
+
+ int Contains(const Vec3& pt) const;
+
+ BYTE nverts;
+ BYTE visible;
+ WORD verts[MAX_VERTS];
+ WORD vlocs[MAX_VERTS];
+ VertexSet* vertex_set;
+ Material* material;
+ int sortval;
+ float flatness;
+ Plane plane;
+};
+
+// +--------------------------------------------------------------------+
+
+struct Material
+{
+ static const char* TYPENAME() { return "Material"; }
+
+ enum BLEND_TYPE { MTL_SOLID=1, MTL_TRANSLUCENT=2, MTL_ADDITIVE=4 };
+ enum { NAMELEN=32 };
+
+ Material();
+ ~Material();
+
+ int operator == (const Material& m) const;
+
+ void Clear();
+
+ char name[NAMELEN];
+ char shader[NAMELEN];
+
+ ColorValue Ka; // ambient color
+ ColorValue Kd; // diffuse color
+ ColorValue Ks; // specular color
+ ColorValue Ke; // emissive color
+ float power; // highlight sharpness (big=shiny)
+ float brilliance; // diffuse power function
+ float bump; // bump level (0=none)
+ DWORD blend; // alpha blend type
+ bool shadow; // casts shadow
+ bool luminous; // verts have their own lighting
+
+ Bitmap* tex_diffuse;
+ Bitmap* tex_specular;
+ Bitmap* tex_bumpmap;
+ Bitmap* tex_emissive;
+ Bitmap* tex_alternate;
+ Bitmap* tex_detail;
+
+ bool IsSolid() const { return blend == MTL_SOLID; }
+ bool IsTranslucent() const { return blend == MTL_TRANSLUCENT; }
+ bool IsGlowing() const { return blend == MTL_ADDITIVE; }
+ const char* GetShader(int n) const;
+
+ //
+ // Support for Magic GUI
+ //
+
+ Color ambient_color;
+ Color diffuse_color;
+ Color specular_color;
+ Color emissive_color;
+
+ float ambient_value;
+ float diffuse_value;
+ float specular_value;
+ float emissive_value;
+
+ Bitmap* thumbnail; // preview image
+
+ void CreateThumbnail(int size=128);
+ DWORD GetThumbColor(int i, int j, int size);
+};
+
+// +--------------------------------------------------------------------+
+
+struct VertexSet
+{
+ static const char* TYPENAME() { return "VertexSet"; }
+
+ enum VertexSpaces { OBJECT_SPACE, WORLD_SPACE, VIEW_SPACE, SCREEN_SPACE };
+
+ VertexSet(int m);
+ ~VertexSet();
+
+ void Resize(int m, bool preserve=false);
+ void Delete();
+ void Clear();
+ void CreateTangents();
+ void CreateAdditionalTexCoords();
+ bool CopyVertex(int dst, int src);
+ void CalcExtents(Point& plus, Point& minus);
+
+ VertexSet* Clone() const;
+
+ int nverts;
+ int space;
+
+ Vec3* loc;
+ Vec3* nrm;
+ Vec3* s_loc;
+ float* rw;
+ float* tu;
+ float* tv;
+ float* tu1;
+ float* tv1;
+ DWORD* diffuse;
+ DWORD* specular;
+ Vec3* tangent;
+ Vec3* binormal;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif Polygon_h
+
diff --git a/nGenEx/Projector.cpp b/nGenEx/Projector.cpp
new file mode 100644
index 0000000..af88d08
--- /dev/null
+++ b/nGenEx/Projector.cpp
@@ -0,0 +1,470 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Projector.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ 3D Projection Camera class
+*/
+
+#include "MemDebug.h"
+#include "Projector.h"
+
+// +--------------------------------------------------------------------+
+
+static const float CLIP_PLANE_EPSILON = 0.0001f;
+static const double Z_NEAR = 1.0;
+
+void Print(const char* fmt, ...);
+
+// +--------------------------------------------------------------------+
+
+static Camera emergency_cam;
+
+// +--------------------------------------------------------------------+
+
+Projector::Projector(Window* window, Camera* cam)
+ : camera(cam), infinite(0), depth_scale(1.0f), orthogonal(false), field_of_view(2)
+{
+ if (!camera)
+ camera = &emergency_cam;
+
+ UseWindow(window);
+}
+
+Projector::~Projector()
+{ }
+
+// +--------------------------------------------------------------------+
+
+void
+Projector::UseCamera(Camera* cam)
+{
+ if (cam)
+ camera = cam;
+ else
+ camera = &emergency_cam;
+}
+
+void
+Projector::UseWindow(Window* win)
+{
+ Rect r = win->GetRect();
+ width = r.w;
+ height = r.h;
+
+ xcenter = (width / 2.0);
+ ycenter = (height / 2.0);
+
+ xclip0 = 0.0f;
+ xclip1 = (float) width-0.5f;
+ yclip0 = 0.0f;
+ yclip1 = (float) height-0.5f;
+
+ SetFieldOfView(field_of_view);
+}
+
+void
+Projector::SetFieldOfView(double fov)
+{
+ field_of_view = fov;
+
+ xscreenscale = width / fov;
+ yscreenscale = height / fov;
+
+ maxscale = max(xscreenscale, yscreenscale);
+
+ xangle = atan(2.0/fov * maxscale/xscreenscale);
+ yangle = atan(2.0/fov * maxscale/yscreenscale);
+}
+
+double
+Projector::GetFieldOfView() const
+{
+ return field_of_view;
+}
+
+void
+Projector::SetDepthScale(float scale)
+{
+ depth_scale = scale;
+}
+
+double
+Projector::GetDepthScale() const
+{
+ return depth_scale;
+}
+
+int
+Projector::SetInfinite(int i)
+{
+ int old = infinite;
+ infinite = i;
+ return old;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Projector::StartFrame()
+{
+ SetUpFrustum();
+ SetWorldSpace();
+}
+
+// +--------------------------------------------------------------------+
+// Transform a point from worldspace to viewspace.
+// +--------------------------------------------------------------------+
+
+void
+Projector::Transform(Vec3& vec) const
+{
+ Vec3 tvert = vec;
+
+ // Translate into a viewpoint-relative coordinate
+ if (!infinite)
+ tvert -= camera->Pos();
+
+ // old method:
+ vec.x = (tvert * camera->vrt());
+ vec.y = (tvert * camera->vup());
+ vec.z = (tvert * camera->vpn());
+
+ // Rotate into the view orientation
+ // vec = tvert * camera->Orientation();
+}
+
+// +--------------------------------------------------------------------+
+// Transform a point from worldspace to viewspace.
+// +--------------------------------------------------------------------+
+
+void
+Projector::Transform(Point& point) const
+{
+ Point tvert = point;
+
+ // Translate into a viewpoint-relative coordinate
+ if (!infinite)
+ tvert -= camera->Pos();
+
+ // old method:
+ point.x = (tvert * camera->vrt());
+ point.y = (tvert * camera->vup());
+ point.z = (tvert * camera->vpn());
+
+ // Rotate into the view orientation
+ // point = tvert * camera->Orientation();
+}
+
+// +--------------------------------------------------------------------+
+// APPARENT RADIUS OF PROJECTED OBJECT
+// Project a viewspace point into screen coordinates.
+// Use projected Z to determine apparent radius of object.
+// +--------------------------------------------------------------------+
+
+float
+Projector::ProjectRadius(const Vec3& v, float radius) const
+{
+ return (float) fabs((radius * maxscale) / v.z);
+}
+
+// +--------------------------------------------------------------------+
+// IN PLACE PROJECTION OF POINT
+// Project a viewspace point into screen coordinates.
+// Note that the y axis goes up in worldspace and viewspace, but
+// goes down in screenspace.
+// +--------------------------------------------------------------------+
+
+void
+Projector::Project(Vec3& v, bool clamp) const
+{
+ double zrecip;
+
+ if (orthogonal) {
+ double scale = field_of_view/2;
+ v.x = (float) (xcenter + scale * v.x);
+ v.y = (float) (height - (ycenter + scale * v.y));
+ v.z = (float) (0.0f);
+ }
+
+ else {
+ //zrecip = 2 * (1.0e5 / (1.0e5-1)) / v.z;
+ //zrecip = 2 * 0.97 / v.z; -- what the heck was this version used for?
+
+ zrecip = 2 / v.z;
+ v.x = (float) (xcenter + maxscale * v.x * zrecip);
+ v.y = (float) (height - (ycenter + maxscale * v.y * zrecip));
+ v.z = (float) (1 - zrecip);
+ }
+
+ // clamp the point to the viewport:
+ if (clamp) {
+ if (v.x < xclip0) v.x = xclip0;
+ if (v.x > xclip1) v.x = xclip1;
+ if (v.y < yclip0) v.y = yclip0;
+ if (v.y > yclip1) v.y = yclip1;
+ }
+}
+
+// +--------------------------------------------------------------------+
+// IN PLACE PROJECTION OF POINT
+// Project a viewspace point into screen coordinates.
+// Note that the y axis goes up in worldspace and viewspace, but
+// goes down in screenspace.
+// +--------------------------------------------------------------------+
+
+void
+Projector::Project(Point& v, bool clamp) const
+{
+ double zrecip;
+
+ if (orthogonal) {
+ double scale = field_of_view/2;
+ v.x = (xcenter + scale * v.x);
+ v.y = (height - (ycenter + scale * v.y));
+ v.z = 0;
+ }
+
+ else {
+ zrecip = 1 / v.z;
+ v.x = (xcenter + 2 * maxscale * v.x * zrecip);
+ v.y = (height - (ycenter + 2 * maxscale * v.y * zrecip));
+ v.z = (1 - zrecip);
+ }
+
+ // clamp the point to the viewport:
+ if (clamp) {
+ if (v.x < xclip0) v.x = xclip0;
+ if (v.x > xclip1) v.x = xclip1;
+ if (v.y < yclip0) v.y = yclip0;
+ if (v.y > yclip1) v.y = yclip1;
+ }
+}
+
+// +--------------------------------------------------------------------+
+// IN PLACE UN-PROJECTION OF POINT
+// Convert a point in screen coordinates back to viewspace.
+// Note that the y axis goes up in worldspace and viewspace, but
+// goes down in screenspace.
+// +--------------------------------------------------------------------+
+
+void
+Projector::Unproject(Point& v) const
+{
+ double zrecip = 1 / v.z;
+
+ /***
+ * forward projection:
+ v.x = (xcenter + maxscale * v.x * zrecip);
+ v.y = (height - (ycenter + maxscale * v.y * zrecip));
+ v.z = (1 - zrecip);
+ ***/
+
+ v.x = ( v.x - xcenter) / (maxscale * zrecip);
+ v.y = (height - v.y - ycenter) / (maxscale * zrecip);
+}
+
+// +--------------------------------------------------------------------+
+// IN PLACE PROJECTION OF RECTANGLE (FOR SPRITES)
+// Project a viewspace point into screen coordinates.
+// Note that the y axis goes up in worldspace and viewspace, but
+// goes down in screenspace.
+// +--------------------------------------------------------------------+
+
+void
+Projector::ProjectRect(Point& v, double& w, double& h) const
+{
+ double zrecip;
+
+ if (orthogonal) {
+ double scale = field_of_view/2;
+ v.x = (xcenter + scale * v.x);
+ v.y = (height - (ycenter + scale * v.y));
+ v.z = 0;
+ }
+
+ else {
+ zrecip = 1 / v.z;
+ v.x = (xcenter + 2 * maxscale * v.x * zrecip);
+ v.y = (height - (ycenter + 2 * maxscale * v.y * zrecip));
+ v.z = (1 - Z_NEAR*zrecip);
+
+ w *= maxscale * zrecip;
+ h *= maxscale * zrecip;
+ }
+}
+
+// +--------------------------------------------------------------------+
+// Set up a clip plane with the specified normal.
+// +--------------------------------------------------------------------+
+
+void
+Projector::SetWorldspaceClipPlane(Vec3& normal, Plane& plane)
+{
+ // Rotate the plane normal into worldspace
+ ViewToWorld(normal, plane.normal);
+ plane.distance = (float) (camera->Pos() * plane.normal + CLIP_PLANE_EPSILON);
+}
+
+// +--------------------------------------------------------------------+
+// Set up the planes of the frustum, in worldspace coordinates.
+// +--------------------------------------------------------------------+
+
+void
+Projector::SetUpFrustum()
+{
+ double angle, s, c;
+ Vec3 normal;
+
+ angle = XAngle();
+ s = sin(angle);
+ c = cos(angle);
+
+ // Left clip plane
+ normal.x = (float) s;
+ normal.y = (float) 0;
+ normal.z = (float) c;
+ view_planes[0].normal = normal;
+ view_planes[0].distance = CLIP_PLANE_EPSILON;
+ SetWorldspaceClipPlane(normal, world_planes[0]);
+
+ // Right clip plane
+ normal.x = (float) -s;
+ view_planes[1].normal = normal;
+ view_planes[1].distance = CLIP_PLANE_EPSILON;
+ SetWorldspaceClipPlane(normal, world_planes[1]);
+
+ angle = YAngle();
+ s = sin(angle);
+ c = cos(angle);
+
+ // Bottom clip plane
+ normal.x = (float) 0;
+ normal.y = (float) s;
+ normal.z = (float) c;
+ view_planes[2].normal = normal;
+ view_planes[2].distance = CLIP_PLANE_EPSILON;
+ SetWorldspaceClipPlane(normal, world_planes[2]);
+
+ // Top clip plane
+ normal.y = (float) -s;
+ view_planes[3].normal = normal;
+ view_planes[3].distance = CLIP_PLANE_EPSILON;
+ SetWorldspaceClipPlane(normal, world_planes[3]);
+}
+
+// +--------------------------------------------------------------------+
+// Clip the point against the frustum and return 1 if partially inside
+// Return 2 if completely inside
+// +--------------------------------------------------------------------+
+
+int
+Projector::IsVisible(const Vec3& v, float radius) const
+{
+ int visible = 1;
+ int complete = 1;
+
+ Plane* plane = (Plane*) frustum_planes;
+ if (infinite) {
+ complete = 0;
+
+ for (int i = 0; visible && (i < NUM_FRUSTUM_PLANES); i++) {
+ visible = ((v * plane->normal) >= CLIP_PLANE_EPSILON);
+ plane++;
+ }
+ }
+ else {
+ for (int i = 0; visible && (i < NUM_FRUSTUM_PLANES); i++) {
+ float dot = v * plane->normal;
+ visible = ((dot + radius) >= plane->distance);
+ complete = complete && ((dot - radius) >= plane->distance);
+ plane++;
+ }
+ }
+
+ return visible + complete;
+}
+
+// +--------------------------------------------------------------------+
+// Clip the bouding point against the frustum and return non zero
+// if at least partially inside. This version is not terribly
+// efficient as it checks all eight box corners rather than just
+// the minimum two.
+// +--------------------------------------------------------------------+
+
+int
+Projector::IsBoxVisible(const Point* p) const
+{
+ int i, j, outside = 0;
+
+ // if all eight corners are outside of the same
+ // frustrum plane, then the box is not visible
+ Plane* plane = (Plane*) frustum_planes;
+
+ if (infinite) {
+ for (i = 0; !outside && (i < NUM_FRUSTUM_PLANES); i++) {
+ for (j = 0; j < 8; j++)
+ outside += (p[j] * plane->normal) < CLIP_PLANE_EPSILON;
+
+ if (outside < 8)
+ outside = 0;
+
+ plane++;
+ }
+ }
+ else {
+ for (i = 0; !outside && (i < NUM_FRUSTUM_PLANES); i++) {
+ for (j = 0; j < 8; j++)
+ outside += (p[j] * plane->normal) < plane->distance;
+
+ if (outside < 8)
+ outside = 0;
+
+ plane++;
+ }
+ }
+
+ // if not outside, then the box is visible
+ return !outside;
+}
+
+// +--------------------------------------------------------------------+
+
+float
+Projector::ApparentRadius(const Vec3& v, float radius) const
+{
+ Vec3 vloc = v;
+
+ Transform(vloc); // transform in place
+ return ProjectRadius(vloc, radius);
+}
+
+
+// +--------------------------------------------------------------------+
+// Rotate a vector from viewspace to worldspace.
+// +--------------------------------------------------------------------+
+
+void
+Projector::ViewToWorld(Point& pin, Point& pout)
+{
+ // Rotate into the world orientation
+ pout.x = pin.x * camera->vrt().x + pin.y * camera->vup().x + pin.z * camera->vpn().x;
+ pout.y = pin.x * camera->vrt().y + pin.y * camera->vup().y + pin.z * camera->vpn().y;
+ pout.z = pin.x * camera->vrt().z + pin.y * camera->vup().z + pin.z * camera->vpn().z;
+}
+
+void
+Projector::ViewToWorld(Vec3& vin, Vec3& vout)
+{
+ // Rotate into the world orientation
+ vout.x = (float) (vin.x * camera->vrt().x + vin.y * camera->vup().x + vin.z * camera->vpn().x);
+ vout.y = (float) (vin.x * camera->vrt().y + vin.y * camera->vup().y + vin.z * camera->vpn().y);
+ vout.z = (float) (vin.x * camera->vrt().z + vin.y * camera->vup().z + vin.z * camera->vpn().z);
+}
+
diff --git a/nGenEx/Projector.h b/nGenEx/Projector.h
new file mode 100644
index 0000000..e70a383
--- /dev/null
+++ b/nGenEx/Projector.h
@@ -0,0 +1,106 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Projector.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ 3D Projection Camera class
+*/
+
+#ifndef Projector_h
+#define Projector_h
+
+#include "Geometry.h"
+#include "Window.h"
+#include "Camera.h"
+#include "Polygon.h"
+
+// +--------------------------------------------------------------------+
+
+class Projector
+{
+public:
+ Projector(Window* win, Camera* cam);
+ virtual ~Projector();
+
+ // Operations:
+ virtual void UseWindow(Window* win);
+ virtual void UseCamera(Camera* cam);
+ virtual void SetDepthScale(float scale);
+ virtual double GetDepthScale() const;
+ virtual void SetFieldOfView(double fov);
+ virtual double GetFieldOfView() const;
+ virtual int SetInfinite(int i);
+ virtual void StartFrame();
+
+ // accessor:
+ Point Pos() const { return camera->Pos(); }
+ Point vrt() { return camera->vrt(); }
+ Point vup() { return camera->vup(); }
+ Point vpn() { return camera->vpn(); }
+ const Matrix& Orientation() const { return camera->Orientation(); }
+
+ double XAngle() const { return xangle; }
+ double YAngle() const { return yangle; }
+
+ bool IsOrthogonal() const { return orthogonal; }
+ void SetOrthogonal(bool o) { orthogonal = o; }
+
+ // projection and clipping geometry:
+ virtual void Transform(Vec3& vec) const;
+ virtual void Transform(Point& point) const;
+
+ virtual void Project(Vec3& vec, bool clamp=true) const;
+ virtual void Project(Point& point, bool clamp=true) const;
+ virtual void ProjectRect(Point& origin, double& w, double& h) const;
+
+ virtual float ProjectRadius(const Vec3& vec, float radius) const;
+
+ virtual void Unproject(Point& point) const;
+ int IsVisible(const Vec3& v, float radius) const;
+ int IsBoxVisible(const Point* p) const;
+
+ float ApparentRadius(const Vec3& v, float radius) const;
+
+ virtual void SetWorldSpace() { frustum_planes = world_planes; }
+ virtual void SetViewSpace() { frustum_planes = view_planes; }
+
+ Plane* GetCurrentClipPlanes() { return frustum_planes; }
+
+ void SetUpFrustum();
+ void ViewToWorld(Point& pin, Point& pout);
+ void ViewToWorld(Vec3& vin, Vec3& vout);
+ void SetWorldspaceClipPlane(Vec3& normal, Plane& plane);
+
+protected:
+ Camera* camera;
+
+ int width, height;
+ double field_of_view;
+ double xscreenscale, yscreenscale, maxscale;
+ double xcenter, ycenter;
+ double xangle, yangle;
+
+ int infinite;
+ float depth_scale;
+ bool orthogonal;
+
+ enum DISPLAY_CONST {
+ NUM_FRUSTUM_PLANES= 4,
+ };
+
+ Plane* frustum_planes;
+ Plane world_planes[NUM_FRUSTUM_PLANES];
+ Plane view_planes[NUM_FRUSTUM_PLANES];
+
+ float xclip0, xclip1;
+ float yclip0, yclip1;
+};
+
+#endif Projector_h
+
diff --git a/nGenEx/Random.cpp b/nGenEx/Random.cpp
new file mode 100644
index 0000000..646b385
--- /dev/null
+++ b/nGenEx/Random.cpp
@@ -0,0 +1,148 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Random.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Utility functions for generating random numbers and locations.
+*/
+
+#include "MemDebug.h"
+#include "Random.h"
+
+// +----------------------------------------------------------------------+
+
+void RandomInit()
+{
+ srand(timeGetTime());
+}
+
+// +----------------------------------------------------------------------+
+
+Point RandomDirection()
+{
+ Point p = Point(rand() - 16384, rand() - 16384, 0);
+ p.Normalize();
+ return p;
+}
+
+// +----------------------------------------------------------------------+
+
+Point RandomPoint()
+{
+ Point p = Point(rand() - 16384, rand() - 16384, 0);
+ p.Normalize();
+ p *= 15e3 + rand()/3;
+ return p;
+}
+
+// +----------------------------------------------------------------------+
+
+Vec3 RandomVector(double radius)
+{
+ Vec3 v = Vec3(rand() - 16384, rand() - 16384, rand() - 16384);
+ v.Normalize();
+
+ if (radius > 0)
+ v *= (float) radius;
+ else
+ v *= (float) Random(radius/3, radius);
+
+ return v;
+}
+
+// +----------------------------------------------------------------------+
+
+double Random(double min, double max)
+{
+ double delta = max - min;
+ double r = delta * rand() / 32768.0;
+
+ return min + r;
+}
+
+// +----------------------------------------------------------------------+
+
+int RandomIndex()
+{
+ static int index = 0;
+ static int table[16] = { 0, 9, 4, 7, 14, 11, 2, 12, 1, 5, 13, 8, 6, 10, 3, 15 };
+
+ int r = 1 + ((rand() & 0x0700) >> 8);
+ index += r;
+ if (index > 1e7) index = 0;
+ return table[index % 16];
+}
+
+// +----------------------------------------------------------------------+
+
+bool RandomChance(int wins, int tries)
+{
+ double fraction = 256.0 * wins / tries;
+ double r = (rand() >> 4) & 0xFF;
+
+ return r < fraction;
+}
+
+// +----------------------------------------------------------------------+
+
+int RandomSequence(int current, int range)
+{
+ if (range > 1) {
+ int step = (int) Random(1, range-1);
+ return (current + step) % range;
+ }
+
+ return current;
+}
+
+// +----------------------------------------------------------------------+
+
+int RandomShuffle(int count)
+{
+ static int set_size = -1;
+ static BYTE set[256];
+ static int index = -1;
+
+ if (count < 0 || count > 250)
+ return 0;
+
+ if (set_size != count) {
+ set_size = count;
+ index = -1;
+ }
+
+ // need to reshuffle
+ if (index < 0 || index > set_size-1) {
+ // set up the deck
+ int tmp[256];
+ for (int i = 0; i < 256; i++)
+ tmp[i] = i;
+
+ // shuffle the cards
+ for (i = 0; i < set_size; i++) {
+ int n = (int) Random(0, set_size);
+ int tries = set_size;
+ while (tmp[n] < 0 && tries--) {
+ n = (n+1) % set_size;
+ }
+
+ if (tmp[n] >= 0) {
+ set[i] = tmp[n];
+ tmp[n] = -1;
+ }
+ else {
+ set[i] = 0;
+ }
+ }
+
+ index = 0;
+ }
+
+ return set[index++];
+}
diff --git a/nGenEx/Random.h b/nGenEx/Random.h
new file mode 100644
index 0000000..05411e1
--- /dev/null
+++ b/nGenEx/Random.h
@@ -0,0 +1,35 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Random.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Utility functions for generating random numbers and locations.
+*/
+
+#ifndef Random_h
+#define Random_h
+
+#include "Types.h"
+#include "Geometry.h"
+
+// +----------------------------------------------------------------------+
+
+void RandomInit();
+Point RandomDirection();
+Point RandomPoint();
+Vec3 RandomVector(double radius);
+double Random(double min=0, double max=1);
+int RandomIndex();
+bool RandomChance(int wins=1, int tries=2);
+int RandomSequence(int current, int range);
+int RandomShuffle(int count);
+
+// +----------------------------------------------------------------------+
+
+#endif Random_h
diff --git a/nGenEx/Res.cpp b/nGenEx/Res.cpp
new file mode 100644
index 0000000..60903be
--- /dev/null
+++ b/nGenEx/Res.cpp
@@ -0,0 +1,28 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Res.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Abstract Resource class
+*/
+
+#include "MemDebug.h"
+#include "Res.h"
+
+// +--------------------------------------------------------------------+
+
+static int RESOURCE_KEY = 1;
+
+Resource::Resource()
+ : id((HANDLE) RESOURCE_KEY++)
+{ }
+
+Resource::~Resource()
+{ }
+
diff --git a/nGenEx/Res.h b/nGenEx/Res.h
new file mode 100644
index 0000000..4732d23
--- /dev/null
+++ b/nGenEx/Res.h
@@ -0,0 +1,37 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Res.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Abstract Resource class
+*/
+
+#ifndef Res_h
+#define Res_h
+
+#include "Types.h"
+
+// +--------------------------------------------------------------------+
+
+class Resource
+{
+public:
+ Resource();
+ virtual ~Resource();
+
+ int operator == (const Resource& r) const { return id == r.id; }
+
+ HANDLE Handle() const { return id; }
+
+protected:
+ HANDLE id;
+};
+
+#endif Res_h
+
diff --git a/nGenEx/Resource.h b/nGenEx/Resource.h
new file mode 100644
index 0000000..0b19029
--- /dev/null
+++ b/nGenEx/Resource.h
@@ -0,0 +1,21 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by nGen.rc
+//
+#define IDI_ICON1 101
+#define IDR_MENU1 102
+#define IDM_RAMP 40001
+#define IDM_RGB 40002
+#define IDM_HAL 40003
+#define IDM_EXIT 40004
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 105
+#define _APS_NEXT_COMMAND_VALUE 40005
+#define _APS_NEXT_CONTROL_VALUE 1000
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/nGenEx/RichTextBox.cpp b/nGenEx/RichTextBox.cpp
new file mode 100644
index 0000000..f7c75ff
--- /dev/null
+++ b/nGenEx/RichTextBox.cpp
@@ -0,0 +1,454 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: RichTextBox.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Window class
+*/
+
+#include "MemDebug.h"
+#include "RichTextBox.h"
+#include "EventDispatch.h"
+#include "Color.h"
+#include "Bitmap.h"
+#include "Font.h"
+#include "FontMgr.h"
+#include "Mouse.h"
+#include "Screen.h"
+#include "View.h"
+
+// +--------------------------------------------------------------------+
+
+RichTextBox::RichTextBox(ActiveWindow* p, int ax, int ay, int aw, int ah, DWORD aid, DWORD astyle)
+ : ScrollWindow(p->GetScreen(), ax, ay, aw, ah, aid, astyle, p)
+{
+ leading = 2;
+
+ char buf[32];
+ sprintf(buf, "RichTextBox %d", id);
+ desc = buf;
+}
+
+RichTextBox::RichTextBox(Screen* screen, int ax, int ay, int aw, int ah, DWORD aid, DWORD astyle)
+ : ScrollWindow(screen, ax, ay, aw, ah, aid, astyle)
+{
+ leading = 2;
+
+ char buf[32];
+ sprintf(buf, "RichTextBox %d", id);
+ desc = buf;
+}
+
+RichTextBox::~RichTextBox()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+RichTextBox::SetText(const char* t)
+{
+ ActiveWindow::SetText(t);
+ ScrollTo(0);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+RichTextBox::DrawContent(const Rect& ctrl_rect)
+{
+ DrawTabbedText();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+RichTextBox::DrawTabbedText()
+{
+ if (font && text.length()) {
+ int border_size = 4;
+
+ if (style & WIN_RAISED_FRAME && style & WIN_SUNK_FRAME)
+ border_size = 8;
+
+ Rect label_rect = rect;
+
+ label_rect.x = border_size;
+ label_rect.y = border_size;
+ label_rect.w -= border_size * 2;
+ label_rect.h -= border_size * 2;
+
+ if (scroll_bar)
+ label_rect.w -= SCROLL_TRACK;
+
+ if (line_height < font->Height())
+ line_height = font->Height();
+
+ font->SetColor(fore_color);
+ DrawRichText(label_rect);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+int RichTextBox::find_next_word_start(const char* text, int index)
+{
+ // step through intra-word space:
+ while (text[index] && isspace(text[index]) &&
+ (text[index] != '\t') &&
+ (text[index] != '\n') &&
+ (text[index] != '<'))
+ index++;
+
+ return index;
+}
+
+int RichTextBox::find_next_word_end(const char* text, int index)
+{
+ // check for leading newline or tag:
+ if (text[index] == '\n' || text[index] == '\t' || text[index] == '<')
+ return index;
+
+ // step through intra-word space:
+ while (text[index] && isspace(text[index]))
+ index++;
+
+ // step through word:
+ while (text[index] && !isspace(text[index]) &&
+ (text[index] != '-') &&
+ (text[index] != '<'))
+ index++;
+
+ if (index) {
+ if (text[index] != '-')
+ return index-1;
+ else
+ return index;
+ }
+
+ return 0;
+}
+
+int RichTextBox::parse_hex_digit(char c)
+{
+ if (isalpha(c))
+ return 10 + tolower(c) - 'a';
+
+ else if (isdigit(c))
+ return c - '0';
+
+ return 0;
+}
+
+int RichTextBox::process_tag(const char* text, int index, Font*& font)
+{
+ if (text[index] == '<') {
+ char tag[64];
+ int i = 0;
+
+ while (text[index] && (text[index] != '>'))
+ tag[i++] = text[index++];
+
+ if (text[index] == '>')
+ tag[i++] = text[index++];
+
+ tag[i] = 0;
+
+ switch (tag[1]) {
+ case 'c':
+ case 'C': if (strnicmp(tag+1, "color", 5) == 0) {
+ int r = 0;
+ int g = 0;
+ int b = 0;
+
+ if (i > 12) {
+ r = 16 * parse_hex_digit(tag[ 7]) + parse_hex_digit(tag[ 8]);
+ g = 16 * parse_hex_digit(tag[ 9]) + parse_hex_digit(tag[10]);
+ b = 16 * parse_hex_digit(tag[11]) + parse_hex_digit(tag[12]);
+ }
+
+ font->SetColor(Color(r,g,b));
+ }
+ break;
+
+ case 'f':
+ case 'F': if (strnicmp(tag+1, "font", 4) == 0) {
+ Color current_color = Color::White;
+
+ if (font)
+ current_color = font->GetColor();
+
+ tag[i-1] = 0;
+ font = FontMgr::Find(tag+6);
+ font->SetColor(current_color);
+ }
+ break;
+ }
+ }
+
+ return index;
+}
+
+int
+RichTextBox::GetNextTab(int xpos)
+{
+ for (int i = 0; i < 10; i++) {
+ if (tab[i] > xpos)
+ return tab[i];
+ }
+
+ return (xpos / 20) * 20 + 20;
+}
+
+void
+RichTextBox::DrawRichText(Rect& text_rect)
+{
+ // clip the rect:
+ Rect clip_rect = ClipRect(text_rect);
+ clip_rect.h -= 8;
+
+ if (clip_rect.w < 1 || clip_rect.h < 1)
+ return;
+
+ const char* t = text.data();
+ int count = text.length();
+ int nlines = 0;
+
+ int xpos = 0;
+ int block_start = 0;
+ int block_count = 0;
+ int curr_word_end = -1;
+ int next_word_end = 0;
+ int eol_index = 0;
+
+ int new_line = 0;
+ int x_offset = 0;
+ int y_offset = 0;
+ int length = 0;
+
+ Font* rich_font = font;
+ rich_font->SetColor(fore_color);
+
+ if (smooth_scroll) {
+ double fraction = smooth_offset - (int) smooth_offset;
+
+ y_offset = (int) ((1-fraction) * (rich_font->Height() + leading));
+ }
+
+ // while there is still text:
+ while (block_start < count) {
+ bool found_tag = false;
+
+ do {
+ found_tag = false;
+
+ if (t[block_start] == '<') {
+ block_start = process_tag(t, block_start, rich_font);
+ found_tag = true;
+ }
+
+ else if (t[block_start] == '\t') {
+ block_start++;
+ x_offset = GetNextTab(x_offset);
+
+ if (x_offset > text_rect.w) {
+ nlines++;
+ if (nlines > top_index)
+ y_offset += rich_font->Height() + leading;
+ x_offset = 0;
+ new_line = false;
+ }
+
+ found_tag = true;
+ }
+
+ else if (t[block_start] == '\r') {
+ block_start++;
+
+ if (t[block_start] == '\n')
+ block_start++;
+
+ nlines++;
+ if (nlines > top_index)
+ y_offset += rich_font->Height() + leading;
+ x_offset = 0;
+ new_line = false;
+
+ found_tag = true;
+ }
+
+ else if (t[block_start] == '\n') {
+ block_start++;
+
+ if (t[block_start] == '\r')
+ block_start++;
+
+ nlines++;
+ if (nlines > top_index)
+ y_offset += rich_font->Height() + leading;
+ x_offset = 0;
+ new_line = false;
+
+ found_tag = true;
+ }
+ }
+ while (found_tag);
+
+ next_word_end = find_next_word_end(t, block_start);
+
+ if (!next_word_end || next_word_end == curr_word_end) {
+ new_line = true;
+ }
+
+ else if (t[next_word_end] == '\n') {
+ eol_index = curr_word_end = next_word_end;
+ new_line = true;
+ }
+
+ else {
+ int word_len = next_word_end - block_start + 1;
+
+ length = rich_font->StringWidth(t+block_start, word_len);
+
+ // if this word was too long, wrap to next line:
+ if (x_offset + length > text_rect.w) {
+ nlines++;
+ if (nlines > top_index)
+ y_offset += rich_font->Height() + leading;
+ x_offset = 0;
+ new_line = false;
+ }
+
+ // is there a trailing newline?
+ curr_word_end = next_word_end;
+
+ // check for a newline in the next block of white space:
+ eol_index = 0;
+ const char* eol = &t[curr_word_end+1];
+ while (*eol && isspace(*eol) && *eol != '\n')
+ eol++;
+
+ if (*eol == '\n') {
+ eol_index = eol - t;
+ new_line = true;
+ }
+ }
+
+ block_count = curr_word_end - block_start + 1;
+
+ if (block_count > 0) {
+ length = rich_font->StringWidth(t+block_start, block_count);
+ }
+
+ // there was a single word longer than the entire line:
+ else {
+ block_count = next_word_end - block_start + 1;
+ length = rich_font->StringWidth(t+block_start, block_count);
+ curr_word_end = next_word_end;
+ }
+
+ if (length > 0 && nlines >= top_index && nlines < top_index+page_size) {
+ int x1 = text_rect.x + x_offset + rect.x;
+ int y1 = text_rect.y + y_offset + rect.y;
+
+ rich_font->DrawString(t+block_start, block_count, x1, y1, clip_rect);
+ }
+
+ if (new_line) {
+ nlines++;
+ if (nlines > top_index)
+ y_offset += rich_font->Height() + leading;
+ x_offset = 0;
+ new_line = false;
+ }
+
+ else if (length < 1 || text[next_word_end] == '-') {
+ x_offset += length;
+ }
+
+ else {
+ x_offset += length + rich_font->SpaceWidth();
+ }
+
+ if (eol_index > 0)
+ curr_word_end = eol_index;
+
+ block_start = find_next_word_start(t, curr_word_end+1);
+ }
+
+ line_count = nlines;
+}
+
+// +--------------------------------------------------------------------+
+
+int RichTextBox::OnMouseMove(int x, int y)
+{
+ if (captured) {
+ ActiveWindow* test = GetCapture();
+
+ if (test != this) {
+ captured = false;
+ }
+
+ else {
+ if (scrolling == SCROLL_THUMB) {
+ mouse_y = y - rect.y - TRACK_START;
+
+ int dest = (int) ((double) mouse_y/track_length * (line_count-1));
+ ScrollTo(dest);
+ }
+ }
+ }
+
+ return ActiveWindow::OnMouseMove(x,y);
+}
+
+// +--------------------------------------------------------------------+
+
+int RichTextBox::OnLButtonDown(int x, int y)
+{
+ return ScrollWindow::OnLButtonDown(x,y);
+}
+
+// +--------------------------------------------------------------------+
+
+int RichTextBox::OnLButtonUp(int x, int y)
+{
+ return ScrollWindow::OnLButtonUp(x,y);
+}
+
+// +--------------------------------------------------------------------+
+
+int RichTextBox::OnMouseWheel(int wheel)
+{
+ return ScrollWindow::OnMouseWheel(wheel);
+}
+
+// +--------------------------------------------------------------------+
+
+int RichTextBox::OnClick()
+{
+ int fire_click = !scrolling;
+
+ if (scrolling == SCROLL_THUMB)
+ scrolling = SCROLL_NONE;
+
+ if (fire_click)
+ return ActiveWindow::OnClick();
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+int RichTextBox::OnKeyDown(int vk, int flags)
+{
+ return ScrollWindow::OnKeyDown(vk, flags);
+}
+
diff --git a/nGenEx/RichTextBox.h b/nGenEx/RichTextBox.h
new file mode 100644
index 0000000..deb7caa
--- /dev/null
+++ b/nGenEx/RichTextBox.h
@@ -0,0 +1,65 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: RichTextBox.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Rich Text Window - an HTML-like control
+*/
+
+#ifndef RichTextBox_h
+#define RichTextBox_h
+
+#include "Types.h"
+#include "Color.h"
+#include "Bitmap.h"
+#include "ScrollWindow.h"
+#include "EventTarget.h"
+#include "List.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class RichTextBox : public ScrollWindow
+{
+public:
+ static const char* TYPENAME() { return "RichTextBox"; }
+
+ RichTextBox(ActiveWindow* p, int ax, int ay, int aw, int ah, DWORD aid=0, DWORD astyle=0);
+ RichTextBox(Screen* s, int ax, int ay, int aw, int ah, DWORD aid=0, DWORD astyle=0);
+ virtual ~RichTextBox();
+
+ int operator == (const RichTextBox& w) const { return id == w.id; }
+
+ // Operations:
+ virtual void DrawContent(const Rect& ctrl_rect);
+ virtual void SetText(const char* t);
+
+ // Event Target Interface:
+ virtual int OnMouseMove(int x, int y);
+ virtual int OnLButtonDown(int x, int y);
+ virtual int OnLButtonUp(int x, int y);
+ virtual int OnMouseWheel(int wheel);
+ virtual int OnClick();
+
+ virtual int OnKeyDown(int vk, int flags);
+
+protected:
+ virtual void DrawTabbedText();
+ virtual void DrawRichText(Rect& text_rect);
+ int GetNextTab(int xpos);
+
+ virtual int find_next_word_start(const char* text, int index);
+ virtual int find_next_word_end(const char* text, int index);
+ virtual int parse_hex_digit(char c);
+ virtual int process_tag(const char* text, int index, Font*& font);
+
+};
+
+#endif RichTextBox_h
+
diff --git a/nGenEx/Scene.cpp b/nGenEx/Scene.cpp
new file mode 100644
index 0000000..26ef58e
--- /dev/null
+++ b/nGenEx/Scene.cpp
@@ -0,0 +1,261 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Scene.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ A 3D Scene
+*/
+
+#include "MemDebug.h"
+#include "Scene.h"
+#include "Graphic.h"
+#include "Light.h"
+
+void Print(const char* fmt, ...);
+
+// +--------------------------------------------------------------------+
+
+Scene::Scene()
+{ }
+
+Scene::~Scene()
+{
+ background.destroy();
+ foreground.destroy();
+ graphics.destroy();
+ sprites.destroy();
+
+ lights.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Scene::AddBackground(Graphic* g)
+{
+ if (g) {
+ if (!background.contains(g))
+ background.append(g);
+
+ g->SetScene(this);
+ }
+}
+
+void
+Scene::DelBackground(Graphic* g)
+{
+ if (g) {
+ background.remove(g);
+ g->SetScene(0);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Scene::AddForeground(Graphic* g)
+{
+ if (g) {
+ if (!foreground.contains(g))
+ foreground.append(g);
+
+ g->SetScene(this);
+ }
+}
+
+void
+Scene::DelForeground(Graphic* g)
+{
+ if (g) {
+ foreground.remove(g);
+ g->SetScene(0);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Scene::AddGraphic(Graphic* g)
+{
+ if (g) {
+ if (!graphics.contains(g))
+ graphics.append(g);
+
+ g->SetScene(this);
+ }
+}
+
+void
+Scene::DelGraphic(Graphic* g)
+{
+ if (g) {
+ graphics.remove(g) || // it's gotta be in here somewhere!
+ foreground.remove(g) || // use the logical-or operator to early
+ sprites.remove(g) || // out when we find it...
+ background.remove(g);
+
+ g->SetScene(0);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Scene::AddSprite(Graphic* g)
+{
+ if (g) {
+ if (!sprites.contains(g))
+ sprites.append(g);
+
+ g->SetScene(this);
+ }
+}
+
+void
+Scene::DelSprite(Graphic* g)
+{
+ if (g) {
+ sprites.remove(g);
+ g->SetScene(0);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Scene::AddLight(Light* l)
+{
+ if (l) {
+ if (!lights.contains(l))
+ lights.append(l);
+ l->SetScene(this);
+ }
+}
+
+void
+Scene::DelLight(Light* l)
+{
+ if (l) {
+ lights.remove(l);
+ l->SetScene(0);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Scene::Collect()
+{
+ ListIter<Graphic> iter = graphics;
+
+ while (++iter) {
+ Graphic* g = iter.value();
+ if (g->Life() == 0) {
+ delete iter.removeItem();
+ }
+ }
+
+ iter.attach(sprites);
+
+ while (++iter) {
+ Graphic* g = iter.value();
+ if (g->Life() == 0) {
+ delete iter.removeItem();
+ }
+ }
+
+ ListIter<Light> iter1 = lights;
+
+ while (++iter1) {
+ Light* l = iter1.value();
+ if (l->Life() == 0) {
+ delete iter1.removeItem();
+ }
+ }
+}
+
+
+// +--------------------------------------------------------------------+
+
+bool
+Scene::IsLightObscured(const Point& obj_pos, const Point& light_pos, double obj_radius, Point* impact_point) const
+{
+ Point dir = light_pos - obj_pos;
+ double len = dir.Normalize();
+
+ Scene* pThis = (Scene*) this; // cast-away const
+ Graphic* g = 0;
+ bool obscured = false;
+
+ ListIter<Graphic> g_iter = pThis->graphics;
+ while (++g_iter && !obscured) {
+ g = g_iter.value();
+
+ if (g->CastsShadow() && !g->Hidden() && !g->IsInfinite()) {
+ double gdist = (g->Location() - obj_pos).length();
+ if (gdist > 0.1 && // different than object being obscured
+ g->Radius() > obj_radius && // larger than object being obscured
+ (g->Radius()*400)/gdist > 10) { // projects to a resonable size
+
+ Point delta = (g->Location() - light_pos);
+
+ if (delta.length() > g->Radius() / 100) { // not the object that is emitting the light
+ Point impact;
+ obscured = g->CheckRayIntersection(obj_pos, dir, len, impact, false) ? true : false;
+
+ if (impact_point)
+ *impact_point = impact;
+ }
+ }
+
+ else if (obj_radius < 0 && gdist < 0.1) { // special case for camera (needed for cockpits)
+ Point delta = (g->Location() - light_pos);
+
+ if (delta.length() > g->Radius() / 100) { // not the object that is emitting the light
+ Point impact;
+ obscured = g->CheckRayIntersection(obj_pos, dir, len, impact, false) ? true : false;
+ }
+ }
+ }
+ }
+
+ g_iter.attach(pThis->foreground);
+ while (++g_iter && !obscured) {
+ g = g_iter.value();
+
+ if (g->CastsShadow() && !g->Hidden()) {
+ double gdist = (g->Location() - obj_pos).length();
+ if (gdist > 0.1 && // different than object being obscured
+ g->Radius() > obj_radius && // larger than object being obscured
+ (g->Radius()*400)/gdist > 10) { // projects to a resonable size
+
+ Point delta = (g->Location() - light_pos);
+
+ if (delta.length() > g->Radius() / 100) { // not the object that is emitting the light
+ Point impact;
+ obscured = g->CheckRayIntersection(obj_pos, dir, len, impact, false) ? true : false;
+
+ if (impact_point)
+ *impact_point = impact;
+ }
+ }
+
+ else if (obj_radius < 0 && gdist < 0.1) { // special case for camera (needed for cockpits)
+ Point delta = (g->Location() - light_pos);
+
+ if (delta.length() > g->Radius() / 100) { // not the object that is emitting the light
+ Point impact;
+ obscured = g->CheckRayIntersection(obj_pos, dir, len, impact, false) ? true : false;
+ }
+ }
+ }
+ }
+
+ return obscured;
+}
diff --git a/nGenEx/Scene.h b/nGenEx/Scene.h
new file mode 100644
index 0000000..fdefed5
--- /dev/null
+++ b/nGenEx/Scene.h
@@ -0,0 +1,76 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Scene.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ A 3D Scene, basically a collection of 3D graphic objects
+*/
+
+#ifndef Scene_h
+#define Scene_h
+
+#include "Types.h"
+#include "Color.h"
+#include "Geometry.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class Graphic;
+class Light;
+
+// +--------------------------------------------------------------------+
+
+class Scene
+{
+public:
+ static const char* TYPENAME() { return "Scene"; }
+
+ Scene();
+ virtual ~Scene();
+
+ void AddBackground(Graphic* g);
+ void DelBackground(Graphic* g);
+ void AddForeground(Graphic* g);
+ void DelForeground(Graphic* g);
+ void AddGraphic(Graphic* g);
+ void DelGraphic(Graphic* g);
+ void AddSprite(Graphic* g);
+ void DelSprite(Graphic* g);
+
+ void AddLight(Light* l);
+ void DelLight(Light* l);
+
+ List<Graphic>& Background() { return background; }
+ List<Graphic>& Foreground() { return foreground; }
+ List<Graphic>& Graphics() { return graphics; }
+ List<Graphic>& Sprites() { return sprites; }
+ List<Light>& Lights() { return lights; }
+ Color Ambient() { return ambient; }
+ void SetAmbient(Color a) { ambient = a; }
+
+ virtual void Collect();
+
+ virtual bool IsLightObscured(const Point& obj_pos,
+ const Point& light_pos,
+ double obj_radius,
+ Point* imp_point=0) const;
+
+protected:
+ List<Graphic> background;
+ List<Graphic> foreground;
+ List<Graphic> graphics;
+ List<Graphic> sprites;
+ List<Light> lights;
+ Color ambient;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif Scene_h
diff --git a/nGenEx/Screen.cpp b/nGenEx/Screen.cpp
new file mode 100644
index 0000000..f50f8ec
--- /dev/null
+++ b/nGenEx/Screen.cpp
@@ -0,0 +1,161 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Screen.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ General Screen class - maintains and displays a list of windows
+*/
+
+#include "MemDebug.h"
+#include "Screen.h"
+#include "Bitmap.h"
+#include "Color.h"
+#include "Window.h"
+#include "Mouse.h"
+#include "Pcx.h"
+#include "Video.h"
+
+// +--------------------------------------------------------------------+
+
+Screen::Screen(Video* v)
+ : width(0), height(0), video(v), clear(0), closed(0)
+{
+ if (video) {
+ width = video->Width();
+ height = video->Height();
+ }
+
+ Mouse::Create(this);
+}
+
+Screen::~Screen()
+{
+ Mouse::Close();
+
+ closed = 1;
+ window_list.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Screen::AddWindow(Window* c)
+{
+ if (!c || closed) return false;
+
+ if (c->X() < 0) return false;
+ if (c->Y() < 0) return false;
+ if (c->X() + c->Width() > Width()) return false;
+ if (c->Y() + c->Height() > Height()) return false;
+
+ if (!window_list.contains(c))
+ window_list.append(c);
+
+ return true;
+}
+
+bool
+Screen::DelWindow(Window* c)
+{
+ if (!c || closed) return false;
+
+ return window_list.remove(c) == c;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Screen::ClearAllFrames(bool clear_all)
+{
+ if (clear_all)
+ clear = -1;
+ else
+ clear = 0;
+}
+
+void
+Screen::ClearNextFrames(int num_frames)
+{
+ if (clear >= 0 && clear < num_frames)
+ clear = num_frames;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Screen::SetBackgroundColor(Color c)
+{
+ if (video)
+ return video->SetBackgroundColor(c);
+ else
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Screen::Resize(int w, int h)
+{
+ // scale all root-level windows to new screen size:
+
+ ListIter<Window> iter = window_list;
+ while (++iter) {
+ Window* win = iter.value();
+
+ double w_x = win->GetRect().x / (double) width;
+ double w_y = win->GetRect().y / (double) height;
+ double w_w = win->GetRect().w / (double) width;
+ double w_h = win->GetRect().h / (double) height;
+
+ Rect r;
+
+ r.x = (int) (w_x * w);
+ r.y = (int) (w_y * h);
+ r.w = (int) (w_w * w);
+ r.h = (int) (w_h * h);
+
+ win->MoveTo(r);
+ }
+
+ width = w;
+ height = h;
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Screen::Refresh()
+{
+ if (clear && !video->ClearAll())
+ return false;
+
+ video->StartFrame();
+
+ ListIter<Window> iter = window_list;
+ while (++iter) {
+ Window* win = iter.value();
+
+ if (win->IsShown()) {
+ win->Paint();
+ }
+ }
+
+ Mouse::Paint();
+
+ video->EndFrame();
+
+ if (clear > 0) clear--;
+ return true;
+}
+
+
+
+
diff --git a/nGenEx/Screen.h b/nGenEx/Screen.h
new file mode 100644
index 0000000..39df897
--- /dev/null
+++ b/nGenEx/Screen.h
@@ -0,0 +1,65 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Screen.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ General Screen class - maintains and displays a list of windows
+*/
+
+#ifndef Screen_h
+#define Screen_h
+
+#include "Types.h"
+#include "Color.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class Bitmap;
+class Window;
+struct Rect;
+
+// +--------------------------------------------------------------------+
+
+class Screen
+{
+public:
+ static const char* TYPENAME() { return "Screen"; }
+
+ Screen(Video* v);
+ virtual ~Screen();
+
+ virtual bool SetBackgroundColor(Color c);
+
+ virtual bool Resize(int w, int h);
+ virtual bool Refresh();
+ virtual bool AddWindow(Window* c);
+ virtual bool DelWindow(Window* c);
+
+ int Width() const { return width; }
+ int Height() const { return height; }
+
+ virtual void ClearAllFrames(bool clear_all);
+ virtual void ClearNextFrames(int num_frames);
+
+ virtual Video* GetVideo() const { return video; }
+
+protected:
+ int width;
+ int height;
+ int clear;
+ int closed;
+
+ Video* video;
+
+ List<Window> window_list;
+};
+
+#endif Screen_h
+
diff --git a/nGenEx/ScrollWindow.cpp b/nGenEx/ScrollWindow.cpp
new file mode 100644
index 0000000..effd967
--- /dev/null
+++ b/nGenEx/ScrollWindow.cpp
@@ -0,0 +1,632 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: ScrollWindow.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ ScrollWindow ActiveWindow class
+*/
+
+#include "MemDebug.h"
+#include "ScrollWindow.h"
+#include "Button.h"
+#include "Bitmap.h"
+#include "FormWindow.h"
+#include "Video.h"
+#include "Font.h"
+#include "Keyboard.h"
+#include "Mouse.h"
+
+
+DWORD GetRealTime();
+
+// +--------------------------------------------------------------------+
+
+static int old_cursor;
+
+// +--------------------------------------------------------------------+
+
+ScrollWindow::ScrollWindow(ActiveWindow* p, int ax, int ay, int aw, int ah, DWORD aid, DWORD s, ActiveWindow* paw)
+ : ActiveWindow(p->GetScreen(), ax, ay, aw, ah, aid, s, paw)
+{
+ captured = false;
+ dragging = false;
+ selecting = false;
+ scrolling = SCROLL_NONE;
+
+ leading = 0;
+ scroll_bar = SCROLL_AUTO;
+ dragdrop = false;
+ scroll_count = 0;
+ line_count = 0;
+ page_count = 0;
+ page_size = 1;
+ top_index = 0;
+ line_height = 0;
+ mouse_x = 0;
+ mouse_y = 0;
+
+ smooth_scroll = false;
+ smooth_offset = 0;
+
+ track_length = ah - 2*TRACK_START - THUMB_HEIGHT;
+ thumb_pos = TRACK_START;
+
+ char buf[32];
+ sprintf(buf, "ScrollWindow %d", id);
+ desc = buf;
+}
+
+ScrollWindow::ScrollWindow(Screen* s, int ax, int ay, int aw, int ah, DWORD aid, DWORD s1, ActiveWindow* paw)
+ : ActiveWindow(s, ax, ay, aw, ah, aid, s1, paw)
+{
+ captured = false;
+ dragging = false;
+ selecting = false;
+ scrolling = SCROLL_NONE;
+
+ leading = 0;
+ scroll_bar = SCROLL_AUTO;
+ dragdrop = false;
+ scroll_count = 0;
+ line_count = 0;
+ page_count = 0;
+ page_size = 1;
+ top_index = 0;
+ line_height = 0;
+ mouse_x = 0;
+ mouse_y = 0;
+
+ smooth_scroll = false;
+ smooth_offset = 0;
+
+ track_length = ah - 2*TRACK_START - THUMB_HEIGHT;
+ thumb_pos = TRACK_START;
+
+ char buf[32];
+ sprintf(buf, "ScrollWindow %d", id);
+ desc = buf;
+}
+
+ScrollWindow::~ScrollWindow()
+{ }
+
+// +--------------------------------------------------------------------+
+
+void
+ScrollWindow::MoveTo(const Rect& r)
+{
+ ActiveWindow::MoveTo(r);
+
+ track_length = rect.h - 2*TRACK_START - THUMB_HEIGHT;
+ thumb_pos = TRACK_START;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ScrollWindow::Paint()
+{
+ if (transparent) {
+ DrawTransparent();
+ }
+
+ else {
+ ActiveWindow::Paint();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ScrollWindow::Draw()
+{
+ int x = 0;
+ int y = 0;
+ int w = rect.w;
+ int h = rect.h;
+
+ if (w < 1 || h < 1 || !shown)
+ return;
+
+ ActiveWindow::Draw();
+
+ if (line_height < 1)
+ line_height = GetFont()->Height();
+ page_size = h / (line_height + leading);
+
+ Rect ctrl_rect(x,y,w,h);
+ ctrl_rect.Deflate(BORDER_WIDTH, BORDER_WIDTH);
+
+ if (IsScrollVisible()) {
+ ctrl_rect.w -= SCROLL_TRACK;
+ }
+
+ DrawContent(ctrl_rect);
+ DrawScrollBar();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ScrollWindow::DrawTransparent()
+{
+ int x = 0;
+ int y = 0;
+ int w = rect.w;
+ int h = rect.h;
+
+ if (w < 1 || h < 1 || !shown)
+ return;
+
+ if (line_height < 1)
+ line_height = GetFont()->Height();
+ page_size = h / (line_height + leading);
+
+ Rect ctrl_rect(x,y,w,h);
+ ctrl_rect.Deflate(BORDER_WIDTH, BORDER_WIDTH);
+
+ if (IsScrollVisible()) {
+ ctrl_rect.w -= SCROLL_TRACK;
+ }
+
+ DrawTransparentContent(ctrl_rect);
+ DrawScrollBar();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+ScrollWindow::DrawContent(const Rect& ctrl_rect)
+{
+ // override to do control-specific drawing
+}
+
+void
+ScrollWindow::DrawTransparentContent(const Rect& ctrl_rect)
+{
+ // override (if necessary) to do control-specific drawing
+ DrawContent(ctrl_rect);
+}
+
+void
+ScrollWindow::DrawScrollBar()
+{
+ // draw scroll bar if necessary:
+ if (IsScrollVisible()) {
+ Color save_color = back_color;
+ back_color = ShadeColor(back_color, 1.3);
+
+ // draw scroll track border:
+ DrawLine(rect.w-SCROLL_TRACK, BORDER_WIDTH, rect.w-SCROLL_TRACK, rect.h-BORDER_WIDTH, back_color);
+
+ // draw top button
+ Rect btn_rect(rect.w-SCROLL_WIDTH, BORDER_WIDTH, SCROLL_WIDTH-BORDER_WIDTH, SCROLL_HEIGHT);
+ FillRect(btn_rect, back_color);
+ DrawStyleRect(btn_rect, WIN_RAISED_FRAME);
+
+ // draw bottom button:
+ btn_rect.y = rect.h - (SCROLL_HEIGHT+BORDER_WIDTH);
+ FillRect(btn_rect, back_color);
+ DrawStyleRect(btn_rect, WIN_RAISED_FRAME);
+
+ // draw thumb:
+ btn_rect.y = thumb_pos;
+ btn_rect.h = btn_rect.w;
+ FillRect(btn_rect, back_color);
+ DrawStyleRect(btn_rect, WIN_RAISED_FRAME);
+
+ back_color = save_color;
+ }
+
+ if (scrolling && scroll_count)
+ Scroll(scrolling, scroll_count);
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+ScrollWindow::IsScrollVisible()
+{
+ bool vis = false;
+
+ if (scroll_bar == SCROLL_ALWAYS ||
+ (scroll_bar == SCROLL_AUTO &&
+ line_count > page_size)) {
+ vis = true;
+ }
+
+ return vis;
+}
+
+int ScrollWindow::GetLineHeight()
+{
+ return line_height;
+}
+
+void ScrollWindow::SetLineHeight(int h)
+{
+ if (h >= 0)
+ line_height = h;
+}
+
+int ScrollWindow::GetLeading()
+{
+ return leading;
+}
+
+void ScrollWindow::SetLeading(int nNewValue)
+{
+ if (leading != nNewValue && nNewValue >= 0) {
+ leading = nNewValue;
+ }
+}
+
+int ScrollWindow::GetDragDrop()
+{
+ return dragdrop;
+}
+
+void ScrollWindow::SetDragDrop(int nNewValue)
+{
+ if (dragdrop != nNewValue && (nNewValue == 0 || nNewValue == 1)) {
+ dragdrop = nNewValue;
+ }
+}
+
+int ScrollWindow::GetScrollBarVisible()
+{
+ return scroll_bar;
+}
+
+void ScrollWindow::SetScrollBarVisible(int nNewValue)
+{
+ if (scroll_bar != nNewValue) {
+ scroll_bar = nNewValue;
+ }
+}
+
+bool ScrollWindow::GetSmoothScroll()
+{
+ return smooth_scroll;
+}
+
+void ScrollWindow::SetSmoothScroll(bool bNewValue)
+{
+ if (smooth_scroll != bNewValue) {
+ smooth_scroll = bNewValue;
+ smooth_offset = top_index;
+ }
+}
+
+bool ScrollWindow::CanScroll(int direction, int nlines)
+{
+ return false;
+}
+
+void ScrollWindow::EnsureVisible(int index)
+{
+ if (index < top_index)
+ ScrollTo(index);
+
+ else if (index > top_index+page_size)
+ ScrollTo(index-page_size);
+}
+
+void ScrollWindow::Scroll(int direction, int nlines)
+{
+ if (nlines) {
+ scrolling = direction;
+
+ if (direction == SCROLL_UP || direction == SCROLL_PAGE_UP) {
+ top_index--;
+
+ if (top_index < 0)
+ top_index = 0;
+
+ else
+ scroll_count = nlines-1;
+ }
+
+ else if (direction == SCROLL_DOWN || direction == SCROLL_PAGE_DOWN) {
+ top_index++;
+
+ if (top_index >= line_count)
+ top_index = line_count-1;
+
+ else
+ scroll_count = nlines-1;
+ }
+
+ smooth_offset = top_index;
+ thumb_pos = TRACK_START + (int) (track_length * (double) top_index/(line_count-1));
+
+ if (scroll_count < 1)
+ scrolling = SCROLL_NONE;
+ }
+}
+
+void ScrollWindow::SmoothScroll(int direction, double nlines)
+{
+ if (!smooth_scroll) {
+ Scroll(direction, (int) nlines);
+ return;
+ }
+
+ if (direction == SCROLL_UP || direction == SCROLL_PAGE_UP) {
+ smooth_offset -= nlines;
+
+ if (smooth_offset < 0)
+ smooth_offset = 0;
+ }
+
+ else if (direction == SCROLL_DOWN || direction == SCROLL_PAGE_DOWN) {
+ smooth_offset += nlines;
+
+ if (smooth_offset >= line_count)
+ smooth_offset = line_count-1;
+ }
+
+ top_index = (int) smooth_offset;
+ thumb_pos = TRACK_START + (int) (track_length * smooth_offset/(line_count-1));
+ scrolling = SCROLL_NONE;
+}
+
+void ScrollWindow::ScrollTo(int index)
+{
+ if (index >= 0 && index < line_count) {
+ top_index = index;
+ smooth_offset = index;
+
+ thumb_pos = TRACK_START + (int) (track_length * smooth_offset/(line_count-1));
+ }
+}
+
+int ScrollWindow::GetTopIndex()
+{
+ return top_index;
+}
+
+int ScrollWindow::GetPageCount()
+{
+ return line_count / GetPageSize();
+}
+
+int ScrollWindow::GetPageSize()
+{
+ return page_size;
+}
+
+int ScrollWindow::GetScrollTrack()
+{
+ return rect.w-SCROLL_TRACK;
+}
+
+int ScrollWindow::GetLineCount()
+{
+ return line_count;
+}
+
+// +--------------------------------------------------------------------+
+
+int ScrollWindow::OnMouseMove(int x, int y)
+{
+ bool dirty = false;
+
+ if (captured) {
+ ActiveWindow* test = GetCapture();
+
+ if (test != this) {
+ captured = false;
+ dirty = true;
+ }
+
+ else {
+ if (selecting && !dragging) {
+ if (dragdrop && (x < rect.x ||
+ x > rect.x+rect.w ||
+ y < rect.y ||
+ y > rect.y+rect.h)) {
+
+ dragging = true;
+ OnDragStart(x,y);
+ }
+ }
+
+ if (scrolling == SCROLL_THUMB) {
+ mouse_y = y - rect.y - TRACK_START;
+
+ int dest = (int) ((double) mouse_y/track_length * (line_count-1));
+ ScrollTo(dest);
+ dirty = true;
+ }
+ }
+ }
+
+ return ActiveWindow::OnMouseMove(x,y);
+}
+
+// +--------------------------------------------------------------------+
+
+int ScrollWindow::OnLButtonDown(int x, int y)
+{
+ if (!captured)
+ captured = SetCapture();
+
+ mouse_x = x - rect.x;
+ mouse_y = y - rect.y;
+
+ int x_scroll_bar = rect.w;
+
+ if (scroll_bar == SCROLL_ALWAYS ||
+ (scroll_bar == SCROLL_AUTO &&
+ line_count > page_size))
+ x_scroll_bar -= SCROLL_WIDTH;
+
+ if (mouse_x < x_scroll_bar) {
+ scrolling = SCROLL_NONE;
+ selecting = true;
+ }
+
+ else {
+ selecting = false;
+
+ if (mouse_y < TRACK_START) {
+ scrolling = SCROLL_UP;
+ Scroll(scrolling, 1);
+ Button::PlaySound(Button::SND_LIST_SCROLL);
+ }
+
+ else if (mouse_y > rect.h-TRACK_START) {
+ scrolling = SCROLL_DOWN;
+ if (top_index < line_count-1)
+ top_index++;
+ Button::PlaySound(Button::SND_LIST_SCROLL);
+ }
+
+ else if (mouse_y < thumb_pos) {
+ scrolling = SCROLL_PAGE_UP;
+ Scroll(scrolling, page_size);
+ Button::PlaySound(Button::SND_LIST_SCROLL);
+ }
+
+ else if (mouse_y > thumb_pos+THUMB_HEIGHT) {
+ scrolling = SCROLL_PAGE_DOWN;
+ Scroll(scrolling, page_size);
+ Button::PlaySound(Button::SND_LIST_SCROLL);
+ }
+
+ else {
+ scrolling = SCROLL_THUMB;
+ }
+ }
+
+ return ActiveWindow::OnLButtonDown(x,y);
+}
+
+// +--------------------------------------------------------------------+
+
+int ScrollWindow::OnLButtonUp(int x, int y)
+{
+ if (captured) {
+ mouse_x = x-rect.x;
+ mouse_y = y-rect.y;
+
+ if (dragging) {
+ if (mouse_x < 0 || mouse_x > rect.w || mouse_y < 0 || mouse_y > rect.h) {
+ FormWindow* parent_form = (FormWindow*) form;
+
+ if (parent_form) {
+ ActiveWindow* drop_target = parent_form->FindControl(x,y);
+
+ if (drop_target && drop_target->IsEnabled() && drop_target->IsShown())
+ drop_target->OnDragDrop(x,y,this);
+ }
+ }
+ }
+
+ ReleaseCapture();
+ captured = false;
+
+ Mouse::SetCursor((Mouse::CURSOR) old_cursor);
+ }
+
+ dragging = false;
+ selecting = false;
+
+ return ActiveWindow::OnLButtonUp(x,y);
+}
+
+// +--------------------------------------------------------------------+
+
+int ScrollWindow::OnMouseWheel(int wheel)
+{
+ if (wheel > 0)
+ Scroll(SCROLL_UP, 1);
+
+ else if (wheel < 0)
+ Scroll(SCROLL_DOWN, 1);
+
+ if (GetLineCount() > 0) {
+ static double scroll_time = 0;
+
+ if (GetRealTime() - scroll_time > 0.5) {
+ scroll_time = GetRealTime();
+ Button::PlaySound(Button::SND_LIST_SCROLL);
+ }
+ }
+
+ return ActiveWindow::OnMouseWheel(wheel);
+}
+
+// +--------------------------------------------------------------------+
+
+int ScrollWindow::OnClick()
+{
+ int fire_select = !scrolling;
+
+ if (scrolling == SCROLL_THUMB)
+ scrolling = SCROLL_NONE;
+
+ if (fire_select)
+ return ActiveWindow::OnSelect();
+ else
+ return ActiveWindow::OnClick();
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+int ScrollWindow::OnKeyDown(int vk, int flags)
+{
+ switch (vk) {
+ case VK_UP: Scroll(SCROLL_UP, 1);
+ Button::PlaySound(Button::SND_LIST_SCROLL);
+ break;
+
+ case VK_DOWN: Scroll(SCROLL_DOWN, 1);
+ Button::PlaySound(Button::SND_LIST_SCROLL);
+ break;
+
+ case VK_PRIOR: Scroll(SCROLL_UP, page_count);
+ Button::PlaySound(Button::SND_LIST_SCROLL);
+ break;
+
+ case VK_NEXT: Scroll(SCROLL_DOWN, page_count);
+ Button::PlaySound(Button::SND_LIST_SCROLL);
+ break;
+
+ case VK_HOME: EnsureVisible(0);
+ Button::PlaySound(Button::SND_LIST_SCROLL);
+ break;
+
+ case VK_END: EnsureVisible(line_count-1);
+ Button::PlaySound(Button::SND_LIST_SCROLL);
+ break;
+
+ default: break;
+ }
+
+ return ActiveWindow::OnKeyDown(vk, flags);
+}
+
+// +--------------------------------------------------------------------+
+
+int ScrollWindow::OnDragStart(int x, int y)
+{
+ old_cursor = Mouse::SetCursor(Mouse::DRAG);
+ return ActiveWindow::OnDragStart(x,y);
+}
+
+// +--------------------------------------------------------------------+
+
+int ScrollWindow::OnDragDrop(int x, int y, ActiveWindow* source)
+{
+ return ActiveWindow::OnDragDrop(x,y,source);
+}
diff --git a/nGenEx/ScrollWindow.h b/nGenEx/ScrollWindow.h
new file mode 100644
index 0000000..21d44ea
--- /dev/null
+++ b/nGenEx/ScrollWindow.h
@@ -0,0 +1,132 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: ScrollWindow.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ ScrollWindow base class for List, Edit, and Rich Text controls
+*/
+
+#ifndef ScrollWindow_h
+#define ScrollWindow_h
+
+#include "Types.h"
+#include "ActiveWindow.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class ScrollWindow : public ActiveWindow
+{
+public:
+ enum POLICY { SCROLL_NEVER,
+ SCROLL_AUTO,
+ SCROLL_ALWAYS
+ };
+
+ enum SCROLL { SCROLL_NONE,
+ SCROLL_UP,
+ SCROLL_PAGE_UP,
+ SCROLL_DOWN,
+ SCROLL_PAGE_DOWN,
+ SCROLL_THUMB
+ };
+
+ enum MISC { BORDER_WIDTH = 2,
+ EXTRA_WIDTH = 4,
+ SCROLL_WIDTH = 16,
+ SCROLL_HEIGHT = 6,
+ SCROLL_TRACK = SCROLL_WIDTH + 1,
+ TRACK_START = BORDER_WIDTH + SCROLL_HEIGHT,
+ THUMB_HEIGHT = SCROLL_WIDTH,
+ HEADING_EXTRA = BORDER_WIDTH + EXTRA_WIDTH
+ };
+
+ ScrollWindow(ActiveWindow* p, int ax, int ay, int aw, int ah, DWORD aid, DWORD style=0, ActiveWindow* parent=0);
+ ScrollWindow(Screen* s, int ax, int ay, int aw, int ah, DWORD aid, DWORD style=0, ActiveWindow* parent=0);
+ virtual ~ScrollWindow();
+
+ // Operations:
+ virtual void Paint();
+ virtual void Draw();
+ virtual void DrawTransparent();
+ virtual void DrawContent(const Rect& ctrl_rect);
+ virtual void DrawTransparentContent(const Rect& ctrl_rect);
+ virtual void DrawScrollBar();
+ virtual void MoveTo(const Rect& r);
+
+ // Event Target Interface:
+ virtual int OnMouseMove(int x, int y);
+ virtual int OnLButtonDown(int x, int y);
+ virtual int OnLButtonUp(int x, int y);
+ virtual int OnMouseWheel(int wheel);
+ virtual int OnClick();
+
+ virtual int OnKeyDown(int vk, int flags);
+
+ // pseudo-events:
+ virtual int OnDragStart(int x, int y);
+ virtual int OnDragDrop(int x, int y, ActiveWindow* source);
+
+ // Property accessors:
+ virtual int GetLineHeight();
+ virtual void SetLineHeight(int h);
+
+ virtual int GetLeading();
+ virtual void SetLeading(int nNewValue);
+ virtual int GetScrollBarVisible();
+ virtual void SetScrollBarVisible(int nNewValue);
+ virtual int GetDragDrop();
+ virtual void SetDragDrop(int nNewValue);
+ virtual bool GetSmoothScroll();
+ virtual void SetSmoothScroll(bool s);
+
+ virtual bool IsScrollVisible();
+ virtual bool CanScroll(int direction, int nlines=1);
+ virtual void EnsureVisible(int index);
+ virtual void Scroll(int direction, int nlines=1);
+ virtual void SmoothScroll(int direction, double nlines);
+ virtual void ScrollTo(int index);
+
+ // read-only:
+ virtual int GetTopIndex();
+ virtual int GetLineCount();
+ virtual int GetPageCount();
+ virtual int GetPageSize();
+ virtual int GetScrollTrack();
+
+ int IsDragging() const { return dragging; }
+ int IsSelecting() const { return selecting; }
+ int IsScrolling() const { return scrolling; }
+
+protected:
+ int captured;
+ int dragging;
+ int selecting;
+ int scrolling;
+ int scroll_count;
+ int mouse_x;
+ int mouse_y;
+ int track_length;
+ int thumb_pos;
+
+ int leading;
+ int scroll_bar;
+ int dragdrop;
+ int line_count;
+ int page_count;
+ int page_size;
+ int top_index;
+ int line_height;
+
+ bool smooth_scroll;
+ double smooth_offset;
+};
+
+#endif ScrollWindow_h
+
diff --git a/nGenEx/Sha1.cpp b/nGenEx/Sha1.cpp
new file mode 100644
index 0000000..bc27c56
--- /dev/null
+++ b/nGenEx/Sha1.cpp
@@ -0,0 +1,589 @@
+/*
+ * Sha1.cpp
+ *
+ * Copyright (C) 1998
+ * Paul E. Jones <paulej@acm.org>
+ * All Rights Reserved.
+ *
+ *****************************************************************************
+ * $Id: sha1.cpp,v 1.6 2001/03/20 06:54:54 paulej Exp $
+ *****************************************************************************
+ *
+ * Description:
+ * This class implements the Secure Hashing Standard as defined
+ * in FIPS PUB 180-1 published April 17, 1995.
+ *
+ * The Secure Hashing Standard, which uses the Secure Hashing
+ * Algorithm (SHA), produces a 160-bit message digest for a
+ * given data stream. In theory, it is highly improbable that
+ * two messages will produce the same message digest. Therefore,
+ * this algorithm can serve as a means of providing a "fingerprint"
+ * for a message.
+ *
+ * Portability Issues:
+ * SHA-1 is defined in terms of 32-bit "words". This code was
+ * written with the expectation that the processor has at least
+ * a 32-bit machine word size. If the machine word size is larger,
+ * the code should still function properly. One caveat to that
+ * is that the input functions taking characters and character arrays
+ * assume that only 8 bits of information are stored in each character.
+ *
+ * Caveats:
+ * SHA-1 is designed to work with messages less than 2^64 bits long.
+ * Although SHA-1 allows a message digest to be generated for
+ * messages of any number of bits less than 2^64, this implementation
+ * only works with messages with a length that is a multiple of 8
+ * bits.
+ *
+ */
+
+
+#include "Sha1.h"
+
+/*
+ * SHA1
+ *
+ * Description:
+ * This is the constructor for the sha1 class.
+ *
+ * Parameters:
+ * None.
+ *
+ * Returns:
+ * Nothing.
+ *
+ * Comments:
+ *
+ */
+SHA1::SHA1()
+{
+ Reset();
+}
+
+/*
+ * ~SHA1
+ *
+ * Description:
+ * This is the destructor for the sha1 class
+ *
+ * Parameters:
+ * None.
+ *
+ * Returns:
+ * Nothing.
+ *
+ * Comments:
+ *
+ */
+SHA1::~SHA1()
+{
+ // The destructor does nothing
+}
+
+/*
+ * Reset
+ *
+ * Description:
+ * This function will initialize the sha1 class member variables
+ * in preparation for computing a new message digest.
+ *
+ * Parameters:
+ * None.
+ *
+ * Returns:
+ * Nothing.
+ *
+ * Comments:
+ *
+ */
+void SHA1::Reset()
+{
+ Length_Low = 0;
+ Length_High = 0;
+ Message_Block_Index = 0;
+
+ H[0] = 0x67452301;
+ H[1] = 0xEFCDAB89;
+ H[2] = 0x98BADCFE;
+ H[3] = 0x10325476;
+ H[4] = 0xC3D2E1F0;
+
+ Computed = false;
+ Corrupted = false;
+}
+
+/*
+ * Result
+ *
+ * Description:
+ * This function will return the 160-bit message digest into the
+ * array provided.
+ *
+ * Parameters:
+ * message_digest_array: [out]
+ * This is an array of five unsigned integers which will be filled
+ * with the message digest that has been computed.
+ *
+ * Returns:
+ * True if successful, false if it failed.
+ *
+ * Comments:
+ *
+ */
+bool SHA1::Result(unsigned *message_digest_array)
+{
+ int i; // Counter
+
+ if (Corrupted)
+ {
+ return false;
+ }
+
+ if (!Computed)
+ {
+ PadMessage();
+ Computed = true;
+ }
+
+ for(i = 0; i < 5; i++)
+ {
+ message_digest_array[i] = H[i];
+ }
+
+ return true;
+}
+
+/*
+ * Input
+ *
+ * Description:
+ * This function accepts an array of octets as the next portion of
+ * the message.
+ *
+ * Parameters:
+ * message_array: [in]
+ * An array of characters representing the next portion of the
+ * message.
+ *
+ * Returns:
+ * Nothing.
+ *
+ * Comments:
+ *
+ */
+void SHA1::Input( const unsigned char *message_array,
+ unsigned length)
+{
+ if (!length)
+ {
+ return;
+ }
+
+ if (Computed || Corrupted)
+ {
+ Corrupted = true;
+ return;
+ }
+
+ while(length-- && !Corrupted)
+ {
+ Message_Block[Message_Block_Index++] = (*message_array & 0xFF);
+
+ Length_Low += 8;
+ Length_Low &= 0xFFFFFFFF; // Force it to 32 bits
+ if (Length_Low == 0)
+ {
+ Length_High++;
+ Length_High &= 0xFFFFFFFF; // Force it to 32 bits
+ if (Length_High == 0)
+ {
+ Corrupted = true; // Message is too long
+ }
+ }
+
+ if (Message_Block_Index == 64)
+ {
+ ProcessMessageBlock();
+ }
+
+ message_array++;
+ }
+}
+
+/*
+ * Input
+ *
+ * Description:
+ * This function accepts an array of octets as the next portion of
+ * the message.
+ *
+ * Parameters:
+ * message_array: [in]
+ * An array of characters representing the next portion of the
+ * message.
+ * length: [in]
+ * The length of the message_array
+ *
+ * Returns:
+ * Nothing.
+ *
+ * Comments:
+ *
+ */
+void SHA1::Input( const char *message_array,
+ unsigned length)
+{
+ Input((unsigned char *) message_array, length);
+}
+
+/*
+ * Input
+ *
+ * Description:
+ * This function accepts a single octets as the next message element.
+ *
+ * Parameters:
+ * message_element: [in]
+ * The next octet in the message.
+ *
+ * Returns:
+ * Nothing.
+ *
+ * Comments:
+ *
+ */
+void SHA1::Input(unsigned char message_element)
+{
+ Input(&message_element, 1);
+}
+
+/*
+ * Input
+ *
+ * Description:
+ * This function accepts a single octet as the next message element.
+ *
+ * Parameters:
+ * message_element: [in]
+ * The next octet in the message.
+ *
+ * Returns:
+ * Nothing.
+ *
+ * Comments:
+ *
+ */
+void SHA1::Input(char message_element)
+{
+ Input((unsigned char *) &message_element, 1);
+}
+
+/*
+ * operator<<
+ *
+ * Description:
+ * This operator makes it convenient to provide character strings to
+ * the SHA1 object for processing.
+ *
+ * Parameters:
+ * message_array: [in]
+ * The character array to take as input.
+ *
+ * Returns:
+ * A reference to the SHA1 object.
+ *
+ * Comments:
+ * Each character is assumed to hold 8 bits of information.
+ *
+ */
+SHA1& SHA1::operator<<(const char *message_array)
+{
+ const char *p = message_array;
+
+ while(*p)
+ {
+ Input(*p);
+ p++;
+ }
+
+ return *this;
+}
+
+/*
+ * operator<<
+ *
+ * Description:
+ * This operator makes it convenient to provide character strings to
+ * the SHA1 object for processing.
+ *
+ * Parameters:
+ * message_array: [in]
+ * The character array to take as input.
+ *
+ * Returns:
+ * A reference to the SHA1 object.
+ *
+ * Comments:
+ * Each character is assumed to hold 8 bits of information.
+ *
+ */
+SHA1& SHA1::operator<<(const unsigned char *message_array)
+{
+ const unsigned char *p = message_array;
+
+ while(*p)
+ {
+ Input(*p);
+ p++;
+ }
+
+ return *this;
+}
+
+/*
+ * operator<<
+ *
+ * Description:
+ * This function provides the next octet in the message.
+ *
+ * Parameters:
+ * message_element: [in]
+ * The next octet in the message
+ *
+ * Returns:
+ * A reference to the SHA1 object.
+ *
+ * Comments:
+ * The character is assumed to hold 8 bits of information.
+ *
+ */
+SHA1& SHA1::operator<<(const char message_element)
+{
+ Input((unsigned char *) &message_element, 1);
+
+ return *this;
+}
+
+/*
+ * operator<<
+ *
+ * Description:
+ * This function provides the next octet in the message.
+ *
+ * Parameters:
+ * message_element: [in]
+ * The next octet in the message
+ *
+ * Returns:
+ * A reference to the SHA1 object.
+ *
+ * Comments:
+ * The character is assumed to hold 8 bits of information.
+ *
+ */
+SHA1& SHA1::operator<<(const unsigned char message_element)
+{
+ Input(&message_element, 1);
+
+ return *this;
+}
+
+/*
+ * ProcessMessageBlock
+ *
+ * Description:
+ * This function will process the next 512 bits of the message
+ * stored in the Message_Block array.
+ *
+ * Parameters:
+ * None.
+ *
+ * Returns:
+ * Nothing.
+ *
+ * Comments:
+ * Many of the variable names in this function, especially the single
+ * character names, were used because those were the names used
+ * in the publication.
+ *
+ */
+void SHA1::ProcessMessageBlock()
+{
+ const unsigned K[] = { // Constants defined for SHA-1
+ 0x5A827999,
+ 0x6ED9EBA1,
+ 0x8F1BBCDC,
+ 0xCA62C1D6
+ };
+ int t; // Loop counter
+ unsigned temp; // Temporary word value
+ unsigned W[80]; // Word sequence
+ unsigned A, B, C, D, E; // Word buffers
+
+ /*
+ * Initialize the first 16 words in the array W
+ */
+ for(t = 0; t < 16; t++)
+ {
+ W[t] = Message_Block[t * 4] << 24;
+ W[t] |= Message_Block[t * 4 + 1] << 16;
+ W[t] |= Message_Block[t * 4 + 2] << 8;
+ W[t] |= Message_Block[t * 4 + 3];
+ }
+
+ for(t = 16; t < 80; t++)
+ {
+ W[t] = CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
+ }
+
+ A = H[0];
+ B = H[1];
+ C = H[2];
+ D = H[3];
+ E = H[4];
+
+ for(t = 0; t < 20; t++)
+ {
+ temp = CircularShift(5,A) + ((B & C) | ((~B) & D)) + E + W[t] + K[0];
+ temp &= 0xFFFFFFFF;
+ E = D;
+ D = C;
+ C = CircularShift(30,B);
+ B = A;
+ A = temp;
+ }
+
+ for(t = 20; t < 40; t++)
+ {
+ temp = CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
+ temp &= 0xFFFFFFFF;
+ E = D;
+ D = C;
+ C = CircularShift(30,B);
+ B = A;
+ A = temp;
+ }
+
+ for(t = 40; t < 60; t++)
+ {
+ temp = CircularShift(5,A) +
+ ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
+ temp &= 0xFFFFFFFF;
+ E = D;
+ D = C;
+ C = CircularShift(30,B);
+ B = A;
+ A = temp;
+ }
+
+ for(t = 60; t < 80; t++)
+ {
+ temp = CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
+ temp &= 0xFFFFFFFF;
+ E = D;
+ D = C;
+ C = CircularShift(30,B);
+ B = A;
+ A = temp;
+ }
+
+ H[0] = (H[0] + A) & 0xFFFFFFFF;
+ H[1] = (H[1] + B) & 0xFFFFFFFF;
+ H[2] = (H[2] + C) & 0xFFFFFFFF;
+ H[3] = (H[3] + D) & 0xFFFFFFFF;
+ H[4] = (H[4] + E) & 0xFFFFFFFF;
+
+ Message_Block_Index = 0;
+}
+
+/*
+ * PadMessage
+ *
+ * Description:
+ * According to the standard, the message must be padded to an even
+ * 512 bits. The first padding bit must be a '1'. The last 64 bits
+ * represent the length of the original message. All bits in between
+ * should be 0. This function will pad the message according to those
+ * rules by filling the message_block array accordingly. It will also
+ * call ProcessMessageBlock() appropriately. When it returns, it
+ * can be assumed that the message digest has been computed.
+ *
+ * Parameters:
+ * None.
+ *
+ * Returns:
+ * Nothing.
+ *
+ * Comments:
+ *
+ */
+void SHA1::PadMessage()
+{
+ /*
+ * Check to see if the current message block is too small to hold
+ * the initial padding bits and length. If so, we will pad the
+ * block, process it, and then continue padding into a second block.
+ */
+ if (Message_Block_Index > 55)
+ {
+ Message_Block[Message_Block_Index++] = 0x80;
+ while(Message_Block_Index < 64)
+ {
+ Message_Block[Message_Block_Index++] = 0;
+ }
+
+ ProcessMessageBlock();
+
+ while(Message_Block_Index < 56)
+ {
+ Message_Block[Message_Block_Index++] = 0;
+ }
+ }
+ else
+ {
+ Message_Block[Message_Block_Index++] = 0x80;
+ while(Message_Block_Index < 56)
+ {
+ Message_Block[Message_Block_Index++] = 0;
+ }
+
+ }
+
+ /*
+ * Store the message length as the last 8 octets
+ */
+ Message_Block[56] = (Length_High >> 24) & 0xFF;
+ Message_Block[57] = (Length_High >> 16) & 0xFF;
+ Message_Block[58] = (Length_High >> 8) & 0xFF;
+ Message_Block[59] = (Length_High) & 0xFF;
+ Message_Block[60] = (Length_Low >> 24) & 0xFF;
+ Message_Block[61] = (Length_Low >> 16) & 0xFF;
+ Message_Block[62] = (Length_Low >> 8) & 0xFF;
+ Message_Block[63] = (Length_Low) & 0xFF;
+
+ ProcessMessageBlock();
+}
+
+
+/*
+ * CircularShift
+ *
+ * Description:
+ * This member function will perform a circular shifting operation.
+ *
+ * Parameters:
+ * bits: [in]
+ * The number of bits to shift (1-31)
+ * word: [in]
+ * The value to shift (assumes a 32-bit integer)
+ *
+ * Returns:
+ * The shifted value.
+ *
+ * Comments:
+ *
+ */
+unsigned SHA1::CircularShift(int bits, unsigned word)
+{
+ return ((word << bits) & 0xFFFFFFFF) | ((word & 0xFFFFFFFF) >> (32-bits));
+}
diff --git a/nGenEx/Sha1.h b/nGenEx/Sha1.h
new file mode 100644
index 0000000..531a543
--- /dev/null
+++ b/nGenEx/Sha1.h
@@ -0,0 +1,89 @@
+/*
+ * Sha1.h
+ *
+ * Copyright (C) 1998
+ * Paul E. Jones <paulej@acm.org>
+ * All Rights Reserved.
+ *
+ *****************************************************************************
+ * $Id: sha1.h,v 1.4 2001/03/20 06:25:06 paulej Exp $
+ *****************************************************************************
+ *
+ * Description:
+ * This class implements the Secure Hashing Standard as defined
+ * in FIPS PUB 180-1 published April 17, 1995.
+ *
+ * Many of the variable names in this class, especially the single
+ * character names, were used because those were the names used
+ * in the publication.
+ *
+ * Please read the file sha1.cpp for more information.
+ *
+ */
+
+#ifndef _SHA1_H_
+#define _SHA1_H_
+
+class SHA1
+{
+
+ public:
+
+ SHA1();
+ virtual ~SHA1();
+
+ /*
+ * Re-initialize the class
+ */
+ void Reset();
+
+ /*
+ * Returns the message digest
+ */
+ bool Result(unsigned *message_digest_array);
+
+ /*
+ * Provide input to SHA1
+ */
+ void Input( const unsigned char *message_array,
+ unsigned length);
+ void Input( const char *message_array,
+ unsigned length);
+ void Input(unsigned char message_element);
+ void Input(char message_element);
+ SHA1& operator<<(const char *message_array);
+ SHA1& operator<<(const unsigned char *message_array);
+ SHA1& operator<<(const char message_element);
+ SHA1& operator<<(const unsigned char message_element);
+
+ private:
+
+ /*
+ * Process the next 512 bits of the message
+ */
+ void ProcessMessageBlock();
+
+ /*
+ * Pads the current message block to 512 bits
+ */
+ void PadMessage();
+
+ /*
+ * Performs a circular left shift operation
+ */
+ inline unsigned CircularShift(int bits, unsigned word);
+
+ unsigned H[5]; // Message digest buffers
+
+ unsigned Length_Low; // Message length in bits
+ unsigned Length_High; // Message length in bits
+
+ unsigned char Message_Block[64]; // 512-bit message blocks
+ int Message_Block_Index; // Index into message block array
+
+ bool Computed; // Is the digest computed?
+ bool Corrupted; // Is the message digest corruped?
+
+};
+
+#endif
diff --git a/nGenEx/Shadow.cpp b/nGenEx/Shadow.cpp
new file mode 100644
index 0000000..3038062
--- /dev/null
+++ b/nGenEx/Shadow.cpp
@@ -0,0 +1,176 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Shadow.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Dynamic Stencil Shadow Volumes
+*/
+
+#include "MemDebug.h"
+#include "Shadow.h"
+#include "Light.h"
+#include "Solid.h"
+#include "Scene.h"
+#include "Video.h"
+
+static bool visible_shadow_volumes = false;
+
+// +--------------------------------------------------------------------+
+
+Shadow::Shadow(Solid* s)
+ : verts(0), nverts(0), max_verts(0), edges(0), num_edges(0), enabled(true)
+{
+ solid = s;
+
+ if (solid && solid->GetModel()) {
+ Model* model = solid->GetModel();
+ int npolys = model->NumPolys();
+
+ max_verts = model->NumVerts() * 4;
+ verts = new(__FILE__,__LINE__) Vec3[max_verts];
+ edges = new(__FILE__,__LINE__) WORD[npolys * 6];
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Shadow::~Shadow()
+{
+ if (verts) delete [] verts;
+ if (edges) delete [] edges;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Shadow::Reset()
+{
+ num_edges = 0;
+ nverts = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Shadow::Render(Video* video)
+{
+ if (enabled)
+ video->DrawShadow(solid, nverts, verts, visible_shadow_volumes);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Shadow::Update(Light* light)
+{
+ Reset();
+
+ if (!light || !solid || !solid->GetModel() || !edges) return;
+
+ Vec3 lpos = light->Location();
+ bool directional = light->Type() == Light::LIGHT_DIRECTIONAL;
+ Model* model = solid->GetModel();
+
+ ListIter<Surface> iter = model->GetSurfaces();
+ while (++iter) {
+ Surface* s = iter.value();
+
+ // transform light location into surface object space
+ Matrix xform(solid->Orientation()); // XXX should be: (s->GetOrientation());
+
+ Vec3 tmp = light->Location();
+
+ if (!directional)
+ tmp -= (solid->Location() + s->GetOffset());
+
+ lpos.x = tmp * Vec3(xform(0,0), xform(0,1), xform(0,2));
+ lpos.y = tmp * Vec3(xform(1,0), xform(1,1), xform(1,2));
+ lpos.z = tmp * Vec3(xform(2,0), xform(2,1), xform(2,2));
+
+ // compute the silohuette for the mesh with respect to the light:
+
+ for (int i = 0; i < s->NumPolys(); i++) {
+ Poly* p = s->GetPolys() + i;
+
+ // skip polys with non-shadowing materials:
+ if (p->material && !p->material->shadow)
+ continue;
+
+ // if this poly faces the light:
+ if (p->plane.normal * lpos > 0) {
+ for (int n = 0; n < p->nverts; n++) {
+ if (n < p->nverts-1)
+ AddEdge(p->vlocs[n], p->vlocs[n+1]);
+ else
+ AddEdge(p->vlocs[n], p->vlocs[0]);
+ }
+ }
+ }
+
+ // extrude the silohuette away from the light source
+ // to create the shadow volume:
+
+ Vec3 extent = lpos * -1;
+ extent.Normalize();
+ extent *= 50.0e3f; //solid->Radius() * 2.1f;
+
+ for (i = 0; i < (int) num_edges; i++) {
+ if (nverts+6 <= max_verts) {
+ Vec3 v1 = s->GetVLoc()[edges[2*i+0]];
+ Vec3 v2 = s->GetVLoc()[edges[2*i+1]];
+ Vec3 v3 = v1 + extent;
+ Vec3 v4 = v2 + extent;
+
+ verts[nverts++] = v1;
+ verts[nverts++] = v2;
+ verts[nverts++] = v3;
+
+ verts[nverts++] = v2;
+ verts[nverts++] = v4;
+ verts[nverts++] = v3;
+ }
+ }
+ }
+}
+
+void
+Shadow::AddEdge(WORD v1, WORD v2)
+{
+ // Remove interior edges (which appear in the list twice)
+ for (DWORD i = 0; i < num_edges; i++) {
+ if ((edges[2*i+0] == v1 && edges[2*i+1] == v2) ||
+ (edges[2*i+0] == v2 && edges[2*i+1] == v1))
+ {
+ if (num_edges > 1) {
+ edges[2*i+0] = edges[2*(num_edges-1)+0];
+ edges[2*i+1] = edges[2*(num_edges-1)+1];
+ }
+
+ num_edges--;
+ return;
+ }
+ }
+
+ edges[2*num_edges+0] = v1;
+ edges[2*num_edges+1] = v2;
+
+ num_edges++;
+}
+
+bool
+Shadow::GetVisibleShadowVolumes()
+{
+ return visible_shadow_volumes;
+}
+
+void
+Shadow::SetVisibleShadowVolumes(bool vis)
+{
+ visible_shadow_volumes = vis;
+}
diff --git a/nGenEx/Shadow.h b/nGenEx/Shadow.h
new file mode 100644
index 0000000..a039337
--- /dev/null
+++ b/nGenEx/Shadow.h
@@ -0,0 +1,70 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Shadow.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Dynamic Stencil Shadow Volumes
+*/
+
+#ifndef Shadow_h
+#define Shadow_h
+
+#include "Geometry.h"
+#include "Color.h"
+
+// +--------------------------------------------------------------------+
+
+#define Shadow_DESTROY(x) if (x) { x->Destroy(); x = 0; }
+
+// +--------------------------------------------------------------------+
+
+class Light;
+class Scene;
+class Solid;
+class Video;
+
+// +--------------------------------------------------------------------+
+
+class Shadow
+{
+public:
+ static const char* TYPENAME() { return "Shadow"; }
+
+ Shadow(Solid* solid);
+ virtual ~Shadow();
+
+ int operator == (const Shadow& s) const { return this == &s; }
+
+ // operations
+ void Render(Video* video);
+ void Update(Light* light);
+ void AddEdge(WORD v1, WORD v2);
+ void Reset();
+
+ bool IsEnabled() const { return enabled; }
+ void SetEnabled(bool e) { enabled = e; }
+
+ static void SetVisibleShadowVolumes(bool vis);
+ static bool GetVisibleShadowVolumes();
+
+protected:
+ Solid* solid;
+ Vec3* verts;
+ int nverts;
+ int max_verts;
+ bool enabled;
+
+ WORD* edges;
+ DWORD num_edges;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif Shadow_h
+
diff --git a/nGenEx/Skin.cpp b/nGenEx/Skin.cpp
new file mode 100644
index 0000000..f409501
--- /dev/null
+++ b/nGenEx/Skin.cpp
@@ -0,0 +1,175 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Skin.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Classes for rendering solid meshes of polygons
+*/
+
+#include "MemDebug.h"
+#include "Skin.h"
+#include "Solid.h"
+
+void Print(const char* fmt, ...);
+
+// +--------------------------------------------------------------------+
+
+Skin::Skin(const char* n)
+{
+ if (n && *n) {
+ strncpy(name, n, NAMELEN);
+ name[NAMELEN-1] = 0;
+ }
+
+ else {
+ ZeroMemory(name, NAMELEN);
+ }
+
+ ZeroMemory(path, 256);
+}
+
+// +--------------------------------------------------------------------+
+
+Skin::~Skin()
+{
+ cells.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Skin::SetName(const char* n)
+{
+ if (n && *n) {
+ strncpy(name, n, NAMELEN);
+ name[NAMELEN-1] = 0;
+ }
+}
+
+void
+Skin::SetPath(const char* n)
+{
+ if (n && *n) {
+ strncpy(path, n, 256);
+ path[255] = 0;
+ }
+
+ else {
+ ZeroMemory(path, 256);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Skin::AddMaterial(const Material* mtl)
+{
+ if (!mtl) return;
+
+ bool found = false;
+
+ ListIter<SkinCell> iter = cells;
+ while (++iter && !found) {
+ SkinCell* s = iter.value();
+
+ if (s->skin && !strcmp(s->skin->name, mtl->name)) {
+ s->skin = mtl;
+ found = true;
+ }
+ }
+
+ if (!found) {
+ SkinCell* s = new(__FILE__,__LINE__) SkinCell(mtl);
+ cells.append(s);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Skin::ApplyTo(Model* model) const
+{
+ if (model) {
+ for (int i = 0; i < cells.size(); i++) {
+ SkinCell* s = cells[i];
+
+ if (s->skin) {
+ s->orig = model->ReplaceMaterial(s->skin);
+ }
+ }
+ }
+}
+
+void
+Skin::Restore(Model* model) const
+{
+ if (model) {
+ for (int i = 0; i < cells.size(); i++) {
+ SkinCell* s = cells[i];
+
+ if (s->orig) {
+ model->ReplaceMaterial(s->orig);
+ s->orig = 0;
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+
+SkinCell::SkinCell(const Material* mtl)
+ : skin(mtl), orig(0)
+{
+}
+
+SkinCell::~SkinCell()
+{
+ delete skin;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+SkinCell::operator == (const SkinCell& other) const
+{
+ if (skin == other.skin)
+ return true;
+
+ if (skin && other.skin)
+ return !strcmp(skin->name, other.skin->name);
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+const char*
+SkinCell::Name() const
+{
+ if (skin)
+ return skin->name;
+
+ return "Invalid Skin Cell";
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SkinCell::SetSkin(const Material* mtl)
+{
+ skin = mtl;
+}
+
+void
+SkinCell::SetOrig(const Material* mtl)
+{
+ orig = mtl;
+} \ No newline at end of file
diff --git a/nGenEx/Skin.h b/nGenEx/Skin.h
new file mode 100644
index 0000000..888c76b
--- /dev/null
+++ b/nGenEx/Skin.h
@@ -0,0 +1,92 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Skin.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Classes for managing run-time selectable skins on solid objects
+*/
+
+#ifndef Skin_h
+#define Skin_h
+
+#include "Polygon.h"
+#include "Graphic.h"
+#include "Video.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class Solid;
+class Model;
+class Surface;
+class Segment;
+
+class Skin;
+class SkinCell;
+
+// +--------------------------------------------------------------------+
+
+class Skin
+{
+public:
+ static const char* TYPENAME() { return "Skin"; }
+ enum { NAMELEN=64 };
+
+ Skin(const char* name = 0);
+ virtual ~Skin();
+
+ // operations
+ void ApplyTo(Model* model) const;
+ void Restore(Model* model) const;
+
+ // accessors / mutators
+ const char* Name() const { return name; }
+ const char* Path() const { return path; }
+ int NumCells() const { return cells.size(); }
+
+ void SetName(const char* n);
+ void SetPath(const char* n);
+ void AddMaterial(const Material* mtl);
+
+protected:
+ char name[NAMELEN];
+ char path[256];
+ List<SkinCell> cells;
+};
+
+// +--------------------------------------------------------------------+
+
+class SkinCell
+{
+ friend class Skin;
+
+public:
+ static const char* TYPENAME() { return "SkinCell"; }
+
+ SkinCell(const Material* mtl=0);
+ ~SkinCell();
+
+ int operator == (const SkinCell& other) const;
+
+ const char* Name() const;
+ const Material* Skin() const { return skin; }
+ const Material* Orig() const { return orig; }
+
+ void SetSkin(const Material* mtl);
+ void SetOrig(const Material* mtl);
+
+private:
+ const Material* skin;
+ const Material* orig;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif Skin_h
+
diff --git a/nGenEx/Slider.cpp b/nGenEx/Slider.cpp
new file mode 100644
index 0000000..f39254e
--- /dev/null
+++ b/nGenEx/Slider.cpp
@@ -0,0 +1,557 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Slider.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Slider/Gauge ActiveWindow class
+*/
+
+#include "MemDebug.h"
+#include "Slider.h"
+#include "Video.h"
+#include "Font.h"
+
+// +--------------------------------------------------------------------+
+
+Slider::Slider(ActiveWindow* p, int ax, int ay, int aw, int ah, DWORD aid)
+ : ActiveWindow(p->GetScreen(), ax, ay, aw, ah, aid, 0, p)
+{
+ captured = false;
+ dragging = false;
+
+ border_color = Color::Black;
+ fill_color = Color::DarkBlue;
+
+ active = true;
+ border = true;
+ num_leds = 1;
+ orientation = 0;
+
+ range_min = 0;
+ range_max = 100;
+ step_size = 10;
+ show_thumb = 0;
+ thumb_size = -1;
+
+ nvalues = 1;
+ ZeroMemory(value, sizeof(value));
+
+ marker[0] = -1;
+ marker[1] = -1;
+
+ char buf[32];
+ sprintf(buf, "Slider %d", id);
+ desc = buf;
+}
+
+Slider::Slider(Screen* s, int ax, int ay, int aw, int ah, DWORD aid)
+ : ActiveWindow(s, ax, ay, aw, ah, aid)
+{
+ captured = false;
+ dragging = false;
+
+ border_color = Color::Black;
+ fill_color = Color::DarkBlue;
+
+ active = true;
+ border = true;
+ num_leds = 1;
+ orientation = 0;
+
+ range_min = 0;
+ range_max = 100;
+ step_size = 10;
+ show_thumb = 0;
+ thumb_size = -1;
+
+ nvalues = 1;
+ ZeroMemory(value, sizeof(value));
+
+ marker[0] = -1;
+ marker[1] = -1;
+
+ char buf[32];
+ sprintf(buf, "Slider %d", id);
+ desc = buf;
+}
+
+Slider::~Slider()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Slider::Draw()
+{
+ int x = 0;
+ int y = 0;
+ int w = rect.w;
+ int h = rect.h;
+
+ if (w < 1 || h < 1 || !shown)
+ return;
+
+ Rect ctrl_rect(x,y,w,h);
+
+ // draw the border:
+ if (border) {
+ Color oc = ShadeColor(border_color, 1);
+ DrawRect(0,0,w-1,h-1,oc);
+ DrawRect(1,1,w-2,h-2,oc);
+
+ ctrl_rect.Deflate(2,2);
+ }
+
+ // draw the bevel:
+ FillRect(ctrl_rect, back_color);
+ DrawStyleRect(ctrl_rect, WIN_SUNK_FRAME);
+
+ // HORIZONTAL
+ if (orientation == 0) {
+ // draw the leds:
+ int led_width = ((w - 6) / (num_leds)) - 1;
+ int led_height = ((h - 5) / (nvalues)) - 1;
+
+ if (nvalues < 2) {
+ int fill_width = (int) ((double)(w-4) * FractionalValue());
+ int num_lit = fill_width / (led_width+1);
+
+ Color fc = ShadeColor(fill_color, 1);
+
+ if (num_leds == 1) {
+ FillRect(2,2,2+fill_width,h-3,fc);
+ }
+ else {
+ int x0 = 2;
+
+ for (int i = 0; i < num_lit; i++) {
+ FillRect(x0,2,x0+led_width,h-3,fc);
+ x0 += led_width + 1;
+ }
+ }
+
+ // draw the thumb:
+ if (thumb_size) {
+ if (thumb_size < 0) thumb_size = h;
+
+ thumb_pos = 2+fill_width;
+
+ Rect thumb_rect(thumb_pos-thumb_size/2, 0, thumb_size, h);
+
+ if (thumb_rect.x < 0)
+ thumb_rect.x = 0;
+
+ else if (thumb_rect.x > w-thumb_size)
+ thumb_rect.x = w-thumb_size;
+
+ if (show_thumb) {
+ FillRect(thumb_rect, back_color);
+ DrawStyleRect(thumb_rect, WIN_RAISED_FRAME);
+ }
+ }
+ }
+
+ else {
+ Color fc = ShadeColor(fill_color, 1);
+
+ int y0 = 3;
+
+ for (int i = 0; i < nvalues; i++) {
+ int fill_width = (int) ((double)(w-6) * FractionalValue(i));
+ FillRect(3,y0,3+fill_width,y0+led_height,fc);
+
+ y0 += led_height+1;
+ }
+ }
+
+ // draw the markers:
+ if (marker[0] >= 0) {
+ int m = marker[0];
+ Color c = ShadeColor(base_color, 1.6); // full highlight
+ DrawLine(m-3, 1, m+4, 1, c);
+ c = ShadeColor(base_color, 1.3); // soft highlight
+ DrawLine(m-3, 2, m-3, 4, c);
+ DrawLine(m-2, 4, m-2, 6, c);
+ DrawLine(m-1, 6, m-1, 8, c);
+ DrawLine(m , 8, m , 10, c);
+ c = base_color; // body
+ DrawLine(m-2, 2, m-2, 4, c);
+ DrawLine(m-1, 2, m-1, 6, c);
+ DrawLine(m , 2, m , 8, c);
+ DrawLine(m+1, 2, m+1, 6, c);
+ DrawLine(m+2, 2, m+2, 4, c);
+ c = ShadeColor(base_color, 0.5); // shadow
+ DrawLine(m+1, 6, m+1, 8, c);
+ DrawLine(m+2, 4, m+2, 6, c);
+ DrawLine(m+3, 2, m+3, 4, c);
+ }
+
+ if (marker[1] >= 0) {
+ int m = marker[0];
+ Color c = ShadeColor(base_color, 0.5); // shadow
+ DrawLine(m-3, h-2, m+4, h-2, c);
+ DrawLine(m+1, h-6, m+1, h-8, c);
+ DrawLine(m+2, h-4, m+2, h-6, c);
+ DrawLine(m+3, h-2, m+3, h-4, c);
+ c = ShadeColor(base_color, 1.3); // soft highlight
+ DrawLine(m-3, h-2, m-3, h-4, c);
+ DrawLine(m-2, h-4, m-2, h-6, c);
+ DrawLine(m-1, h-6, m-1, h-8, c);
+ DrawLine(m , h-8, m , h-10, c);
+ c = base_color; // body
+ DrawLine(m-2, h-2, m-2, h-4, c);
+ DrawLine(m-1, h-2, m-1, h-6, c);
+ DrawLine(m , h-2, m , h-8, c);
+ DrawLine(m+1, h-2, m+1, h-6, c);
+ DrawLine(m+2, h-2, m+2, h-4, c);
+ }
+ }
+
+ // VERTICAL
+ else {
+ // draw the leds:
+ int led_width = ((w - 5) / (nvalues)) - 1;
+
+ if (num_leds > 1) {
+ }
+ else {
+ if (nvalues < 2) {
+ int led_width = w - 4;
+ int led_height = (int) ((double)(h-4) * FractionalValue());
+
+ Color fc = ShadeColor(fill_color, 1);
+ FillRect(2, h-2-led_height, 2+led_width, h-2, fc);
+
+ // draw the thumb:
+ if (thumb_size) {
+ if (thumb_size < 0) thumb_size = w;
+
+ thumb_pos = h-2-led_height;
+
+ Rect thumb_rect(0, thumb_pos-(thumb_size/2), w, thumb_size);
+
+ if (thumb_rect.y < 0)
+ thumb_rect.y = 0;
+
+ else if (thumb_rect.y > h-thumb_size)
+ thumb_rect.y = h-thumb_size;
+
+ if (show_thumb) {
+ FillRect(thumb_rect, back_color);
+ DrawStyleRect(thumb_rect, WIN_RAISED_FRAME);
+ }
+ }
+ }
+
+ else {
+ Color fc = ShadeColor(fill_color, 1);
+
+ int x0 = 3;
+
+ for (int i = 0; i < nvalues; i++) {
+ int led_height = (int) ((double)(h-6) * FractionalValue(i));
+ FillRect(x0,h-3-led_height,x0+led_width,h-3,fc);
+
+ x0 += led_width+1;
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool Slider::GetActive()
+{
+ return active;
+}
+
+void Slider::SetActive(bool bNewValue)
+{
+ active = bNewValue;
+}
+
+bool Slider::GetBorder()
+{
+ return border;
+}
+
+void Slider::SetBorder(bool bNewValue)
+{
+ border = bNewValue;
+}
+
+Color Slider::GetBorderColor()
+{
+ return border_color;
+}
+
+void Slider::SetBorderColor(Color newValue)
+{
+ border_color = newValue;
+}
+
+Color Slider::GetFillColor()
+{
+ return fill_color;
+}
+
+void Slider::SetFillColor(Color cNewValue)
+{
+ fill_color = cNewValue;
+}
+
+int Slider::GetNumLeds()
+{
+ return num_leds;
+}
+
+void Slider::SetNumLeds(int nNewValue)
+{
+ if (nNewValue >= 0) {
+ num_leds = nNewValue;
+ }
+}
+
+int Slider::GetOrientation()
+{
+ return orientation;
+}
+
+void Slider::SetOrientation(int nNewValue)
+{
+ if (nNewValue == 0 || nNewValue == 1) {
+ orientation = nNewValue;
+ }
+}
+
+int Slider::GetRangeMin()
+{
+ return range_min;
+}
+
+void Slider::SetRangeMin(int nNewValue)
+{
+ range_min = nNewValue;
+}
+
+int Slider::GetRangeMax()
+{
+ return range_max;
+}
+
+void Slider::SetRangeMax(int nNewValue)
+{
+ range_max = nNewValue;
+}
+
+int Slider::GetStepSize()
+{
+ return step_size;
+}
+
+void Slider::SetStepSize(int nNewValue)
+{
+ if (nNewValue < range_max - range_min)
+ step_size = nNewValue;
+}
+
+bool Slider::GetShowThumb()
+{
+ return show_thumb?true:false;
+}
+
+void Slider::SetShowThumb(bool bNewValue)
+{
+ show_thumb = bNewValue;
+}
+
+int Slider::GetThumbSize()
+{
+ return thumb_size;
+}
+
+void Slider::SetThumbSize(int nNewValue)
+{
+ if (nNewValue < range_max - range_min)
+ thumb_size = nNewValue;
+}
+
+int Slider::NumValues()
+{
+ return nvalues;
+}
+
+int Slider::GetValue(int index)
+{
+ if (index >= 0 && index < nvalues)
+ return value[index];
+
+ return 0;
+}
+
+void Slider::SetValue(int nNewValue, int index)
+{
+ if (index >= 0 && index < MAX_VAL) {
+ value[index] = nNewValue;
+
+ if (index >= nvalues)
+ nvalues = index+1;
+ }
+}
+
+void Slider::SetMarker(int nNewValue, int index)
+{
+ if (index >= 0 && index < 2) {
+ marker[index] = nNewValue;
+ }
+}
+
+double Slider::FractionalValue(int index)
+{
+ if (index >= 0 && index < nvalues)
+ return ((double) (value[index]-range_min)) / ((double) (range_max-range_min));
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void Slider::StepUp(int index)
+{
+ if (index >= 0 && index < nvalues) {
+ value[index] += step_size;
+
+ if (value[index] > range_max)
+ value[index] = range_max;
+ }
+}
+
+void Slider::StepDown(int index)
+{
+ if (index >= 0 && index < nvalues) {
+ value[index] -= step_size;
+
+ if (value[index] < range_min)
+ value[index] = range_min;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+int Slider::OnMouseMove(int x, int y)
+{
+ bool dirty = false;
+
+ if (captured)
+ {
+ ActiveWindow* test = GetCapture();
+
+ if (test != this)
+ {
+ captured = false;
+ dirty = true;
+ }
+
+ else if (dragging)
+ {
+ mouse_x = x - rect.x;
+ if (mouse_x < 0) mouse_x = 0;
+ else if (mouse_x > rect.w) mouse_x = rect.w;
+
+ mouse_y = rect.h - (y - rect.y);
+ if (mouse_y < 0) mouse_y = 0;
+ else if (mouse_y > rect.h) mouse_y = rect.h;
+
+ // HORIZONTAL
+ if (orientation == 0) {
+ SetValue((int) ((double) mouse_x/rect.w * (range_max-range_min) + range_min));
+ }
+
+ // VERTICAL
+ else {
+ SetValue((int) ((double) mouse_y/rect.h * (range_max-range_min) + range_min));
+ }
+
+ dirty = true;
+ }
+ }
+
+ if (dirty)
+ OnClick();
+
+ return ActiveWindow::OnMouseMove(x,y);
+}
+
+int Slider::OnLButtonDown(int x, int y)
+{
+ if (!active)
+ return 0;
+
+ if (!captured)
+ captured = SetCapture();
+
+ mouse_x = x - rect.x;
+ mouse_y = y - rect.y;
+
+ // HORIZONTAL
+ if (orientation == 0) {
+ if (mouse_x < thumb_pos-thumb_size/2) {
+ StepDown();
+ }
+
+ else if (mouse_x > thumb_pos+thumb_size/2) {
+ StepUp();
+ }
+
+ else {
+ dragging = true;
+ }
+ }
+
+ // VERTICAL
+ else {
+ if (mouse_y < thumb_pos-thumb_size/2) {
+ StepUp();
+ }
+
+ else if (mouse_y > thumb_pos+thumb_size/2) {
+ StepDown();
+ }
+
+ else {
+ dragging = true;
+ }
+ }
+
+ if (!dragging)
+ OnClick();
+
+ return ActiveWindow::OnLButtonDown(x,y);
+}
+
+int Slider::OnLButtonUp(int x, int y)
+{
+ if (!active)
+ return 0;
+
+ if (captured) {
+ ReleaseCapture();
+ captured = 0;
+ dragging = false;
+ }
+
+ return ActiveWindow::OnLButtonUp(x,y);
+}
+
+int Slider::OnClick()
+{
+ return ActiveWindow::OnClick();
+}
diff --git a/nGenEx/Slider.h b/nGenEx/Slider.h
new file mode 100644
index 0000000..0adbec3
--- /dev/null
+++ b/nGenEx/Slider.h
@@ -0,0 +1,107 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Slider.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Slider/Gauge ActiveWindow class
+*/
+
+#ifndef Slider_h
+#define Slider_h
+
+#include "Types.h"
+#include "ActiveWindow.h"
+
+// +--------------------------------------------------------------------+
+
+class Slider : public ActiveWindow
+{
+public:
+ static const char* TYPENAME() { return "Slider"; }
+
+ enum { MAX_VAL=8, ORIENT_HORIZONTAL=0, ORIENT_VERTICAL=1 };
+
+ Slider(ActiveWindow* p, int ax, int ay, int aw, int ah, DWORD aid);
+ Slider(Screen* s, int ax, int ay, int aw, int ah, DWORD aid);
+ virtual ~Slider();
+
+ // Operations:
+ virtual void Draw();
+
+ // Event Target Interface:
+ virtual int OnMouseMove(int x, int y);
+ virtual int OnLButtonDown(int x, int y);
+ virtual int OnLButtonUp(int x, int y);
+ virtual int OnClick();
+
+ // Property accessors:
+ bool GetActive();
+ void SetActive(bool bNewValue);
+ Color GetFillColor();
+ void SetFillColor(Color c);
+ bool GetBorder();
+ void SetBorder(bool bNewValue);
+ Color GetBorderColor();
+ void SetBorderColor(Color c);
+
+ int GetNumLeds();
+ void SetNumLeds(int nNewValue);
+ int GetOrientation();
+ void SetOrientation(int nNewValue);
+
+ int GetRangeMin();
+ void SetRangeMin(int nNewValue);
+ int GetRangeMax();
+ void SetRangeMax(int nNewValue);
+ int GetStepSize();
+ void SetStepSize(int nNewValue);
+ int GetThumbSize();
+ void SetThumbSize(int nNewValue);
+ bool GetShowThumb();
+ void SetShowThumb(bool bNewValue);
+
+ int NumValues();
+ int GetValue(int index=0);
+ void SetValue(int nNewValue, int index=0);
+ double FractionalValue(int index=0);
+
+ void SetMarker(int nNewValue, int index=0);
+
+ // Methods:
+ void StepUp(int index=0);
+ void StepDown(int index=0);
+
+protected:
+ int captured;
+ int dragging;
+ int mouse_x;
+ int mouse_y;
+
+ bool active; // true => slider; false => gauge
+ bool border;
+ Color border_color;
+ Color fill_color; // default: dark blue
+
+ int num_leds; // default: 1
+ int orientation; // 0 => horizontal; !0 => vertical
+
+ int range_min;
+ int range_max;
+ int step_size;
+ int show_thumb;
+ int thumb_size;
+ int thumb_pos;
+
+ int nvalues;
+ int value[MAX_VAL];
+ int marker[2];
+};
+
+#endif Slider_h
+
diff --git a/nGenEx/Solid.cpp b/nGenEx/Solid.cpp
new file mode 100644
index 0000000..e60ba16
--- /dev/null
+++ b/nGenEx/Solid.cpp
@@ -0,0 +1,2481 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Solid.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Classes for rendering solid meshes of polygons
+*/
+
+#include "MemDebug.h"
+#include "Solid.h"
+#include "Scene.h"
+#include "Bitmap.h"
+#include "DataLoader.h"
+#include "Light.h"
+#include "Shadow.h"
+#include "Projector.h"
+#include "OPCODE.h"
+
+#ifdef for
+#undef for
+#endif
+
+void Print(const char* fmt, ...);
+
+// +--------------------------------------------------------------------+
+
+static bool use_collision_detection = true;
+
+bool Solid::IsCollisionEnabled() { return use_collision_detection; }
+void Solid::EnableCollision(bool e) { use_collision_detection = e; }
+
+// +--------------------------------------------------------------------+
+
+Opcode::AABBTreeCollider opcode_collider;
+
+class OPCODE_data
+{
+public:
+ OPCODE_data(Surface* s) {
+ bool status = false;
+
+ if (s) {
+ using namespace Opcode;
+ opcode_collider.SetFirstContact(true);
+
+ npolys = s->NumPolys();
+ nverts = s->NumVerts();
+ ntris = s->NumIndices() / 3;
+
+ locs = new(__FILE__,__LINE__) IcePoint[nverts];
+ tris = new(__FILE__,__LINE__) IndexedTriangle[ntris];
+
+ if (locs && tris) {
+ int i, n = 0;
+
+ for (i = 0; i < nverts; i++) {
+ IcePoint* p = locs + i;
+ Vec3* v = s->GetVertexSet()->loc + i;
+
+ p->Set(v->x, v->y, v->z);
+ }
+
+ for (i = 0; i < npolys; i++) {
+ Poly* p = s->GetPolys() + i;
+
+ if (p->nverts == 3) {
+ IndexedTriangle& t = tris[n++];
+
+ t.mVRef[0] = p->verts[0];
+ t.mVRef[1] = p->verts[2];
+ t.mVRef[2] = p->verts[1];
+ }
+ else {
+ IndexedTriangle& t1 = tris[n++];
+ IndexedTriangle& t2 = tris[n++];
+
+ t1.mVRef[0] = p->verts[0];
+ t1.mVRef[1] = p->verts[2];
+ t1.mVRef[2] = p->verts[1];
+
+ t2.mVRef[0] = p->verts[0];
+ t2.mVRef[1] = p->verts[3];
+ t2.mVRef[2] = p->verts[2];
+ }
+ }
+
+ mesh.SetNbVertices(nverts);
+ mesh.SetNbTriangles(ntris);
+ mesh.SetPointers(tris, locs);
+
+ OPCODECREATE creator;
+ creator.mIMesh = &mesh;
+ status = model.Build(creator);
+ }
+ }
+ else {
+ tris = 0;
+ locs = 0;
+ npolys = 0;
+ nverts = 0;
+ ntris = 0;
+ }
+ }
+
+ ~OPCODE_data() {
+ delete [] tris;
+ delete [] locs;
+ }
+
+ Opcode::Model model;
+ Opcode::MeshInterface mesh;
+ IndexedTriangle* tris;
+ IcePoint* locs;
+ int npolys;
+ int nverts;
+ int ntris;
+};
+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+
+Solid::Solid()
+ : model(0), own_model(1),
+ roll(0.0f), pitch(0.0f), yaw(0.0f), intersection_poly(0)
+{
+ shadow = true;
+ sprintf(name, "Solid %d", id);
+}
+
+// +--------------------------------------------------------------------+
+
+Solid::~Solid()
+{
+ if (own_model)
+ delete model;
+
+ shadows.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Solid::Update()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Solid::SetOrientation(const Matrix& o)
+{
+ orientation = o;
+}
+
+void
+Solid::SetLuminous(bool l)
+{
+ luminous = l;
+
+ if (model && luminous) {
+ model->luminous = luminous;
+
+ ListIter<Material> iter = model->GetMaterials();
+
+ while (++iter) {
+ Material* mtl = iter.value();
+
+ mtl->Ka = Color::Black;
+ mtl->Kd = Color::Black;
+ mtl->Ks = Color::Black;
+ mtl->Ke = Color::White;
+
+ if (mtl->tex_diffuse && !mtl->tex_emissive)
+ mtl->tex_emissive = mtl->tex_diffuse;
+ }
+
+ ListIter<Surface> s_iter = model->GetSurfaces();
+ while (++s_iter) {
+ Surface* surface = s_iter.value();
+ VertexSet* vset = surface->GetVertexSet();
+
+ for (int i = 0; i < vset->nverts; i++) {
+ vset->diffuse[i] = Color::White.Value();
+ vset->specular[i] = Color::Black.Value();
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Solid::SetOrientation(const Solid& match)
+{
+ if (!model || infinite)
+ return;
+
+ // copy the orientation matrix from the solid we are matching:
+ orientation = match.Orientation();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Solid::Render(Video* video, DWORD flags)
+{
+ if (flags & RENDER_ADDITIVE)
+ return;
+
+ if (video && model && model->NumPolys()) {
+ DWORD blend_modes = Video::BLEND_SOLID;
+
+ if (flags == RENDER_ALPHA)
+ blend_modes = Video::BLEND_ALPHA | Video::BLEND_ADDITIVE;
+
+ video->DrawSolid(this, blend_modes);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Solid::SelectDetail(Projector* p)
+{
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Solid::ProjectScreenRect(Projector* p)
+{
+ if (model && p) {
+ Point tmp = loc;
+ p->Transform(tmp);
+
+ if (tmp.z > 1) {
+ int l = 2000;
+ int r = -2000;
+ int t = 2000;
+ int b = -2000;
+
+ for (int i = 0; i < 6; i++) {
+ Point extent;
+
+ if (i < 2)
+ extent.x = model->extents[i];
+
+ else if (i < 4)
+ extent.y = model->extents[i];
+
+ else
+ extent.z = model->extents[i];
+
+ extent = extent * orientation + loc;
+
+ p->Transform(extent);
+ p->Project(extent);
+
+ if (extent.x < l) l = (int) extent.x;
+ if (extent.x > r) r = (int) extent.x;
+ if (extent.y < t) t = (int) extent.y;
+ if (extent.y > b) b = (int) extent.y;
+ }
+
+ screen_rect.x = l;
+ screen_rect.y = t;
+ screen_rect.w = r-l;
+ screen_rect.h = b-t;
+ return;
+ }
+ }
+
+ screen_rect.x = 2000;
+ screen_rect.y = 2000;
+ screen_rect.w = 0;
+ screen_rect.h = 0;
+}
+
+// +--------------------------------------------------------------------+
+// Polygon Interference Detection:
+
+int
+Solid::CollidesWith(Graphic& o)
+{
+ Vec3 delta_loc = Location() - o.Location();
+
+ // bounding spheres test:
+ if (delta_loc.length() > Radius() + o.Radius())
+ return 0;
+
+ // possible collision, but no further refinement can be done:
+ if (!o.IsSolid())
+ return 1;
+
+ Solid& s = (Solid&) o;
+
+ // use the OPCODE library to check for polygon interference:
+ if (model && s.model) {
+ using namespace Opcode;
+
+ bool contact = false;
+
+ // first, reverse the orientation matrices for OPCODE:
+ Matrix m1 = orientation;
+ Matrix m2 = s.orientation;
+
+ Matrix4x4 world0;
+ Matrix4x4 world1;
+
+ world0.m[0][0] = (float) m1.elem[0][0];
+ world0.m[0][1] = (float) m1.elem[0][1];
+ world0.m[0][2] = (float) m1.elem[0][2];
+ world0.m[0][3] = 0.0f;
+
+ world0.m[1][0] = (float) m1.elem[1][0];
+ world0.m[1][1] = (float) m1.elem[1][1];
+ world0.m[1][2] = (float) m1.elem[1][2];
+ world0.m[1][3] = 0.0f;
+
+ world0.m[2][0] = (float) m1.elem[2][0];
+ world0.m[2][1] = (float) m1.elem[2][1];
+ world0.m[2][2] = (float) m1.elem[2][2];
+ world0.m[2][3] = 0.0f;
+
+ world0.m[3][0] = (float) Location().x;
+ world0.m[3][1] = (float) Location().y;
+ world0.m[3][2] = (float) Location().z;
+ world0.m[3][3] = 1.0f;
+
+ world1.m[0][0] = (float) m2.elem[0][0];
+ world1.m[0][1] = (float) m2.elem[1][0];
+ world1.m[0][2] = (float) m2.elem[2][0];
+ world1.m[0][3] = 0.0f;
+
+ world1.m[1][0] = (float) m2.elem[0][1];
+ world1.m[1][1] = (float) m2.elem[1][1];
+ world1.m[1][2] = (float) m2.elem[2][1];
+ world1.m[1][3] = 0.0f;
+
+ world1.m[2][0] = (float) m2.elem[0][2];
+ world1.m[2][1] = (float) m2.elem[1][2];
+ world1.m[2][2] = (float) m2.elem[2][2];
+ world1.m[2][3] = 0.0f;
+
+ world1.m[3][0] = (float) s.Location().x;
+ world1.m[3][1] = (float) s.Location().y;
+ world1.m[3][2] = (float) s.Location().z;
+ world1.m[3][3] = 1.0f;
+
+ ListIter<Surface> s1_iter = model->surfaces;
+ while (++s1_iter && !contact) {
+ Surface* s1 = s1_iter.value();
+
+ ListIter<Surface> s2_iter = s.model->surfaces;
+ while (++s2_iter && !contact) {
+ Surface* s2 = s2_iter.value();
+
+ if (s1->opcode && s2->opcode) {
+ BVTCache bvt;
+ bvt.Model0 = &s1->opcode->model;
+ bvt.Model1 = &s2->opcode->model;
+
+ if (opcode_collider.Collide(bvt, &world0, &world1))
+ if (opcode_collider.GetContactStatus() != 0)
+ contact = true;
+ }
+ }
+ }
+
+ return contact;
+ }
+
+
+ return 1;
+}
+
+// +--------------------------------------------------------------------+
+// Find the intersection of the ray (Q + w*len) with the solid.
+// If the ray intersects a polygon of the solid, place the intersection
+// point in ipt, and return 1. Otherwise, return 0.
+
+int
+Solid::CheckRayIntersection(Point Q, Point w, double len, Point& ipt,
+ bool treat_translucent_polys_as_solid)
+{
+ int impact = 0;
+
+ if (!model || model->npolys < 1)
+ return impact;
+
+ // check right angle spherical distance:
+ Point d0 = loc - Q;
+ Point d1 = d0.cross(w);
+ double dlen = d1.length(); // distance of point from line
+
+ if (dlen > radius) // clean miss
+ return 0; // (no impact)
+
+ // possible collision course...
+
+ /**********************************
+
+
+ /--- + leading_edge = Q + w * len
+ / / \
+ delta2 / delta 0
+ / / \
+ / *........x <- solid location
+ / /
+ / / delta1
+ /--- Q * = closest point
+
+
+ ************************************/
+
+ // find the point on the ray that is closest
+ // to the solid's location:
+ Point closest = Q + w * (d0 * w);
+
+ // find the leading edge, and it's distance from the location:
+ Point leading_edge = Q + w*len;
+ Point leading_delta = leading_edge - loc;
+ double leading_dist = leading_delta.length();
+
+ // if the leading edge is not within the bounding sphere,
+ if (leading_dist > radius) {
+ // check to see if the closest point is between the
+ // ray's endpoints:
+ Point delta1 = closest - Q;
+ Point delta2 = leading_edge - Q; // this is w*len
+
+ // if the closest point is not between the leading edge
+ // and the origin, this ray does not intersect:
+ if (delta1 * delta2 < 0 || delta1.length() > len) {
+ return 0;
+ }
+ }
+
+ // probable hit at this point...
+
+ // if not active, that's good enough:
+ if (GetScene() == 0) {
+ ipt = closest;
+ return 1;
+ }
+
+ // transform ray into object space:
+ Matrix xform(Orientation());
+
+ Vec3 tmp = w;
+
+ w.x = tmp * Vec3(xform(0,0), xform(0,1), xform(0,2));
+ w.y = tmp * Vec3(xform(1,0), xform(1,1), xform(1,2));
+ w.z = tmp * Vec3(xform(2,0), xform(2,1), xform(2,2));
+
+ tmp = Q-loc;
+
+ Q.x = tmp * Vec3(xform(0,0), xform(0,1), xform(0,2));
+ Q.y = tmp * Vec3(xform(1,0), xform(1,1), xform(1,2));
+ Q.z = tmp * Vec3(xform(2,0), xform(2,1), xform(2,2));
+
+ double min = len;
+ intersection_poly = 0;
+
+ // check each polygon:
+ ListIter<Surface> iter = model->surfaces;
+ while (++iter) {
+ Surface* s = iter.value();
+ Poly* p = s->GetPolys();
+
+ for (int i = 0; i < s->NumPolys(); i++) {
+ if (!treat_translucent_polys_as_solid && p->material && !p->material->IsSolid()) {
+ p++;
+ continue;
+ }
+
+ Point v = p->plane.normal;
+ double d = p->plane.distance;
+
+ double denom = w*v;
+
+ if (denom < -1.0e-5) {
+ Point P = v * d;
+ double ilen = ((P-Q)*v)/denom;
+
+ if (ilen > 0 && ilen < min) {
+ Point intersect = Q + w * ilen;
+
+ if (p->Contains(intersect)) {
+ intersection_poly = p;
+ ipt = intersect;
+ min = ilen;
+ impact = 1;
+ }
+ }
+ }
+
+ p++;
+ }
+ }
+
+ // xform impact point back into world coordinates:
+
+ if (impact) {
+ ipt = (ipt * Orientation()) + loc;
+ }
+
+ return impact;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Solid::ClearModel()
+{
+ if (own_model && model) {
+ delete model;
+ model = 0;
+ }
+
+ radius = 0.0f;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Solid::UseModel(Model* m)
+{
+ // get rid of the existing model:
+ ClearModel();
+
+ // point to the new model:
+ own_model = 0;
+ model = m;
+ radius = m->radius;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Solid::Load(const char* mag_file, double scale)
+{
+ // get ready to load, delete existing model:
+ ClearModel();
+
+ // loading our own copy, so we own the model:
+ model = new(__FILE__,__LINE__) Model;
+ own_model = 1;
+
+ // now load the model:
+ if (model->Load(mag_file, scale)) {
+ radius = model->radius;
+ strncpy(name, model->name, sizeof(name));
+ return true;
+ }
+
+ // load failed:
+ ClearModel();
+ return false;
+}
+
+bool
+Solid::Load(ModelFile* mod_file, double scale)
+{
+ // get ready to load, delete existing model:
+ ClearModel();
+
+ // loading our own copy, so we own the model:
+ model = new(__FILE__,__LINE__) Model;
+ own_model = 1;
+
+ // now load the model:
+ if (model->Load(mod_file, scale)) {
+ radius = model->radius;
+ return true;
+ }
+
+ // load failed:
+ ClearModel();
+ return false;
+}
+
+bool
+Solid::Rescale(double scale)
+{
+ if (!own_model || !model)
+ return false;
+
+ radius = 0;
+
+ ListIter<Surface> iter = model->GetSurfaces();
+ while (++iter) {
+ Surface* s = iter.value();
+
+ for (int v = 0; v < s->NumVerts(); v++) {
+ s->vertex_set->loc[v] *= (float) scale;
+ s->vloc[v] *= (float) scale;
+
+ float lvi = s->vloc[v].length();
+ if (lvi > radius)
+ radius = lvi;
+ }
+ }
+
+ model->radius = radius;
+
+ InvalidateSurfaceData();
+
+ return true;
+}
+
+void
+Solid::CreateShadows(int nlights)
+{
+ while (shadows.size() < nlights) {
+ shadows.append(new(__FILE__,__LINE__) Shadow(this));
+ }
+}
+
+void
+Solid::UpdateShadows(List<Light>& lights)
+{
+ List<Light> active_lights;
+ ListIter<Light> iter = lights;
+
+ while (++iter) {
+ Light* light = iter.value();
+
+ if (light->IsActive() && light->CastsShadow()) {
+ double distance = Point(Location() - light->Location()).length();
+ double intensity = light->Intensity();
+
+ if (light->Type() == Light::LIGHT_POINT) {
+ if (intensity / distance > 1)
+ active_lights.append(light);
+ }
+
+ else if (light->Type() == Light::LIGHT_DIRECTIONAL) {
+ if (intensity > 0.65)
+ active_lights.insert(light);
+ }
+ }
+ }
+
+ iter.attach(active_lights);
+
+ while (++iter) {
+ Light* light = iter.value();
+ int index = iter.index();
+
+ if (index < shadows.size()) {
+ shadows[index]->Update(light);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Solid::DeletePrivateData()
+{
+ if (model)
+ model->DeletePrivateData();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Solid::InvalidateSurfaceData()
+{
+ if (!model)
+ return;
+
+ bool invalidate = model->IsDynamic();
+
+ ListIter<Surface> iter = model->GetSurfaces();
+ while (++iter) {
+ Surface* s = iter.value();
+ VideoPrivateData* vpd = s->GetVideoPrivateData();
+
+ if (vpd) {
+ if (invalidate) {
+ vpd->Invalidate();
+ }
+ else {
+ delete vpd;
+ s->SetVideoPrivateData(0);
+ }
+ }
+ }
+}
+
+void
+Solid::InvalidateSegmentData()
+{
+ if (!model)
+ return;
+
+ bool invalidate = model->IsDynamic();
+
+ ListIter<Surface> iter = model->GetSurfaces();
+ while (++iter) {
+ Surface* s = iter.value();
+
+ ListIter<Segment> seg_iter = s->GetSegments();
+ while (++seg_iter) {
+ Segment* segment = seg_iter.value();
+ VideoPrivateData* vpd = segment->GetVideoPrivateData();
+
+ if (vpd) {
+ if (invalidate) {
+ vpd->Invalidate();
+ }
+ else {
+ delete vpd;
+ segment->SetVideoPrivateData(0);
+ }
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Solid::IsDynamic() const
+{
+ if (model)
+ return model->IsDynamic();
+
+ return false;
+}
+
+void
+Solid::SetDynamic(bool d)
+{
+ if (model && own_model)
+ model->SetDynamic(d);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Solid::GetAllTextures(List<Bitmap>& textures)
+{
+ if (model)
+ model->GetAllTextures(textures);
+}
+
+void
+Model::GetAllTextures(List<Bitmap>& textures)
+{
+ ListIter<Material> m_iter = materials;
+ while (++m_iter) {
+ Material* m = m_iter.value();
+
+ if (m->tex_diffuse && !textures.contains(m->tex_diffuse))
+ textures.append(m->tex_diffuse);
+
+ if (m->tex_specular && !textures.contains(m->tex_specular))
+ textures.append(m->tex_specular);
+
+ if (m->tex_emissive && !textures.contains(m->tex_emissive))
+ textures.append(m->tex_emissive);
+
+ if (m->tex_bumpmap && !textures.contains(m->tex_bumpmap))
+ textures.append(m->tex_bumpmap);
+
+ if (m->tex_detail && !textures.contains(m->tex_detail))
+ textures.append(m->tex_detail);
+ }
+}
+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+
+Model::Model()
+ : nverts(0), npolys(0), radius(0), luminous(false), dynamic(false)
+{
+ ZeroMemory(name, sizeof(name));
+}
+
+Model::Model(const Model& m)
+ : nverts(0), npolys(0), radius(0), luminous(false), dynamic(false)
+{
+ operator=(m);
+}
+
+// +--------------------------------------------------------------------+
+
+Model::~Model()
+{
+ surfaces.destroy();
+ materials.destroy();
+}
+
+Model&
+Model::operator = (const Model& m)
+{
+ if (this != &m) {
+ surfaces.destroy();
+ materials.destroy();
+
+ CopyMemory(name, m.name, Solid::NAMELEN);
+
+ nverts = m.nverts;
+ npolys = m.npolys;
+ radius = m.radius;
+ luminous = m.luminous;
+ dynamic = m.dynamic;
+
+ Model* pmod = (Model*) &m;
+
+ ListIter<Material> m_iter = pmod->materials;
+ while (++m_iter) {
+ Material* matl1 = m_iter.value();
+ Material* matl2 = new(__FILE__,__LINE__) Material;
+
+ CopyMemory(matl2, matl1, sizeof(Material));
+ matl2->thumbnail = 0;
+
+ materials.append(matl2);
+ }
+
+ ListIter<Surface> s_iter = pmod->surfaces;
+ while (++s_iter) {
+ Surface* surf1 = s_iter.value();
+ Surface* surf2 = new(__FILE__,__LINE__) Surface;
+
+ surf2->Copy(*surf1, this);
+ surfaces.append(surf2);
+ }
+ }
+
+ return *this;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+Model::NumSegments() const
+{
+ int nsegments = 0;
+
+ for (int i = 0; i < surfaces.size(); i++) {
+ const Surface* s = surfaces[i];
+ nsegments += s->NumSegments();
+ }
+
+ return nsegments;
+}
+
+// +--------------------------------------------------------------------+
+
+inline bool Collinear(const double* a, const double* b, const double* c)
+{
+ Point ab(b[0]-a[0], b[1]-a[1], b[2]-a[2]);
+ Point ac(c[0]-a[0], c[1]-a[1], c[2]-a[2]);
+ Point cross = ab.cross(ac);
+ return (cross.length() == 0);
+}
+
+struct HomogenousPlane
+{
+ double distance;
+ double normal_x;
+ double normal_y;
+ double normal_z;
+ double normal_w;
+};
+
+static void LoadPlane(Plane& p, DataLoader* l, BYTE*& fp)
+{
+ HomogenousPlane tmp;
+ l->fread(&tmp, sizeof(HomogenousPlane), 1, fp);
+}
+
+static void LoadFlags(LPDWORD flags, DataLoader* l, BYTE*& fp)
+{
+ DWORD magic_flags;
+ l->fread(&magic_flags, sizeof(DWORD), 1, fp);
+
+ /** OLD MAGIC FLAGS
+ enum { FLAT_SHADED = 1,
+ LUMINOUS = 2,
+ TRANSLUCENT = 4, \\ must swap
+ CHROMAKEY = 8, // these two
+ FOREGROUND = 16, -- not used
+ WIREFRAME = 32, -- not used
+ SPECULAR1 = 64,
+ SPECULAR2 = 128 };
+ ***/
+
+ const DWORD magic_mask = 0x0fc3;
+
+ *flags = magic_flags & magic_mask;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Model::Load(const char* mag_file, double scale)
+{
+ BYTE* block;
+ DataLoader* loader = DataLoader::GetLoader();
+ bool result = false;
+
+ radius = 0.0f;
+ extents[0] = 0.0f;
+ extents[1] = 0.0f;
+ extents[2] = 0.0f;
+ extents[3] = 0.0f;
+ extents[4] = 0.0f;
+ extents[5] = 0.0f;
+
+ if (!loader) {
+ Print("MAG Open Failed: no data loader for file '%s'\n", mag_file);
+ return result;
+ }
+
+ int size = loader->LoadBuffer(mag_file, block);
+ BYTE* fp = block;
+
+ // check MAG file:
+ if (!size) {
+ Print("MAG Open Failed: could not open file '%s'\n", mag_file);
+ return result;
+ }
+
+ strncpy(name, mag_file, 31);
+ name[31] = 0;
+
+ char file_id[5];
+ CopyMemory(file_id, block, 4);
+ file_id[4] = '\0';
+ int version = 1;
+
+ if (!strcmp(file_id, "MAG6")) {
+ version = 6;
+ }
+ else if (!strcmp(file_id, "MAG5")) {
+ version = 5;
+ }
+ else if (!strcmp(file_id, "MAG4")) {
+ version = 4;
+ }
+ else {
+ Print("MAG Open Failed: File '%s' Invalid file type '%s'\n", mag_file, file_id);
+ loader->ReleaseBuffer(block);
+ return result;
+ }
+
+ // get ready to load, delete existing model:
+ surfaces.destroy();
+ materials.destroy();
+ nverts = 0;
+ npolys = 0;
+
+ // now load the model:
+ switch (version) {
+ case 4:
+ case 5:
+ result = LoadMag5(block, size, scale);
+ break;
+
+ case 6:
+ result = LoadMag6(block, size, scale);
+ break;
+
+ default:
+ break;
+ }
+
+ loader->ReleaseBuffer(block);
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Model::Load(ModelFile* mod_file, double scale)
+{
+ if (mod_file) {
+ return mod_file->Load(this, scale);
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+static int mcomp(const void* a, const void* b)
+{
+ Poly* pa = (Poly*) a;
+ Poly* pb = (Poly*) b;
+
+ if (pa->sortval == pb->sortval)
+ return 0;
+
+ if (pa->sortval < pb->sortval)
+ return 1;
+
+ return -1;
+}
+
+bool
+Model::LoadMag5(BYTE* block, int len, double scale)
+{
+ bool result = false;
+
+ DataLoader* loader = DataLoader::GetLoader();
+ BYTE* fp = block + 4;
+ int ntex = 0;
+ int nsurfs = 0;
+
+ loader->fread(&ntex, sizeof(ntex), 1, fp);
+ loader->fread(&nsurfs, sizeof(nsurfs), 1, fp);
+
+ // create a default gray material:
+ Material* mtl = new Material;
+
+ if (mtl) {
+ mtl->Ka = Color::LightGray;
+ mtl->Kd = Color::LightGray;
+ mtl->Ks = ColorValue(0.2f,0.2f,0.2f);
+ mtl->power = 20.0f;
+
+ mtl->ambient_value = 1.0f;
+ mtl->ambient_color = Color::LightGray;
+ mtl->diffuse_value = 1.0f;
+ mtl->diffuse_color = Color::LightGray;
+ mtl->specular_value = 0.2f;
+ mtl->specular_color = Color::White;
+ strcpy(mtl->name, "(default)");
+
+ materials.append(mtl);
+ }
+
+ // read texture list:
+ for (int i = 0; i < ntex; i++) {
+ Material* mtl = new(__FILE__,__LINE__) Material;
+ char tname[32];
+
+ if (mtl) {
+ mtl->Ka = ColorValue(0.5f,0.5f,0.5f);
+ mtl->Kd = ColorValue(1.0f,1.0f,1.0f);
+ mtl->Ks = ColorValue(0.2f,0.2f,0.2f);
+ mtl->power = 20.0f;
+
+ mtl->ambient_value = 1.0f;
+ mtl->ambient_color = Color::Gray;
+ mtl->diffuse_value = 1.0f;
+ mtl->diffuse_color = Color::White;
+ mtl->specular_value = 0.2f;
+ mtl->specular_color = Color::White;
+
+ loader->fread(tname, 32, 1, fp);
+ loader->LoadTexture(tname, mtl->tex_diffuse, Bitmap::BMP_SOLID, true);
+ strcpy(mtl->name, tname);
+
+ char* dot = strrchr(mtl->name, '.');
+ if (dot)
+ *dot = 0;
+
+ char* plus = strrchr(mtl->name, '+');
+ if (plus)
+ *plus = 0;
+
+ materials.append(mtl);
+ }
+ }
+
+
+ loader->fread(&nverts, 4, 1, fp);
+ loader->fread(&npolys, 4, 1, fp);
+
+ // plan on creating four verts per poly:
+ int mag_nverts = nverts;
+ int next_vert = nverts;
+
+ nverts = npolys * 4;
+
+ Surface* s = new(__FILE__,__LINE__) Surface;
+ VertexSet* vset = 0;
+
+ if (s) {
+ strcpy(s->name, "default");
+
+ s->model = this;
+ s->vertex_set = new(__FILE__,__LINE__) VertexSet(nverts);
+ s->vloc = new(__FILE__,__LINE__) Vec3[nverts];
+
+ ZeroMemory(s->vertex_set->loc, nverts * sizeof(Vec3));
+ ZeroMemory(s->vertex_set->diffuse, nverts * sizeof(DWORD));
+ ZeroMemory(s->vertex_set->specular, nverts * sizeof(DWORD));
+ ZeroMemory(s->vertex_set->tu, nverts * sizeof(float));
+ ZeroMemory(s->vertex_set->tv, nverts * sizeof(float));
+ ZeroMemory(s->vertex_set->rw, nverts * sizeof(float));
+ ZeroMemory(s->vloc, nverts * sizeof(Vec3));
+
+ s->npolys = npolys;
+ s->polys = new(__FILE__,__LINE__) Poly[npolys];
+
+ ZeroMemory(s->polys, sizeof(Poly) * npolys);
+ surfaces.append(s);
+
+ vset = s->vertex_set;
+
+ // read vertex set:
+ for (int v = 0; v < mag_nverts; v++) {
+ Vec3 vert, norm;
+ DWORD vstate;
+
+ loader->fread(&vert, sizeof(Vec3), 1, fp);
+ loader->fread(&norm, sizeof(Vec3), 1, fp);
+ loader->fread(&vstate, sizeof(DWORD), 1, fp);
+
+ vert.SwapYZ();
+ vert *= (float) scale;
+
+ vset->loc[v] = vert;
+ vset->nrm[v] = norm;
+
+ double d = vert.length();
+ if (d > radius)
+ radius = (float) d;
+
+ if (vert.x > extents[0]) extents[0] = vert.x;
+ if (vert.x < extents[1]) extents[1] = vert.x;
+ if (vert.y > extents[2]) extents[2] = vert.y;
+ if (vert.y < extents[3]) extents[3] = vert.y;
+ if (vert.z > extents[4]) extents[4] = vert.z;
+ if (vert.z < extents[5]) extents[5] = vert.z;
+ }
+
+ while (v < nverts)
+ vset->nrm[v++] = Vec3(1,0,0);
+
+ // read polys:
+ Vec3 dummy_center;
+ DWORD dummy_flags;
+ DWORD dummy_color;
+ Plane dummy_plane;
+ int texture_num;
+ int poly_nverts;
+ int vert_index_buffer[32];
+ float texture_index_buffer[32];
+
+ for (int n = 0; n < npolys; n++) {
+ Poly& poly = s->polys[n];
+ poly.vertex_set = vset;
+
+ loader->fread(&dummy_flags, sizeof(DWORD), 1, fp);
+ loader->fread(&dummy_center, sizeof(Vec3), 1, fp);
+
+ LoadPlane(dummy_plane, loader, fp);
+
+ loader->fread(&dummy_color, sizeof(DWORD), 1, fp);
+ loader->fread(&texture_num, sizeof(int), 1, fp);
+
+ if (texture_num >= 0 && texture_num < ntex) {
+ int mtl_num = texture_num + 1;
+ poly.material = materials[mtl_num];
+ poly.sortval = texture_num;
+
+ bool flag_translucent = (dummy_flags & 0x04) ? true : false;
+ bool flag_transparent = (dummy_flags & 0x08) ? true : false;
+
+ // luminous
+ if (dummy_flags & 2) {
+ Material* mtl = materials[mtl_num];
+
+ mtl->Ka = ColorValue(0,0,0,0);
+ mtl->Kd = ColorValue(0,0,0,0);
+ mtl->Ks = ColorValue(0,0,0,0);
+ mtl->Ke = ColorValue(1,1,1,1);
+
+ mtl->tex_emissive = mtl->tex_diffuse;
+ }
+
+ // glowing (additive)
+ if (flag_translucent && flag_transparent)
+ materials[mtl_num]->blend = Material::MTL_ADDITIVE;
+
+ // translucent (alpha)
+ else if (flag_translucent)
+ materials[mtl_num]->blend = Material::MTL_TRANSLUCENT;
+
+ // transparent (just use alpha for this)
+ else if (flag_transparent)
+ materials[mtl_num]->blend = Material::MTL_TRANSLUCENT;
+ }
+ else {
+ poly.material = materials.first();
+ poly.sortval = 1000;
+ }
+
+ // hack: store flat shaded flag in unused visible byte
+ poly.visible = (BYTE) (dummy_flags & 1);
+
+ loader->fread(&poly_nverts, sizeof(int), 1, fp);
+ loader->fread(vert_index_buffer, sizeof(int), poly_nverts, fp);
+
+ if (poly_nverts == 3)
+ s->nindices += 3;
+
+ else if (poly_nverts == 4)
+ s->nindices += 6;
+
+ poly.nverts = poly_nverts;
+ for (int vi = 0; vi < poly_nverts; vi++) {
+ int v = vert_index_buffer[vi];
+
+ if (vset->rw[v] > 0) {
+ vset->CopyVertex(next_vert, v);
+ v = next_vert++;
+ }
+
+ vset->rw[v] = 1;
+ poly.verts[vi] = v;
+ }
+
+ loader->fread(texture_index_buffer, sizeof(float), poly_nverts, fp); // tu's
+ for (vi = 0; vi < poly_nverts; vi++) {
+ int v = poly.verts[vi];
+ vset->tu[v] = texture_index_buffer[vi];
+ }
+
+ loader->fread(texture_index_buffer, sizeof(float), poly_nverts, fp); // tv's
+ for (vi = 0; vi < poly_nverts; vi++) {
+ int v = poly.verts[vi];
+ vset->tv[v] = texture_index_buffer[vi];
+ }
+
+ fp += 16;
+ }
+
+ // pass 2 (adjust vertex normals for flat polys):
+ for (n = 0; n < npolys; n++) {
+ Poly& poly = s->polys[n];
+ poly.plane = Plane(vset->loc[poly.verts[0]],
+ vset->loc[poly.verts[2]],
+ vset->loc[poly.verts[1]]);
+
+ // hack: retrieve flat shaded flag from unused visible byte
+ if (poly.visible) {
+ int poly_nverts = poly.nverts;
+
+ for (int vi = 0; vi < poly_nverts; vi++) {
+ int v = poly.verts[vi];
+ vset->nrm[v] = poly.plane.normal;
+ }
+ }
+ }
+
+ // sort the polys by material index:
+ qsort((void*) s->polys, s->npolys, sizeof(Poly), mcomp);
+
+ // then assign them to cohesive segments:
+ Segment* segment = 0;
+
+ for (n = 0; n < npolys; n++) {
+ if (segment && segment->material == s->polys[n].material) {
+ segment->npolys++;
+ }
+ else {
+ segment = 0;
+ }
+
+ if (!segment) {
+ segment = new(__FILE__,__LINE__) Segment;
+
+ segment->npolys = 1;
+ segment->polys = &s->polys[n];
+ segment->material = segment->polys->material;
+ segment->model = this;
+
+ s->segments.append(segment);
+ }
+ }
+
+ s->BuildHull();
+
+ result = nverts && npolys;
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+struct MaterialMag6 {
+ char name[Material::NAMELEN];
+ char shader[Material::NAMELEN];
+ float power; // highlight sharpness (big=shiny)
+ float brilliance; // diffuse power function
+ float bump; // bump level (0=none)
+ DWORD blend; // alpha blend type
+ bool shadow; // material casts shadow
+ bool luminous; // verts have their own lighting
+
+ Color ambient_color;
+ Color diffuse_color;
+ Color specular_color;
+ Color emissive_color;
+
+ float ambient_value;
+ float diffuse_value;
+ float specular_value;
+ float emissive_value;
+
+ BYTE tex_diffuse;
+ BYTE tex_specular;
+ BYTE tex_bumpmap;
+ BYTE tex_emissive;
+};
+
+// +--------------------------------------------------------------------+
+
+bool
+Model::LoadMag6(BYTE* block, int len, double scale)
+{
+ bool result = false;
+
+ DataLoader* loader = DataLoader::GetLoader();
+ BYTE* fp = block + 4;
+ int i = 0;
+ int ntex = 0;
+ int nmtls = 0;
+ int nsurfs = 0;
+ List<Bitmap> textures;
+
+ loader->fread(&ntex, sizeof(ntex), 1, fp); // size of texture block
+ loader->fread(&nmtls, sizeof(nmtls), 1, fp); // number of materials
+ loader->fread(&nsurfs, sizeof(nsurfs), 1, fp); // number of surfaces
+
+ // read texture list:
+ if (ntex) {
+ char* buffer = new(__FILE__,__LINE__) char[ntex];
+ char* p = buffer;
+ Bitmap* bmp = 0;
+
+ loader->fread(buffer, ntex, 1, fp);
+
+ while (p < buffer + ntex) {
+ loader->LoadTexture(p, bmp, Bitmap::BMP_SOLID, true);
+ textures.append(bmp);
+
+ p += strlen(p) + 1;
+ }
+
+ delete [] buffer;
+ }
+
+ for (i = 0; i < nmtls; i++) {
+ MaterialMag6 m6;
+ Material* mtl = new(__FILE__,__LINE__) Material;
+
+ loader->fread(&m6, sizeof(m6), 1, fp);
+
+ if (mtl) {
+ CopyMemory(mtl->name, m6.name, Material::NAMELEN);
+ CopyMemory(mtl->shader, m6.shader, Material::NAMELEN);
+
+ mtl->ambient_value = m6.ambient_value;
+ mtl->ambient_color = m6.ambient_color;
+ mtl->diffuse_value = m6.diffuse_value;
+ mtl->diffuse_color = m6.diffuse_color;
+ mtl->specular_value = m6.specular_value;
+ mtl->specular_color = m6.specular_color;
+ mtl->emissive_value = m6.emissive_value;
+ mtl->emissive_color = m6.emissive_color;
+
+ mtl->Ka = ColorValue(mtl->ambient_color) * mtl->ambient_value;
+ mtl->Kd = ColorValue(mtl->diffuse_color) * mtl->diffuse_value;
+ mtl->Ks = ColorValue(mtl->specular_color) * mtl->specular_value;
+ mtl->Ke = ColorValue(mtl->emissive_color) * mtl->emissive_value;
+
+ mtl->power = m6.power;
+ mtl->brilliance = m6.brilliance;
+ mtl->bump = m6.bump;
+ mtl->blend = m6.blend;
+ mtl->shadow = m6.shadow;
+ mtl->luminous = m6.luminous;
+
+ if (m6.tex_diffuse && m6.tex_diffuse <= textures.size())
+ mtl->tex_diffuse = textures[m6.tex_diffuse - 1];
+
+ if (m6.tex_specular && m6.tex_specular <= textures.size())
+ mtl->tex_specular = textures[m6.tex_specular - 1];
+
+ if (m6.tex_emissive && m6.tex_emissive <= textures.size())
+ mtl->tex_emissive = textures[m6.tex_emissive - 1];
+
+ if (m6.tex_bumpmap && m6.tex_bumpmap <= textures.size())
+ mtl->tex_bumpmap = textures[m6.tex_bumpmap - 1];
+
+ materials.append(mtl);
+ }
+ }
+
+ for (i = 0; i < nsurfs; i++) {
+ int nverts = 0;
+ int npolys = 0;
+ BYTE namelen = 0;
+ char name[128];
+
+ loader->fread(&nverts, 4, 1, fp);
+ loader->fread(&npolys, 4, 1, fp);
+ loader->fread(&namelen, 1, 1, fp);
+ loader->fread(name, 1, namelen, fp);
+
+ Surface* surface = new(__FILE__,__LINE__) Surface;
+ surface->model = this;
+ surface->SetName(name);
+ surface->CreateVerts(nverts);
+ surface->CreatePolys(npolys);
+
+ VertexSet* vset = surface->GetVertexSet();
+ Poly* polys = surface->GetPolys();
+
+ ZeroMemory(polys, sizeof(Poly) * npolys);
+
+ // read vertex set:
+ for (int v = 0; v < nverts; v++) {
+ loader->fread(&vset->loc[v], sizeof(float), 3, fp);
+ loader->fread(&vset->nrm[v], sizeof(float), 3, fp);
+ loader->fread(&vset->tu[v], sizeof(float), 1, fp);
+ loader->fread(&vset->tv[v], sizeof(float), 1, fp);
+
+ vset->loc[v] *= (float) scale;
+
+ Vec3 vert = vset->loc[v];
+
+ double d = vert.length();
+ if (d > radius)
+ radius = (float) d;
+
+ if (vert.x > extents[0]) extents[0] = vert.x;
+ if (vert.x < extents[1]) extents[1] = vert.x;
+ if (vert.y > extents[2]) extents[2] = vert.y;
+ if (vert.y < extents[3]) extents[3] = vert.y;
+ if (vert.z > extents[4]) extents[4] = vert.z;
+ if (vert.z < extents[5]) extents[5] = vert.z;
+ }
+
+ // read polys:
+ for (int n = 0; n < npolys; n++) {
+ Poly& poly = polys[n];
+ BYTE poly_nverts = 0;
+ BYTE material_index = 0;
+ WORD poly_verts[8];
+
+ loader->fread(&poly_nverts, sizeof(BYTE), 1, fp);
+ loader->fread(&material_index, sizeof(BYTE), 1, fp);
+ loader->fread(&poly_verts[0], sizeof(WORD), poly_nverts, fp);
+
+ if (poly_nverts >= 3) {
+ poly.nverts = poly_nverts;
+
+ for (int i = 0; i < poly_nverts; i++) {
+ poly.verts[i] = poly_verts[i];
+ }
+ }
+ else {
+ poly.sortval = 666;
+ }
+
+ if (material_index > 0) {
+ poly.material = materials[material_index-1];
+ poly.sortval = material_index;
+ }
+ else if (materials.size()) {
+ poly.material = materials.first();
+ poly.sortval = 1;
+ }
+ else {
+ poly.sortval = 1000;
+ }
+
+ if (poly.nverts == 3)
+ surface->AddIndices(3);
+
+ else if (poly.nverts == 4)
+ surface->AddIndices(6);
+
+ poly.vertex_set = vset;
+ poly.plane = Plane(vset->loc[poly.verts[0]],
+ vset->loc[poly.verts[2]],
+ vset->loc[poly.verts[1]]);
+ }
+
+ // sort the polys by material index:
+ qsort((void*) polys, npolys, sizeof(Poly), mcomp);
+
+ // then assign them to cohesive segments:
+ Segment* segment = 0;
+
+ for (n = 0; n < npolys; n++) {
+ if (segment && segment->material == polys[n].material) {
+ segment->npolys++;
+ }
+ else {
+ segment = 0;
+ }
+
+ if (!segment) {
+ segment = new(__FILE__,__LINE__) Segment;
+
+ segment->npolys = 1;
+ segment->polys = &polys[n];
+ segment->material = segment->polys->material;
+ segment->model = this;
+
+ surface->GetSegments().append(segment);
+ }
+ }
+
+ surface->BuildHull();
+ surfaces.append(surface);
+
+ this->nverts += nverts;
+ this->npolys += npolys;
+ }
+
+
+ result = nverts && npolys;
+ return result;
+}
+
+void
+Model::AddSurface(Surface* surface)
+{
+ if (surface) {
+ surface->model = this;
+
+ ListIter<Segment> iter = surface->segments;
+ while (++iter) {
+ Segment* segment = iter.value();
+ segment->model = this;
+ }
+
+ surface->BuildHull();
+ surfaces.append(surface);
+
+ nverts += surface->NumVerts();
+ npolys += surface->NumPolys();
+ }
+}
+
+
+// +--------------------------------------------------------------------+
+
+const Material*
+Model::FindMaterial(const char* mtl_name) const
+{
+ if (mtl_name && *mtl_name) {
+ Model* pThis = (Model*) this;
+
+ ListIter<Material> iter = pThis->materials;
+ while (++iter) {
+ Material* mtl = iter.value();
+
+ if (!strcmp(mtl->name, mtl_name))
+ return mtl;
+ }
+ }
+
+ return 0;
+}
+
+const Material*
+Model::ReplaceMaterial(const Material* mtl)
+{
+ const Material* mtl_orig = 0;
+
+ if (mtl) {
+ mtl_orig = FindMaterial(mtl->name);
+
+ if (mtl_orig) {
+ int n = materials.index(mtl_orig);
+ materials[n] = (Material*) mtl;
+
+ ListIter<Surface> surf_iter = surfaces;
+ while (++surf_iter) {
+ Surface* surf = surf_iter.value();
+
+ ListIter<Segment> seg_iter = surf->GetSegments();
+ while (++seg_iter) {
+ Segment* segment = seg_iter.value();
+
+ if (segment->material == mtl_orig)
+ segment->material = (Material*) mtl;
+ }
+ }
+ }
+ }
+
+ return mtl_orig;
+}
+
+// +--------------------------------------------------------------------+
+
+Poly*
+Model::AddPolys(int nsurf, int np, int nv)
+{
+ if (nsurf >= 0 && nsurf < surfaces.size())
+ return surfaces[nsurf]->AddPolys(np, nv);
+
+ ::Print("WARNING: AddPolys(%d,%d,%d) invalid surface\n", nsurf, np, nv);
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Model::ExplodeMesh()
+{
+ ListIter<Surface> iter = surfaces;
+
+ int nv = 0;
+ int np = 0;
+
+ while (++iter) {
+ Surface* s = iter.value();
+ s->ExplodeMesh();
+
+ nv += s->NumVerts();
+ np += s->NumPolys();
+ }
+
+ nverts = nv;
+ npolys = np;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Model::OptimizeMesh()
+{
+ ListIter<Surface> iter = surfaces;
+
+ int nv = 0;
+ int np = 0;
+
+ while (++iter) {
+ Surface* s = iter.value();
+ s->OptimizeMesh();
+
+ nv += s->NumVerts();
+ np += s->NumPolys();
+ }
+
+ nverts = nv;
+ npolys = np;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Model::OptimizeMaterials()
+{
+ for (int i = 0; i < materials.size(); i++) {
+ Material* m1 = materials[i];
+
+ for (int n = i; n < materials.size(); n++) {
+ Material* m2 = materials[n];
+
+ // if they match, merge them:
+ if (*m1 == *m2) {
+ List<Poly> polys;
+ SelectPolys(polys, m2);
+
+ ListIter<Poly> iter = polys;
+ while (++iter) {
+ Poly* p = iter.value();
+ p->material = m1;
+ }
+
+ // and discard the duplicate:
+ materials.remove(m2);
+ delete m2;
+ }
+ }
+ }
+}
+
+void
+Model::ScaleBy(double factor)
+{
+ ListIter<Surface> iter = surfaces;
+
+ while (++iter) {
+ Surface* s = iter.value();
+ s->ScaleBy(factor);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Model::Normalize()
+{
+ ListIter<Surface> iter = surfaces;
+
+ while (++iter) {
+ Surface* s = iter.value();
+ s->Normalize();
+ }
+}
+
+void
+Model::SelectPolys(List<Poly>& polys, Vec3 loc)
+{
+ ListIter<Surface> iter = surfaces;
+
+ while (++iter) {
+ Surface* s = iter.value();
+ s->SelectPolys(polys, loc);
+ }
+}
+
+void
+Model::SelectPolys(List<Poly>& polys, Material* m)
+{
+ ListIter<Surface> iter = surfaces;
+
+ while (++iter) {
+ Surface* s = iter.value();
+ s->SelectPolys(polys, m);
+ }
+}
+
+void
+Model::ComputeTangents()
+{
+ ListIter<Surface> iter = surfaces;
+
+ while (++iter) {
+ Surface* s = iter.value();
+ s->ComputeTangents();
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Model::DeletePrivateData()
+{
+ ListIter<Surface> iter = surfaces;
+ while (++iter) {
+ Surface* s = iter.value();
+ VideoPrivateData* vpd = s->GetVideoPrivateData();
+
+ if (vpd) {
+ delete vpd;
+ s->SetVideoPrivateData(0);
+ }
+
+ ListIter<Segment> seg_iter = s->GetSegments();
+ while (++seg_iter) {
+ Segment* segment = seg_iter.value();
+ VideoPrivateData* vpd = segment->video_data;
+
+ if (vpd) {
+ delete vpd;
+ segment->video_data = 0;
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+
+Surface::Surface()
+ : model(0), vertex_set(0), vloc(0), nhull(0), npolys(0), nindices(0),
+ polys(0), state(0), video_data(0), opcode(0)
+{
+ ZeroMemory(name, sizeof(name));
+}
+
+Surface::~Surface()
+{
+ segments.destroy();
+
+ delete opcode;
+ delete vertex_set;
+ delete [] vloc;
+ delete [] polys;
+ delete video_data;
+
+ model = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Surface::Copy(Surface& s, Model* m)
+{
+ segments.destroy();
+
+ delete opcode;
+ delete vertex_set;
+ delete [] vloc;
+ delete [] polys;
+ delete video_data;
+
+ CopyMemory(name, s.name, Solid::NAMELEN);
+
+ model = m;
+ radius = s.radius;
+ nhull = s.nhull;
+ npolys = s.npolys;
+ nindices = s.nindices;
+ state = s.state;
+ offset = s.offset;
+ orientation = s.orientation;
+ opcode = 0;
+ video_data = 0;
+
+ vertex_set = s.vertex_set->Clone();
+
+ if (nhull > 0) {
+ vloc = new(__FILE__,__LINE__) Vec3[nhull];
+ CopyMemory(vloc, s.vloc, nhull * sizeof(Vec3));
+ }
+ else {
+ vloc = 0;
+ }
+
+ polys = new(__FILE__,__LINE__) Poly[npolys];
+ CopyMemory(polys, s.polys, npolys * sizeof(Poly));
+
+ for (int i = 0; i < npolys; i++) {
+ polys[i].vertex_set = vertex_set;
+
+ if (s.polys[i].material)
+ polys[i].material = (Material*) model->FindMaterial(s.polys[i].material->name);
+ }
+
+ ListIter<Segment> iter = s.segments;
+ while (++iter) {
+ Segment* seg1 = iter.value();
+ Segment* seg2 = new(__FILE__,__LINE__) Segment;
+
+ seg2->npolys = seg1->npolys;
+ seg2->polys = polys + (seg1->polys - s.polys);
+
+ if (seg2->polys[0].material)
+ seg2->material = seg2->polys[0].material;
+
+ seg2->model = model;
+ seg2->video_data = 0;
+
+ segments.append(seg2);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Surface::SetName(const char* n)
+{
+ int len = sizeof(name);
+
+ ZeroMemory(name, len);
+ strncpy(name, n, len-1);
+}
+
+void
+Surface::SetHidden(bool b)
+{
+ if (b)
+ state = state | HIDDEN;
+
+ else
+ state = state & ~HIDDEN;
+}
+
+void
+Surface::SetLocked(bool b)
+{
+ if (b)
+ state = state | LOCKED;
+
+ else
+ state = state & ~LOCKED;
+}
+
+void
+Surface::SetSimplified(bool b)
+{
+ if (b)
+ state = state | SIMPLE;
+
+ else
+ state = state & ~SIMPLE;
+}
+
+void
+Surface::CreateVerts(int nverts)
+{
+ if (!vertex_set && !vloc) {
+ vertex_set = new(__FILE__,__LINE__) VertexSet(nverts);
+ vloc = new(__FILE__,__LINE__) Vec3[nverts];
+ }
+}
+
+void
+Surface::CreatePolys(int np)
+{
+ if (!polys && !npolys) {
+ npolys = np;
+ polys = new(__FILE__,__LINE__) Poly[npolys];
+
+ ZeroMemory(polys, npolys * sizeof(Poly));
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+Poly*
+Surface::AddPolys(int np, int nv)
+{
+ if ( polys && vertex_set &&
+ np > 0 && np + npolys < MAX_POLYS &&
+ nv > 0 && nv + vertex_set->nverts < MAX_VERTS)
+ {
+ int newverts = nv + vertex_set->nverts;
+ int newpolys = np + npolys;
+
+ vertex_set->Resize(newverts, true);
+
+ Poly* pset = new(__FILE__,__LINE__) Poly[newpolys];
+ Poly* pnew = pset + npolys;
+
+ CopyMemory(pset, polys, npolys * sizeof(Poly));
+ ZeroMemory(pnew, np * sizeof(Poly));
+
+ if (segments.size() > 0) {
+ Segment* seg = segments.last();
+ Material* mtl = seg->material;
+
+ for (int i = 0; i < np; i++) {
+ Poly* p = pnew + i;
+ p->material = mtl;
+ }
+
+ seg->npolys += np;
+ }
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Surface::ExplodeMesh()
+{
+ if (!vertex_set || vertex_set->nverts < 3)
+ return;
+
+ int i, j, v;
+ int nverts = 0;
+
+ // count max verts:
+ for (i = 0; i < npolys; i++) {
+ Poly* p = polys + i;
+ nverts += p->nverts;
+ }
+
+ // create target vertex set:
+ VertexSet* vset = new(__FILE__,__LINE__) VertexSet(nverts);
+ v = 0;
+
+ // explode verts:
+ for (i = 0; i < npolys; i++) {
+ Poly* p = polys + i;
+ p->vertex_set = vset;
+
+ for (j = 0; j < p->nverts; j++) {
+ int vsrc = p->verts[j];
+
+ vset->loc[v] = vertex_set->loc[vsrc];
+ vset->nrm[v] = vertex_set->nrm[vsrc];
+ vset->tu[v] = vertex_set->tu[vsrc];
+ vset->tv[v] = vertex_set->tv[vsrc];
+ vset->rw[v] = vertex_set->rw[vsrc];
+ vset->diffuse[v] = vertex_set->diffuse[vsrc];
+ vset->specular[v] = vertex_set->specular[vsrc];
+
+ p->verts[j] = v++;
+ }
+ }
+
+ // finalize:
+ if (vset) {
+ delete vertex_set;
+ vertex_set = vset;
+ }
+
+ if (vloc)
+ delete [] vloc;
+
+ vloc = new(__FILE__,__LINE__) Vec3[nverts];
+
+ ComputeTangents();
+ BuildHull();
+}
+
+// +--------------------------------------------------------------------+
+
+const double SELECT_EPSILON = 0.05;
+const double SELECT_TEXTURE = 0.0001;
+
+static bool MatchVerts(VertexSet* vset, int i, int j)
+{
+ double d = 0;
+ const Vec3& vl1 = vset->loc[i];
+ const Vec3& vn1 = vset->nrm[i];
+ float tu1 = vset->tu[i];
+ float tv1 = vset->tv[i];
+ const Vec3& vl2 = vset->loc[j];
+ const Vec3& vn2 = vset->nrm[j];
+ float tu2 = vset->tu[j];
+ float tv2 = vset->tv[j];
+
+ d = fabs(vl1.x - vl2.x);
+ if (d > SELECT_EPSILON)
+ return false;
+
+ d = fabs(vl1.y - vl2.y);
+ if (d > SELECT_EPSILON)
+ return false;
+
+ d = fabs(vl1.z - vl2.z);
+ if (d > SELECT_EPSILON)
+ return false;
+
+ d = fabs(vn1.x - vn2.x);
+ if (d > SELECT_EPSILON)
+ return false;
+
+ d = fabs(vn1.y - vn2.y);
+ if (d > SELECT_EPSILON)
+ return false;
+
+ d = fabs(vn1.z - vn2.z);
+ if (d > SELECT_EPSILON)
+ return false;
+
+ d = fabs(tu1 - tu2);
+ if (d > SELECT_TEXTURE)
+ return false;
+
+ d = fabs(tv1 - tv2);
+ if (d > SELECT_TEXTURE)
+ return false;
+
+ return true;
+}
+
+void
+Surface::OptimizeMesh()
+{
+ if (!vertex_set || vertex_set->nverts < 3)
+ return;
+
+ int i, j, n, v;
+ int nverts = vertex_set->nverts;
+ int used = 0;
+ int final = 0;
+ int nmatch = 0;
+
+ // create vertex maps:
+ BYTE* vert_map = new BYTE[nverts];
+ WORD* vert_dst = new WORD[nverts];
+ ZeroMemory(vert_map, nverts * sizeof(BYTE));
+ ZeroMemory(vert_dst, nverts * sizeof(WORD));
+
+ // count used verts:
+ for (i = 0; i < npolys; i++) {
+ Poly* p = polys + i;
+
+ for (j = 0; j < p->nverts; j++) {
+ WORD vert = p->verts[j];
+
+ if (vert < nverts) {
+ vert_map[vert]++;
+ used++;
+ }
+ }
+ }
+
+ // create target vertex set:
+ VertexSet* vset = new(__FILE__,__LINE__) VertexSet(used);
+ v = 0;
+
+ // compress verts:
+ for (i = 0; i < nverts; i++) {
+ if (vert_map[i] == 0) continue;
+
+ vert_dst[i] = v;
+ vset->loc[v] = vertex_set->loc[i];
+ vset->nrm[v] = vertex_set->nrm[i];
+ vset->tu[v] = vertex_set->tu[i];
+ vset->tv[v] = vertex_set->tv[i];
+ vset->rw[v] = vertex_set->rw[i];
+ vset->diffuse[v] = vertex_set->diffuse[i];
+ vset->specular[v] = vertex_set->specular[i];
+
+ for (int j = i+1; j < nverts; j++) {
+ if (vert_map[j] == 0) continue;
+
+ if (MatchVerts(vertex_set, i, j)) {
+ vert_map[j] = 0;
+ vert_dst[j] = v;
+ nmatch++;
+ }
+ }
+
+ v++;
+ }
+
+ final = v;
+
+ // remap polys:
+ for (n = 0; n < npolys; n++) {
+ Poly* p = polys + n;
+ p->vertex_set = vset;
+ for (v = 0; v < p->nverts; v++) {
+ p->verts[v] = vert_dst[ p->verts[v] ];
+ }
+ }
+
+ // finalize:
+ if (vset && final < nverts) {
+ delete vertex_set;
+ vertex_set = vset;
+ vset->Resize(final, true);
+ nverts = final;
+ }
+
+ // clean up and rebuild hull:
+ delete [] vert_map;
+
+ if (vloc)
+ delete [] vloc;
+
+ vloc = new(__FILE__,__LINE__) Vec3[nverts];
+
+ ComputeTangents();
+ BuildHull();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Surface::ScaleBy(double factor)
+{
+ offset *= factor;
+
+ if (vertex_set && vertex_set->nverts) {
+ for (int i = 0; i < vertex_set->nverts; i++) {
+ vertex_set->loc[i] *= (float) factor;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Surface::BuildHull()
+{
+ if (npolys < 1 || !vertex_set || vertex_set->nverts < 1)
+ return;
+
+ nhull = 0;
+
+ for (int i = 0; i < npolys; i++) {
+ Poly* p = polys + i;
+
+ for (int n = 0; n < p->nverts; n++) {
+ WORD v = p->verts[n];
+ WORD h;
+
+ for (h = 0; h < nhull; h++) {
+ Vec3& vl = vertex_set->loc[v];
+ Vec3& loc = vloc[h];
+
+ double d = vl.x - loc.x;
+
+ if (d < -SELECT_EPSILON || d > SELECT_EPSILON)
+ continue;
+
+ d = vl.y - loc.y;
+
+ if (d < -SELECT_EPSILON || d > SELECT_EPSILON)
+ continue;
+
+ d = vl.z - loc.z;
+
+ if (d < -SELECT_EPSILON || d > SELECT_EPSILON)
+ continue;
+
+ // found a match:
+ break;
+ }
+
+ // didn't find a match:
+ if (h >= nhull) {
+ vloc[h] = vertex_set->loc[v];
+ nhull = h+1;
+ }
+
+ p->vlocs[n] = h;
+ }
+ }
+
+ if (use_collision_detection)
+ InitializeCollisionHull();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Surface::Normalize()
+{
+ if (npolys < 1 || !vertex_set || vertex_set->nverts < 1)
+ return;
+
+ // STEP ONE: initialize poly planes
+
+ for (int i = 0; i < npolys; i++) {
+ Poly* p = polys + i;
+
+ p->plane = Plane( vertex_set->loc[ p->verts[0] ],
+ vertex_set->loc[ p->verts[2] ],
+ vertex_set->loc[ p->verts[1] ] );
+ }
+
+ // STEP TWO: compute vertex normals by averaging adjecent poly planes
+
+ List<Poly> faces;
+ for (int v = 0; v < vertex_set->nverts; v++) {
+ faces.clear();
+ SelectPolys(faces, vertex_set->loc[v]);
+
+ if (faces.size()) {
+ vertex_set->nrm[v] = Vec3(0.0f, 0.0f, 0.0f);
+
+ for (i = 0; i < faces.size(); i++) {
+ vertex_set->nrm[v] += faces[i]->plane.normal;
+ }
+
+ vertex_set->nrm[v].Normalize();
+ }
+
+ else if (vertex_set->loc[v].length() > 0) {
+ vertex_set->nrm[v] = vertex_set->loc[v];
+ vertex_set->nrm[v].Normalize();
+ }
+
+ else {
+ vertex_set->nrm[v] = Vec3(0.0f, 1.0f, 0.0f);
+ }
+ }
+
+ // STEP THREE: adjust vertex normals for poly flatness
+
+ for (i = 0; i < npolys; i++) {
+ Poly* p = polys + i;
+
+ for (int n = 0; n < p->nverts; n++) {
+ int v = p->verts[n];
+
+ vertex_set->nrm[v] = vertex_set->nrm[v] * (1.0f - p->flatness) +
+ p->plane.normal * ( p->flatness);
+ }
+ }
+}
+
+void
+Surface::SelectPolys(List<Poly>& selection, Vec3 loc)
+{
+ const double SELECT_EPSILON = 0.05;
+
+ for (int i = 0; i < npolys; i++) {
+ Poly* p = polys + i;
+
+ for (int n = 0; n < p->nverts; n++) {
+ int v = p->verts[n];
+ Vec3& vl = vertex_set->loc[v];
+ double d = vl.x - loc.x;
+
+ if (d < -SELECT_EPSILON || d > SELECT_EPSILON)
+ continue;
+
+ d = vl.y - loc.y;
+
+ if (d < -SELECT_EPSILON || d > SELECT_EPSILON)
+ continue;
+
+ d = vl.z - loc.z;
+
+ if (d < -SELECT_EPSILON || d > SELECT_EPSILON)
+ continue;
+
+ selection.append(p);
+ break;
+ }
+ }
+}
+
+void
+Surface::SelectPolys(List<Poly>& selection, Material* m)
+{
+ for (int i = 0; i < npolys; i++) {
+ Poly* p = polys + i;
+
+ if (p->material == m)
+ selection.append(p);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Surface::ComputeTangents()
+{
+ Vec3 tangent;
+ Vec3 binormal;
+
+ if (!vertex_set || !vertex_set->nverts)
+ return;
+
+ if (vertex_set->tangent)
+ return;
+
+ vertex_set->CreateTangents();
+
+ for (int i = 0; i < npolys; i++) {
+ Poly* p = polys + i;
+
+ CalcGradients(*p, tangent, binormal);
+
+ for (int n = 0; n < p->nverts; n++) {
+ vertex_set->tangent[p->verts[n]] = tangent;
+ vertex_set->binormal[p->verts[n]] = binormal;
+ }
+ }
+}
+
+void
+Surface::CalcGradients(Poly& p, Vec3& tangent, Vec3& binormal)
+{
+ // using Eric Lengyel's approach with a few modifications
+ // from Mathematics for 3D Game Programmming and Computer Graphics
+ // want to be able to trasform a vector in Object Space to Tangent Space
+ // such that the x-axis cooresponds to the 's' direction and the
+ // y-axis corresponds to the 't' direction, and the z-axis corresponds
+ // to <0,0,1>, straight up out of the texture map
+
+ VertexSet* vset = p.vertex_set;
+
+ Vec3 P = vset->loc[p.verts[1]] - vset->loc[p.verts[0]];
+ Vec3 Q = vset->loc[p.verts[2]] - vset->loc[p.verts[0]];
+
+ float s1 = vset->tu[p.verts[1]] - vset->tu[p.verts[0]];
+ float t1 = vset->tv[p.verts[1]] - vset->tv[p.verts[0]];
+ float s2 = vset->tu[p.verts[2]] - vset->tu[p.verts[0]];
+ float t2 = vset->tv[p.verts[2]] - vset->tv[p.verts[0]];
+
+ float tmp = 1.0f;
+ float denom = s1*t2 - s2*t1;
+
+ if (fabsf(denom) > 0.0001f)
+ tmp = 1.0f/(denom);
+
+ tangent.x = (t2*P.x - t1*Q.x) * tmp;
+ tangent.y = (t2*P.y - t1*Q.y) * tmp;
+ tangent.z = (t2*P.z - t1*Q.z) * tmp;
+
+ tangent.Normalize();
+
+ binormal.x = (s1*Q.x - s2*P.x) * tmp;
+ binormal.y = (s1*Q.y - s2*P.y) * tmp;
+ binormal.z = (s1*Q.z - s2*P.z) * tmp;
+
+ binormal.Normalize();
+}
+
+void
+Surface::InitializeCollisionHull()
+{
+ opcode = new(__FILE__,__LINE__) OPCODE_data(this);
+}
+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+
+Segment::Segment()
+{
+ ZeroMemory(this, sizeof(Segment));
+}
+
+Segment::Segment(int n, Poly* p, Material* mtl, Model* mod)
+ : npolys(n), polys(p), material(mtl), model(mod), video_data(0)
+{
+}
+
+Segment::~Segment()
+{
+ delete video_data;
+
+ ZeroMemory(this, sizeof(Segment));
+}
+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+
+ModelFile::ModelFile(const char* fname)
+ : model(0), pname(0), pnverts(0), pnpolys(0), pradius(0)
+{
+ int len = sizeof(filename);
+ ZeroMemory(filename, len);
+ strncpy(filename, fname, len);
+ filename[len-1] = 0;
+}
+
+ModelFile::~ModelFile()
+{
+}
+
+bool
+ModelFile::Load(Model* m, double scale)
+{
+ model = m;
+
+ // expose model innards for child classes:
+
+ if (model) {
+ pname = model->name;
+ pnverts = &model->nverts;
+ pnpolys = &model->npolys;
+ pradius = &model->radius;
+ }
+
+ return false;
+}
+
+bool
+ModelFile::Save(Model* m)
+{
+ model = m;
+
+ // expose model innards for child classes:
+
+ if (model) {
+ pname = model->name;
+ pnverts = &model->nverts;
+ pnpolys = &model->npolys;
+ pradius = &model->radius;
+ }
+
+ return false;
+}
+
diff --git a/nGenEx/Solid.h b/nGenEx/Solid.h
new file mode 100644
index 0000000..99a49f7
--- /dev/null
+++ b/nGenEx/Solid.h
@@ -0,0 +1,313 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Solid.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Classes for rendering solid meshes of polygons
+*/
+
+#ifndef Solid_h
+#define Solid_h
+
+#include "Polygon.h"
+#include "Graphic.h"
+#include "Video.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class Solid;
+class Model;
+class ModelFile;
+class Surface;
+class Segment;
+class Shadow;
+class Light;
+
+class OPCODE_data; // for collision detection
+
+// +--------------------------------------------------------------------+
+
+class Solid : public Graphic
+{
+public:
+ static const char* TYPENAME() { return "Solid"; }
+
+ enum { NAMELEN = 64 };
+
+ static bool IsCollisionEnabled();
+ static void EnableCollision(bool enable);
+
+ Solid();
+ virtual ~Solid();
+
+ // operations
+ virtual void Render(Video* video, DWORD flags);
+ virtual void SelectDetail(Projector* p);
+ virtual void ProjectScreenRect(Projector* p);
+ virtual void Update();
+
+ // accessors / mutators
+ Model* GetModel() const { return model; }
+ void GetAllTextures(List<Bitmap>& textures);
+
+ virtual bool IsDynamic() const;
+ virtual void SetDynamic(bool d);
+ virtual void SetLuminous(bool l);
+ virtual void SetOrientation(const Matrix& o);
+ virtual void SetOrientation(const Solid& match);
+ const Matrix& Orientation() const { return orientation; }
+ float Roll() const { return roll; }
+ float Pitch() const { return pitch; }
+ float Yaw() const { return yaw; }
+ virtual bool IsSolid() const { return true; }
+
+ // stencil shadows
+ virtual void CreateShadows(int nlights=1);
+ virtual void UpdateShadows(List<Light>& lights);
+ List<Shadow>& GetShadows() { return shadows; }
+
+ bool Load(const char* mag_file, double scale=1.0);
+ bool Load(ModelFile* loader, double scale=1.0);
+ void UseModel(Model* model);
+ void ClearModel();
+ bool Rescale(double scale);
+
+ // collision detection
+ virtual int CollidesWith(Graphic& o);
+ virtual int CheckRayIntersection(Point pt, Point vpn, double len, Point& ipt,
+ bool treat_translucent_polys_as_solid=true);
+ virtual Poly* GetIntersectionPoly() const { return intersection_poly; }
+
+ // buffer management
+ virtual void DeletePrivateData();
+ virtual void InvalidateSurfaceData();
+ virtual void InvalidateSegmentData();
+
+protected:
+ Model* model;
+ bool own_model;
+
+ float roll, pitch, yaw;
+ Matrix orientation;
+ Poly* intersection_poly;
+
+ List<Shadow> shadows;
+};
+
+// +--------------------------------------------------------------------+
+
+class Model
+{
+ friend class Solid;
+ friend class ModelFile;
+
+public:
+ static const char* TYPENAME() { return "Model"; }
+
+ enum { MAX_VERTS = 64000, MAX_POLYS = 16000 };
+
+ Model();
+ Model(const Model& m);
+ ~Model();
+
+ Model& operator = (const Model& m);
+ int operator == (const Model& that) const { return this == &that; }
+
+ bool Load(const char* mag_file, double scale=1.0);
+ bool Load(ModelFile* loader, double scale=1.0);
+
+ const char* Name() const { return name; }
+ int NumVerts() const { return nverts; }
+ int NumSurfaces() const { return surfaces.size(); }
+ int NumMaterials() const { return materials.size(); }
+ int NumPolys() const { return npolys; }
+ int NumSegments() const;
+ double Radius() const { return radius; }
+ bool IsDynamic() const { return dynamic; }
+ void SetDynamic(bool d) { dynamic = d; }
+ bool IsLuminous() const { return luminous; }
+ void SetLuminous(bool l) { luminous = l; }
+
+ List<Surface>& GetSurfaces() { return surfaces; }
+ List<Material>& GetMaterials() { return materials; }
+ const Material* FindMaterial(const char* mtl_name) const;
+ const Material* ReplaceMaterial(const Material* mtl);
+ void GetAllTextures(List<Bitmap>& textures);
+
+ Poly* AddPolys(int nsurf, int npolys, int nverts);
+ void ExplodeMesh();
+ void OptimizeMesh();
+ void OptimizeMaterials();
+ void ScaleBy(double factor);
+
+ void Normalize();
+ void SelectPolys(List<Poly>&, Material* mtl);
+ void SelectPolys(List<Poly>&, Vec3 loc);
+
+ void AddSurface(Surface* s);
+ void ComputeTangents();
+
+ // buffer management
+ void DeletePrivateData();
+
+private:
+ bool LoadMag5(BYTE* block, int len, double scale);
+ bool LoadMag6(BYTE* block, int len, double scale);
+
+ char name[Solid::NAMELEN];
+ List<Surface> surfaces;
+ List<Material> materials;
+ int nverts;
+ int npolys;
+ float radius;
+ float extents[6];
+ bool luminous;
+ bool dynamic;
+};
+
+// +--------------------------------------------------------------------+
+
+class Surface
+{
+ friend class Solid;
+ friend class Model;
+
+public:
+ static const char* TYPENAME() { return "Surface"; }
+
+ enum { HIDDEN=1, LOCKED=2, SIMPLE=4, MAX_VERTS=64000, MAX_POLYS=16000 };
+
+ Surface();
+ ~Surface();
+
+ int operator == (const Surface& s) const { return this == &s; }
+
+ const char* Name() const { return name; }
+ int NumVerts() const { return vertex_set ? vertex_set->nverts : 0; }
+ int NumSegments() const { return segments.size(); }
+ int NumPolys() const { return npolys; }
+ int NumIndices() const { return nindices; }
+ bool IsHidden() const { return state & HIDDEN ? true : false; }
+ bool IsLocked() const { return state & LOCKED ? true : false; }
+ bool IsSimplified() const { return state & SIMPLE ? true : false; }
+
+ Model* GetModel() const { return model; }
+ List<Segment>& GetSegments() { return segments; }
+ const Point& GetOffset() const { return offset; }
+ const Matrix& GetOrientation() const { return orientation; }
+ double Radius() const { return radius; }
+ VertexSet* GetVertexSet() const { return vertex_set; }
+ Vec3* GetVLoc() const { return vloc; }
+ Poly* GetPolys() const { return polys; }
+
+ void SetName(const char* n);
+ void SetHidden(bool b);
+ void SetLocked(bool b);
+ void SetSimplified(bool b);
+
+ void CreateVerts(int nverts);
+ void CreatePolys(int npolys);
+ void AddIndices(int n) { nindices += n; }
+ Poly* AddPolys(int npolys, int nverts);
+
+ VideoPrivateData* GetVideoPrivateData() const { return video_data; }
+ void SetVideoPrivateData(VideoPrivateData* vpd)
+ { video_data = vpd; }
+
+ void ScaleBy(double factor);
+
+ void BuildHull();
+ void Normalize();
+ void SelectPolys(List<Poly>&, Material* mtl);
+ void SelectPolys(List<Poly>&, Vec3 loc);
+
+ void InitializeCollisionHull();
+ void ComputeTangents();
+ void CalcGradients(Poly& p, Vec3& tangent, Vec3& binormal);
+
+ void Copy(Surface& s, Model* m);
+ void OptimizeMesh();
+ void ExplodeMesh();
+
+private:
+ char name[Solid::NAMELEN];
+ Model* model;
+ VertexSet* vertex_set; // for rendering
+ Vec3* vloc; // for shadow hull
+ float radius;
+ int nhull;
+ int npolys;
+ int nindices;
+ int state;
+ Poly* polys;
+ List<Segment> segments;
+
+ Point offset;
+ Matrix orientation;
+
+public:
+ OPCODE_data* opcode;
+
+private:
+ VideoPrivateData* video_data;
+};
+
+// +--------------------------------------------------------------------+
+
+class Segment
+{
+public:
+ static const char* TYPENAME() { return "Segment"; }
+
+ Segment();
+ Segment(int n, Poly* p, Material* mtl, Model* mod=0);
+ ~Segment();
+
+ bool IsSolid() const { return material ? material->IsSolid() : true; }
+ bool IsTranslucent() const { return material ? material->IsTranslucent(): false; }
+ bool IsGlowing() const { return material ? material->IsGlowing() : false; }
+
+ VideoPrivateData* GetVideoPrivateData() const { return video_data; }
+ void SetVideoPrivateData(VideoPrivateData* vpd)
+ { video_data = vpd; }
+
+ int npolys;
+ Poly* polys;
+ Material* material;
+ Model* model;
+ VideoPrivateData* video_data;
+};
+
+// +--------------------------------------------------------------------+
+
+class ModelFile
+{
+public:
+ ModelFile(const char* fname);
+ virtual ~ModelFile();
+
+ virtual bool Load(Model* m, double scale=1.0);
+ virtual bool Save(Model* m);
+
+protected:
+ char filename[256];
+ Model* model;
+
+ // internal accessors:
+ char* pname;
+ int* pnverts;
+ int* pnpolys;
+ float* pradius;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif Solid_h
+
diff --git a/nGenEx/Sound.cpp b/nGenEx/Sound.cpp
new file mode 100644
index 0000000..433ebd9
--- /dev/null
+++ b/nGenEx/Sound.cpp
@@ -0,0 +1,284 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Sound.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Abstract sound class
+*/
+
+#include "MemDebug.h"
+#include "Sound.h"
+#include "SoundCard.h"
+#include "Wave.h"
+
+#include <vorbis/codec.h>
+#include <vorbis/vorbisfile.h>
+
+// +--------------------------------------------------------------------+
+
+SoundCard* Sound::creator = 0;
+
+Sound*
+Sound::CreateStream(const char* filename)
+{
+ Sound* sound = 0;
+
+ if (!filename || !filename[0] || !creator)
+ return sound;
+
+ int namelen = strlen(filename);
+
+ if (namelen < 5)
+ return sound;
+
+ if ((filename[namelen-3] == 'o' || filename[namelen-3] == 'O') &&
+ (filename[namelen-2] == 'g' || filename[namelen-2] == 'G') &&
+ (filename[namelen-1] == 'g' || filename[namelen-1] == 'G')) {
+
+ return CreateOggStream(filename);
+ }
+
+ WAVE_HEADER head;
+ WAVE_FMT fmt;
+ WAVE_FACT fact;
+ WAVE_DATA data;
+ WAVEFORMATEX wfex;
+
+ ZeroMemory(&head, sizeof(head));
+ ZeroMemory(&fmt, sizeof(fmt));
+ ZeroMemory(&fact, sizeof(fact));
+ ZeroMemory(&data, sizeof(data));
+
+ LPBYTE buf = 0;
+ LPBYTE p = 0;
+ int len = 0;
+
+ FILE* f = ::fopen(filename, "rb");
+
+ if (f) {
+ fseek(f, 0, SEEK_END);
+ len = ftell(f);
+ fseek(f, 0, SEEK_SET);
+
+ if (len > 4096) {
+ len = 4096;
+ }
+
+ buf = new(__FILE__,__LINE__) BYTE[len];
+
+ if (buf && len)
+ fread(buf, len, 1, f);
+
+ fclose(f);
+ }
+
+
+ if (len > sizeof(head)) {
+ CopyMemory(&head, buf, sizeof(head));
+
+ if (head.RIFF == MAKEFOURCC('R', 'I', 'F', 'F') &&
+ head.WAVE == MAKEFOURCC('W', 'A', 'V', 'E')) {
+
+ p = buf + sizeof(WAVE_HEADER);
+
+ do {
+ DWORD chunk_id = *((LPDWORD) p);
+
+ switch (chunk_id) {
+ case MAKEFOURCC('f', 'm', 't', ' '):
+ CopyMemory(&fmt, p, sizeof(fmt));
+ p += fmt.chunk_size + 8;
+ break;
+
+ case MAKEFOURCC('f', 'a', 'c', 't'):
+ CopyMemory(&fact, p, sizeof(fact));
+ p += fact.chunk_size + 8;
+ break;
+
+ case MAKEFOURCC('s', 'm', 'p', 'l'):
+ CopyMemory(&fact, p, sizeof(fact));
+ p += fact.chunk_size + 8;
+ break;
+
+ case MAKEFOURCC('d', 'a', 't', 'a'):
+ CopyMemory(&data, p, sizeof(data));
+ p += 8;
+ break;
+
+ default:
+ delete buf;
+ return sound;
+ }
+ }
+ while (data.chunk_size == 0);
+
+ wfex.wFormatTag = fmt.wFormatTag;
+ wfex.nChannels = fmt.nChannels;
+ wfex.nSamplesPerSec = fmt.nSamplesPerSec;
+ wfex.nAvgBytesPerSec = fmt.nAvgBytesPerSec;
+ wfex.nBlockAlign = fmt.nBlockAlign;
+ wfex.wBitsPerSample = fmt.wBitsPerSample;
+ wfex.cbSize = 0;
+
+ sound = Create(Sound::STREAMED, &wfex);
+
+ if (sound) {
+ sound->SetFilename(filename);
+ sound->StreamFile(filename, p - buf);
+ }
+ }
+ }
+
+ delete buf;
+ return sound;
+}
+
+// +--------------------------------------------------------------------+
+
+Sound*
+Sound::CreateOggStream(const char* filename)
+{
+ Sound* sound = 0;
+
+ if (!filename || !filename[0] || !creator)
+ return sound;
+
+ int namelen = strlen(filename);
+
+ if (namelen < 5)
+ return sound;
+
+ WAVEFORMATEX wfex;
+ ZeroMemory(&wfex, sizeof(wfex));
+
+ FILE* f = ::fopen(filename, "rb");
+
+ if (f) {
+ OggVorbis_File* povf = new(__FILE__,__LINE__) OggVorbis_File;
+
+ if (!povf) {
+ Print("Sound::CreateOggStream(%s) - out of memory!\n", filename);
+ return sound;
+ }
+
+ ZeroMemory(povf, sizeof(OggVorbis_File));
+
+ if (ov_open(f, povf, NULL, 0) < 0) {
+ Print("Sound::CreateOggStream(%s) - not an Ogg bitstream\n", filename);
+ delete povf;
+ return sound;
+ }
+
+ Print("\nOpened Ogg Bitstream '%s'\n", filename);
+ char **ptr=ov_comment(povf,-1)->user_comments;
+ vorbis_info *vi=ov_info(povf,-1);
+ while(*ptr){
+ Print("%s\n", *ptr);
+ ++ptr;
+ }
+
+ Print("Bitstream is %d channel, %ldHz\n", vi->channels, vi->rate);
+ Print("Decoded length: %ld samples\n",
+ (long)ov_pcm_total(povf,-1));
+ Print("Encoded by: %s\n\n", ov_comment(povf,-1)->vendor);
+
+ wfex.wFormatTag = WAVE_FORMAT_PCM;
+ wfex.nChannels = vi->channels;
+ wfex.nSamplesPerSec = vi->rate;
+ wfex.nAvgBytesPerSec = vi->channels * vi->rate * 2;
+ wfex.nBlockAlign = vi->channels * 2;
+ wfex.wBitsPerSample = 16;
+ wfex.cbSize = 0;
+
+ sound = Create(Sound::STREAMED | Sound::OGGVORBIS,
+ &wfex,
+ sizeof(OggVorbis_File),
+ (LPBYTE) povf);
+
+ sound->SetFilename(filename);
+ }
+
+ return sound;
+}
+
+// +--------------------------------------------------------------------+
+
+Sound*
+Sound::Create(DWORD flags, LPWAVEFORMATEX format)
+{
+ if (creator) return creator->CreateSound(flags, format);
+ else return 0;
+}
+
+Sound*
+Sound::Create(DWORD flags, LPWAVEFORMATEX format, DWORD len, LPBYTE data)
+{
+ if (creator) return creator->CreateSound(flags, format, len, data);
+ else return 0;
+}
+
+void
+Sound::SetListener(const Camera& cam, const Vec3& vel)
+{
+ if (creator)
+ creator->SetListener(cam, vel);
+}
+
+// +--------------------------------------------------------------------+
+
+Sound::Sound()
+ : status(UNINITIALIZED), volume(0), flags(0), looped(0),
+ velocity(0,0,0), location(0,0,0), sound_check(0)
+{
+ strcpy(filename, "Sound()");
+}
+
+// +--------------------------------------------------------------------+
+
+Sound::~Sound()
+{ }
+
+// +--------------------------------------------------------------------+
+
+void
+Sound::Release()
+{
+ flags &= ~LOCKED;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Sound::AddToSoundCard()
+{
+ if (creator)
+ creator->AddSound(this);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Sound::SetFilename(const char* s)
+{
+ if (s) {
+ int n = strlen(s);
+
+ if (n >= 60) {
+ ZeroMemory(filename, sizeof(filename));
+ strcpy(filename, "...");
+ strcat(filename, s + n - 59);
+ filename[63] = 0;
+ }
+
+ else {
+ strcpy(filename, s);
+ }
+ }
+}
+
diff --git a/nGenEx/Sound.h b/nGenEx/Sound.h
new file mode 100644
index 0000000..acd03ad
--- /dev/null
+++ b/nGenEx/Sound.h
@@ -0,0 +1,150 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Sound.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Abstract Sound Object
+*/
+
+#ifndef Sound_h
+#define Sound_h
+
+#include "Types.h"
+#include "Geometry.h"
+
+// +--------------------------------------------------------------------+
+
+class SoundCard;
+class SoundCheck;
+class Camera;
+
+// +--------------------------------------------------------------------+
+
+class Sound
+{
+public:
+ static const char* TYPENAME() { return "Sound"; }
+
+ static Sound* CreateStream(const char* filename);
+ static Sound* CreateOggStream(const char* filename);
+ static Sound* Create(DWORD flags, LPWAVEFORMATEX format);
+ static Sound* Create(DWORD flags, LPWAVEFORMATEX format, DWORD len, LPBYTE data);
+ static void SetListener(const Camera& cam, const Vec3& vel);
+ static void UseSoundCard(SoundCard* s) { creator = s; }
+
+public:
+ Sound();
+ virtual ~Sound();
+
+ int operator==(const Sound& rhs) const { return this == &rhs; }
+
+ enum FlagEnum { AMBIENT = 0x0000,
+ LOCALIZED = 0x0001,
+ LOC_3D = 0x0002,
+ MEMORY = 0x0000,
+ STREAMED = 0x0004,
+ ONCE = 0x0000,
+ LOOP = 0x0008,
+ FREE = 0x0000,
+ LOCKED = 0x0010,
+ DOPPLER = 0x0020,
+ INTERFACE = 0x0040,
+ OGGVORBIS = 0x4000,
+ RESOURCE = 0x8000 // not playable, only used to store data
+ };
+
+ enum StatusEnum { UNINITIALIZED,
+ INITIALIZING,
+ READY,
+ PLAYING,
+ DONE };
+
+ // once per frame:
+ virtual void Update() { }
+
+ // mark for collection:
+ virtual void Release();
+
+ // data loading:
+ // this method is for streamed sounds:
+ virtual HRESULT StreamFile(const char* name, DWORD offset) { return E_NOINTERFACE; }
+
+ // this method is for memory sounds:
+ virtual HRESULT Load(DWORD bytes, BYTE* data) { return E_NOINTERFACE; } // => Ready
+
+ // this method is for sound resources:
+ virtual Sound* Duplicate() { return 0; } // => Ready
+
+ // transport operations:
+ virtual HRESULT Play() { return E_NOINTERFACE; } // => Playing
+ virtual HRESULT Rewind() { return E_NOINTERFACE; } // => Ready
+ virtual HRESULT Pause() { return E_NOINTERFACE; } // => Ready
+ virtual HRESULT Stop() { return E_NOINTERFACE; } // => Done
+
+ // accessors / mutators
+ int IsReady() const { return status == READY; }
+ int IsPlaying() const { return status == PLAYING; }
+ int IsDone() const { return status == DONE; }
+ int LoopCount() const { return looped; }
+
+ virtual DWORD GetFlags() const { return flags; }
+ virtual void SetFlags(DWORD f) { flags = f; }
+ virtual DWORD GetStatus() const { return status; }
+
+ virtual long GetVolume() const { return volume; }
+ virtual void SetVolume(long v) { volume = v; }
+ virtual long GetPan() const { return 0; }
+ virtual void SetPan(long p) { }
+
+ // (only for streamed sounds)
+ virtual double GetTotalTime() const { return 0; }
+ virtual double GetTimeRemaining() const { return 0; }
+ virtual double GetTimeElapsed() const { return 0; }
+
+ // These should be relative to the listener:
+ // (only used for localized sounds)
+ virtual const Vec3& GetLocation() const { return location; }
+ virtual void SetLocation(const Vec3& l) { location = l; }
+ virtual const Vec3& GetVelocity() const { return velocity; }
+ virtual void SetVelocity(const Vec3& v) { velocity = v; }
+
+ virtual float GetMinDistance() const { return 0; }
+ virtual void SetMinDistance(float f) { }
+ virtual float GetMaxDistance() const { return 0; }
+ virtual void SetMaxDistance(float f) { }
+
+ virtual void SetSoundCheck(SoundCheck* s) { sound_check = s; }
+ virtual void AddToSoundCard();
+
+ const char* GetFilename() const { return filename; }
+ void SetFilename(const char* s);
+
+protected:
+ DWORD flags;
+ DWORD status;
+ long volume; // centibels, (0 .. -10000)
+ int looped;
+ Vec3 location;
+ Vec3 velocity;
+ SoundCheck* sound_check;
+ char filename[64];
+
+ static SoundCard* creator;
+};
+
+// +--------------------------------------------------------------------+
+
+class SoundCheck
+{
+public:
+ virtual void Update(Sound* s) { }
+};
+
+#endif Sound_h
+
diff --git a/nGenEx/SoundCard.cpp b/nGenEx/SoundCard.cpp
new file mode 100644
index 0000000..98e9f20
--- /dev/null
+++ b/nGenEx/SoundCard.cpp
@@ -0,0 +1,103 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: SoundCard.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Abstract sound card class
+*/
+
+#include "MemDebug.h"
+#include "SoundCard.h"
+#include "Sound.h"
+
+// +--------------------------------------------------------------------+
+
+DWORD WINAPI SoundCardUpdateProc(LPVOID link);
+
+// +--------------------------------------------------------------------+
+
+SoundCard::SoundCard()
+ : status(SC_UNINITIALIZED), hthread(0), shutdown(false)
+{
+ DWORD thread_id = 0;
+ hthread = CreateThread(0, 4096, SoundCardUpdateProc,
+ (LPVOID) this, 0, &thread_id);
+}
+
+// +--------------------------------------------------------------------+
+
+SoundCard::~SoundCard()
+{
+ shutdown = true;
+
+ WaitForSingleObject(hthread, 500);
+ CloseHandle(hthread);
+ hthread = 0;
+
+ sounds.destroy();
+ status = SC_UNINITIALIZED;
+}
+
+// +--------------------------------------------------------------------+
+
+DWORD WINAPI SoundCardUpdateProc(LPVOID link)
+{
+ SoundCard* card = (SoundCard*) link;
+
+ if (card)
+ return card->UpdateThread();
+
+ return (DWORD) E_POINTER;
+}
+
+// +--------------------------------------------------------------------+
+
+DWORD
+SoundCard::UpdateThread()
+{
+ while (!shutdown) {
+ Update();
+ Sleep(50);
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SoundCard::Update()
+{
+ AutoThreadSync a(sync);
+
+ ListIter<Sound> iter = sounds;
+ while (++iter) {
+ Sound* s = iter.value();
+
+ s->Update();
+
+ if (s->GetStatus() == Sound::DONE &&
+ !(s->GetFlags() & Sound::LOCKED)) {
+
+ delete iter.removeItem();
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SoundCard::AddSound(Sound* s)
+{
+ AutoThreadSync a(sync);
+
+ if (!sounds.contains(s))
+ sounds.append(s);
+}
+
diff --git a/nGenEx/SoundCard.h b/nGenEx/SoundCard.h
new file mode 100644
index 0000000..460a731
--- /dev/null
+++ b/nGenEx/SoundCard.h
@@ -0,0 +1,76 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: SoundCard.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Abstract Audio Output class (hides details of DirectSound)
+*/
+
+#ifndef SoundCard_h
+#define SoundCard_h
+
+#include "Types.h"
+#include "List.h"
+#include "ThreadSync.h"
+
+// +--------------------------------------------------------------------+
+
+class Sound;
+class Camera;
+struct Vec3;
+
+// +--------------------------------------------------------------------+
+
+class SoundCard
+{
+public:
+ static const char* TYPENAME() { return "SoundCard"; }
+
+ SoundCard();
+ virtual ~SoundCard();
+
+ enum SoundStatus { SC_UNINITIALIZED,
+ SC_OK,
+ SC_ERROR,
+ SC_BAD_PARAM };
+ SoundStatus Status() const { return status; }
+
+ // Format of the sound card's primary buffer:
+ virtual bool GetFormat(LPWAVEFORMATEX format) { return false; }
+ virtual bool SetFormat(LPWAVEFORMATEX format) { return false; }
+ virtual bool SetFormat(int bits, int channels, int hertz) { return false; }
+ virtual bool Pause() { return false; }
+ virtual bool Resume() { return false; }
+ virtual bool StopSoundEffects() { return false; }
+
+ // Get a blank, writable sound buffer:
+ virtual Sound* CreateSound(DWORD flags, LPWAVEFORMATEX format) { return 0; }
+
+ // Create a sound resource:
+ virtual Sound* CreateSound(DWORD flags, LPWAVEFORMATEX format,
+ DWORD len, LPBYTE data) { return 0; }
+
+ // once per frame:
+ virtual void Update();
+
+ virtual void SetListener(const Camera& cam, const Vec3& vel) { }
+ virtual DWORD UpdateThread();
+ virtual void AddSound(Sound* s);
+
+protected:
+
+ bool shutdown;
+ HANDLE hthread;
+ SoundStatus status;
+ List<Sound> sounds;
+ ThreadSync sync;
+};
+
+#endif SoundCard_h
+
diff --git a/nGenEx/SoundD3D.cpp b/nGenEx/SoundD3D.cpp
new file mode 100644
index 0000000..212e856
--- /dev/null
+++ b/nGenEx/SoundD3D.cpp
@@ -0,0 +1,1295 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: SoundD3D.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ DirectSound and DirectSound3D (sound card) class
+*/
+
+//#define INITGUID
+#include <objbase.h>
+#include <cguid.h>
+#include <mmsystem.h>
+#include <dsound.h>
+
+
+#include "MemDebug.h"
+#include "SoundD3D.h"
+#include "Game.h"
+
+#ifdef DIRECT_SOUND_3D
+#include "ia3d.h"
+#endif
+
+// +--------------------------------------------------------------------+
+
+void Print(const char* msg, ...);
+char* DSErrStr(HRESULT dserr);
+void SoundD3DError(const char* msg, HRESULT dserr);
+
+static int DS3D_report_errors = 1;
+
+#ifndef RELEASE
+#define RELEASE(x) if (x) { x->Release(); x=NULL; }
+#endif
+
+// +====================================================================+
+// | SOUND CARD D3D
+// +====================================================================+
+
+SoundCardD3D::SoundCardD3D(HWND hwnd)
+ : soundcard(0), primary(0)
+{
+ HRESULT err = 0;
+ status = SC_ERROR;
+
+ // 1) Get interface to DirectSound object:
+
+#ifdef DIRECT_SOUND_3D
+ CoInitialize(NULL);
+ err = CoCreateInstance(CLSID_A3d, NULL, CLSCTX_INPROC_SERVER,
+ IID_IDirectSound, (VOID **)&soundcard);
+
+ if (SUCCEEDED(err)) {
+ soundcard->Initialize(NULL);
+ SoundD3DError("Initialized Aureal3D Sound", 0);
+ }
+ else {
+ SoundD3DError("Could not initialize Aureal3D Sound", err);
+ SoundD3DError("Proceding with standard DirectSoundCreate", 0);
+#endif
+
+ err = DirectSoundCreate(0, &soundcard, 0);
+ if (FAILED(err)) {
+ SoundD3DError("Could not create DirectSound object.", err);
+ soundcard = 0;
+ primary = 0;
+ return;
+ }
+
+#ifdef DIRECT_SOUND_3D
+ }
+#endif
+
+ // 2) Set the cooperative level:
+ err = soundcard->SetCooperativeLevel(hwnd, DSSCL_PRIORITY);
+ if (FAILED(err)) {
+ SoundD3DError("Could not set cooperative level.", err);
+ RELEASE(soundcard);
+ return;
+ }
+
+ // Prepare to initialize the primary buffer:
+ DSCAPS caps;
+ memset(&caps, 0, sizeof(caps));
+ caps.dwSize = sizeof(caps);
+
+ err = soundcard->GetCaps(&caps);
+ if (FAILED(err)) {
+ SoundD3DError("Could not get soundcard caps.", err);
+ RELEASE(soundcard);
+ return;
+ }
+
+ if (caps.dwFlags & DSCAPS_EMULDRIVER)
+ Print(" WARNING: using DirectSound emulated drivers\n");
+
+ memset(&dsbd, 0, sizeof(dsbd));
+ dsbd.dwSize = sizeof(dsbd);
+
+#ifdef DIRECT_SOUND_3D
+ int use_ds3d = true;
+
+ // 3) Set up the primary buffer (try to use DS3D):
+ dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRL3D;
+
+ err = soundcard->CreateSoundBuffer(&dsbd, &primary, 0);
+ if (err == DSERR_CONTROLUNAVAIL) {
+ use_ds3d = false;
+
+ // try again, without using DS3D
+ dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRLDEFAULT;
+
+ err = soundcard->CreateSoundBuffer(&dsbd, &primary, 0);
+ if (FAILED(err)) {
+ SoundD3DError("Could not initialize primary buffer", err);
+ RELEASE(soundcard);
+ return;
+ }
+ else {
+ Print(" WARNING: DirectSound3D is not available, simulating instead\n");
+ }
+ }
+
+ // 4) Set up the listener:
+ if (primary && use_ds3d) {
+ err = primary->QueryInterface(IID_IDirectSound3DListener, (void**)&listener);
+ if (FAILED(err)) {
+ SoundD3DError("Could not get listener interface", err);
+ }
+ else {
+ listener->SetPosition(0.0f, 0.0f, 0.0f, DS3D_IMMEDIATE);
+ listener->SetOrientation(0.0f, 0.0f, 1.0f,
+ 0.0f, 1.0f, 0.0f, DS3D_IMMEDIATE);
+ }
+ }
+
+#else
+ // 3) Set up the primary buffer:
+ dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
+
+ err = soundcard->CreateSoundBuffer(&dsbd, &primary, 0);
+ if (FAILED(err)) {
+ SoundD3DError("Could not initialize primary buffer", err);
+ RELEASE(soundcard);
+ return;
+ }
+#endif
+
+ // 5) Set primary buffer parameters to 16 bit STEREO at 44kHz
+ SetFormat(16, 2, 44100);
+
+ // read back the result format and display to the log file:
+ GetFormat(0);
+ ShowFormat();
+
+ status = SC_OK;
+}
+
+// +--------------------------------------------------------------------+
+
+SoundCardD3D::~SoundCardD3D()
+{
+ ListIter<Sound> iter = sounds;
+ while (++iter) {
+ Sound* s = iter.value();
+ s->Stop();
+ }
+
+ sounds.destroy();
+
+#ifdef DIRECT_SOUND_3D
+ RELEASE(listener);
+#endif
+
+ RELEASE(primary);
+ RELEASE(soundcard);
+
+ Print(" SoundCardD3D: shutdown\n");
+
+ status = SC_UNINITIALIZED;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+SoundCardD3D::SetFormat(int bits, int channels, int hertz)
+{
+ if (!soundcard) return false;
+
+ DSCAPS caps;
+ memset(&caps, 0, sizeof(caps));
+ caps.dwSize = sizeof(caps);
+ soundcard->GetCaps(&caps);
+
+ if (!(caps.dwFlags & DSCAPS_PRIMARY16BIT)) bits = 8;
+ if (!(caps.dwFlags & DSCAPS_PRIMARYSTEREO)) channels = 1;
+
+ memset(&wfex, 0, sizeof(wfex));
+
+ wfex.wFormatTag = WAVE_FORMAT_PCM;
+ wfex.nChannels = channels;
+ wfex.nSamplesPerSec = hertz;
+ wfex.nBlockAlign = (channels * bits) / 8;
+ wfex.nAvgBytesPerSec = wfex.nSamplesPerSec * wfex.nBlockAlign;
+ wfex.wBitsPerSample = bits;
+
+ return SetFormat(&wfex);
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+SoundCardD3D::SetFormat(LPWAVEFORMATEX format)
+{
+ HRESULT err = E_FAIL;
+
+ if (primary)
+ err = primary->SetFormat(format);
+
+ return SUCCEEDED(err);
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+SoundCardD3D::GetFormat(LPWAVEFORMATEX format)
+{
+ if (!format) format = &wfex;
+
+ HRESULT err = E_FAIL;
+
+ if (primary)
+ err = primary->GetFormat(format, sizeof(WAVEFORMATEX), 0);
+
+ return SUCCEEDED(err);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SoundCardD3D::ShowFormat()
+{
+ Print(" SoundCardD3D Primary Buffer Format:\n");
+ Print(" bits: %d\n", wfex.wBitsPerSample);
+ Print(" chls: %d\n", wfex.nChannels);
+ Print(" rate: %d\n\n", wfex.nSamplesPerSec);
+}
+
+// +--------------------------------------------------------------------+
+
+Sound*
+SoundCardD3D::CreateSound(DWORD flags, LPWAVEFORMATEX format)
+{
+ if (!soundcard) return 0;
+
+ Sound* result = new(__FILE__,__LINE__) SoundD3D(soundcard, flags, format);
+ if (result) AddSound(result);
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+Sound*
+SoundCardD3D::CreateSound(DWORD flags, LPWAVEFORMATEX format, DWORD len, LPBYTE data)
+{
+ if (!soundcard) return 0;
+ Sound* result = new(__FILE__,__LINE__) SoundD3D(soundcard, flags, format, len, data);
+
+ if (flags & (Sound::STREAMED | Sound::OGGVORBIS)) {
+ if (result)
+ AddSound(result);
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SoundCardD3D::SetListener(const Camera& cam, const Vec3& vel)
+{
+ Point pos = cam.Pos();
+
+#ifdef DIRECT_SOUND_3D
+ listener->SetPosition((float) pos.x, (float) pos.z, (float) pos.y, DS3D_IMMEDIATE);
+ listener->SetOrientation((float) cam.vpn().x, (float) cam.vpn().y, (float) cam.vpn().z,
+ (float) cam.vup().x, (float) cam.vup().y, (float) cam.vup().z,
+ DS3D_IMMEDIATE);
+ listener->SetVelocity(vel.x, vel.y, vel.z, DS3D_IMMEDIATE);
+#else
+ listener.Clone(cam);
+ listener.MoveTo(pos.x, pos.z, pos.y);
+ velocity = vel;
+#endif
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+SoundCardD3D::Pause()
+{
+ AutoThreadSync a(sync);
+
+ ListIter<Sound> iter = sounds;
+ while (++iter) {
+ Sound* s = iter.value();
+
+ if ((s->GetFlags() & Sound::INTERFACE) == 0)
+ s->Pause();
+ }
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+SoundCardD3D::Resume()
+{
+ AutoThreadSync a(sync);
+
+ ListIter<Sound> iter = sounds;
+ while (++iter) {
+ Sound* s = iter.value();
+
+ if (s->IsReady())
+ s->Play();
+ }
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+SoundCardD3D::StopSoundEffects()
+{
+ AutoThreadSync a(sync);
+
+ DWORD ok_sounds = (Sound::INTERFACE | Sound::OGGVORBIS | Sound::RESOURCE);
+
+ ListIter<Sound> iter = sounds;
+ while (++iter) {
+ Sound* s = iter.value();
+
+ if ((s->GetFlags() & ok_sounds) == 0)
+ s->Stop();
+ }
+
+ return true;
+}
+
+// +====================================================================+
+// | SOUND D3D
+// +====================================================================+
+
+SoundD3D::SoundD3D(LPDIRECTSOUND card, DWORD flag_req, LPWAVEFORMATEX format)
+ : soundcard(card), buffer(0), min_dist(1.0f), max_dist(100000.0f),
+ stream(0), stream_left(0), min_safety(0), read_size(0), transfer(0), w(0), r(0),
+ stream_offset(0), data_len(0), data(0), moved(false), eos_written(false), eos_latch(0),
+ ov_file(0), total_time(0)
+{
+ flags = flag_req;
+
+ CopyMemory(&wfex, format, sizeof(wfex));
+ ZeroMemory(&dsbd, sizeof(dsbd));
+
+ dsbd.dwSize = sizeof(dsbd);
+ dsbd.dwFlags = DSBCAPS_CTRLVOLUME /* | DSBCAPS_GETCURRENTPOSITION2 */;
+ dsbd.lpwfxFormat = &wfex;
+
+#ifdef DIRECT_SOUND_3D
+ sound3d = 0;
+ if (flags & LOCALIZED)
+ if (flags & LOC_3D)
+ dsbd.dwFlags |= DSBCAPS_CTRL3D;
+ else
+ dsbd.dwFlags |= DSBCAPS_CTRLPAN | DSBCAPS_CTRLFREQUENCY;
+#else
+ dsbd.dwFlags |= DSBCAPS_LOCSOFTWARE;
+
+ if (flags & LOCALIZED)
+ dsbd.dwFlags |= DSBCAPS_CTRLPAN | DSBCAPS_CTRLFREQUENCY;
+#endif
+
+}
+
+// +--------------------------------------------------------------------+
+// SOUND RESOURCE CONSTRUCTOR:
+// (Now also used to create Ogg Vorbis streaming sound objects)
+
+SoundD3D::SoundD3D(LPDIRECTSOUND card, DWORD flag_req, LPWAVEFORMATEX format, DWORD len, LPBYTE pData)
+ : soundcard(card), buffer(0), min_dist(1.0f), max_dist(100000.0f),
+ stream(0), stream_left(0), min_safety(0), read_size(0), transfer(0), w(0), r(0),
+ stream_offset(0), data_len(0), data(0), moved(false), eos_written(false), eos_latch(0),
+ ov_file(0)
+{
+ flags = flag_req;
+
+ if (!(flags & (STREAMED | OGGVORBIS)))
+ flags = flag_req | RESOURCE;
+
+ CopyMemory(&wfex, format, sizeof(wfex));
+ ZeroMemory(&dsbd, sizeof(dsbd));
+
+ dsbd.dwSize = sizeof(dsbd);
+ dsbd.dwFlags = DSBCAPS_CTRLVOLUME /* | DSBCAPS_GETCURRENTPOSITION2 */;
+ dsbd.lpwfxFormat = &wfex;
+
+#ifdef DIRECT_SOUND_3D
+ sound3d = 0;
+ if (flags & LOCALIZED)
+ if (flags & LOC_3D)
+ dsbd.dwFlags |= DSBCAPS_CTRL3D;
+ else
+ dsbd.dwFlags |= DSBCAPS_CTRLPAN | DSBCAPS_CTRLFREQUENCY;
+#else
+ if (flags & LOCALIZED)
+ dsbd.dwFlags |= DSBCAPS_CTRLPAN | DSBCAPS_CTRLFREQUENCY;
+#endif
+
+ if (len) {
+ // If this is an OGG VORBIS streaming sound,
+ // the parameter that normally points to the actual data
+ // is used to pass in an Ogg Vorbis file structure instead:
+
+ if (flags & OGGVORBIS) {
+ ov_file = (OggVorbis_File*) pData;
+ StreamOggFile();
+ }
+
+ else {
+ data_len = len;
+ data = new(__FILE__,__LINE__) BYTE[len];
+
+ if (!data) {
+ data_len = 0;
+ }
+
+ else {
+ CopyMemory(data, pData, data_len);
+ Load(data_len, data);
+ }
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+SoundD3D::~SoundD3D()
+{
+ delete [] data;
+ delete [] transfer;
+
+ if (ov_file) {
+ ov_clear(ov_file);
+ delete ov_file;
+ }
+
+ else if (stream) {
+ fclose(stream);
+ }
+
+ RELEASE(buffer);
+
+#ifdef DIRECT_SOUND_3D
+ RELEASE(sound3d);
+#endif
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SoundD3D::Update()
+{
+ if (!buffer || status != PLAYING) return; // nothing to do
+
+ DWORD dstat;
+ HRESULT hr = buffer->GetStatus(&dstat);
+ if (FAILED(hr)) {
+ SoundD3DError("Update: GetStatus failed", hr);
+ return;
+ }
+
+ AutoThreadSync a(sync);
+
+ if (sound_check) sound_check->Update(this);
+
+ if (!Game::Paused() || flags & STREAMED) {
+ // see if we are done:
+ if (!(dstat & DSBSTATUS_PLAYING)) {
+ status = DONE;
+ buffer->Stop();
+ return;
+ }
+
+ // if not done, see if we need to change location params:
+ if (moved) {
+ Localize();
+ }
+ }
+
+ // if not done, see if we need to load more data
+ // into the streaming buffer:
+ if (flags & STREAMED) {
+ buffer->GetCurrentPosition(&r, 0);
+
+ DWORD data_left;
+ if (w > r)
+ data_left = w - r;
+ else
+ data_left = w + (read_size + min_safety) - r;
+
+ // getting low, fill 'er up:
+ if (eos_written || data_left <= min_safety) {
+ StreamBlock();
+
+ if (stream_left == 0) {
+ // if this is the end of a looping stream,
+ if (flags & LOOP) {
+ RewindStream();
+ looped++;
+ }
+ else {
+ if (!eos_written) {
+ eos_written = true;
+ eos_latch = 3;
+ }
+
+ else if (--eos_latch == 0) {
+ status = DONE;
+ buffer->Stop();
+ }
+ }
+ }
+
+ status = PLAYING;
+ }
+ }
+}
+
+void
+SoundD3D::StreamBlock()
+{
+ if (flags & OGGVORBIS) {
+ StreamOggBlock();
+ return;
+ }
+
+ if (!stream || !stream_left)
+ return;
+
+ if (stream_left < read_size) {
+ if (stream_left > 0) {
+ fread(transfer, stream_left, 1, stream);
+ Load(stream_left, transfer);
+ stream_left = 0;
+ }
+ }
+
+ else if (read_size > 0) {
+ fread(transfer, read_size, 1, stream);
+ Load(read_size, transfer);
+ stream_left -= read_size;
+ }
+}
+
+int ogg_read_error_count = 0;
+
+void
+SoundD3D::StreamOggBlock()
+{
+ int current_bitstream;
+ DWORD bytes_read = 0;
+ long retval = 0;
+ char* p = (char*) transfer;
+
+ while (stream_left && bytes_read < read_size) {
+ retval = ov_read(ov_file, p, read_size-bytes_read, 0,2,1, &current_bitstream);
+
+ if (retval == 0) {
+ /* EOF */
+ stream_left = 0;
+ }
+ else if (retval < 0) {
+ /* error in the stream. Not a problem, just reporting it in
+ case the app cares. In this case, we don't. */
+ ogg_read_error_count++;
+ }
+ else {
+ /* we don't bother dealing with sample rate changes, etc, but you'll have to ??? */
+ bytes_read += retval;
+ stream_left -= retval;
+ p += retval;
+ }
+ }
+
+ if (bytes_read)
+ Load(bytes_read, transfer);
+}
+
+void
+SoundD3D::RewindStream()
+{
+ if (flags & OGGVORBIS) {
+ RewindOggStream();
+ return;
+ }
+
+ if (!stream || !buffer)
+ return;
+
+ // rewind the stream and keep going...
+ eos_written = false;
+ eos_latch = 0;
+ read_size = wfex.nAvgBytesPerSec / 2;
+
+ // find the size of the file:
+ fseek(stream, 0, SEEK_END);
+ stream_left = ftell(stream) - stream_offset;
+ fseek(stream, stream_offset, SEEK_SET);
+
+ total_time = (double) stream_left /
+ (double) wfex.nAvgBytesPerSec;
+
+ if (stream_left < read_size) {
+ status = DONE;
+ buffer->Stop();
+ }
+}
+
+void
+SoundD3D::RewindOggStream()
+{
+ if (!ov_file || !buffer)
+ return;
+
+ // rewind the stream and keep going...
+ eos_written = false;
+ eos_latch = 0;
+ read_size = wfex.nAvgBytesPerSec / 2;
+
+ // set the stream pointer back to the beginning:
+ ov_pcm_seek(ov_file, 0);
+
+ // find the size of the file:
+ stream_left = (DWORD) ov_pcm_total(ov_file,-1);
+ stream_offset = 0;
+
+ total_time = (double) stream_left /
+ (double) wfex.nAvgBytesPerSec;
+
+ if (stream_left < read_size) {
+ status = DONE;
+ buffer->Stop();
+ }
+}
+
+void
+SoundD3D::Localize()
+{
+#ifdef DIRECT_SOUND_3D
+ if (sound3d) {
+ sound3d->SetMinDistance(min_dist, DS3D_IMMEDIATE);
+ sound3d->SetMaxDistance(max_dist, DS3D_IMMEDIATE);
+ sound3d->SetPosition(location.x, location.y, location.z, DS3D_IMMEDIATE);
+ sound3d->SetVelocity(velocity.x, velocity.y, velocity.z, DS3D_IMMEDIATE);
+ }
+
+#else
+
+ // if no buffer, nothing to do:
+ if (!buffer) {
+ moved = false;
+ return;
+ }
+
+ // Compute pan and volume from scratch:
+
+ if ((flags & LOC_3D) && creator) {
+ Vec3 loc = location;
+
+ SoundCardD3D* ears = (SoundCardD3D*) creator;
+ Camera& listener = ears->listener;
+ Vec3 ear_loc = listener.Pos(); ear_loc.SwapYZ();
+ Vec3 direction = loc - ear_loc;
+
+ loc.x = direction * listener.vrt();
+ loc.y = direction * listener.vup();
+ loc.z = direction * listener.vpn();
+
+ double pan = 10000;
+ if (loc.z != 0.0f) pan = fabs(1000.0f * loc.x / loc.z);
+ if (pan > 10000) pan = 10000;
+ if (loc.x < 0) pan = -pan;
+
+ if (volume > 0)
+ volume = 0;
+
+ double vol = volume;
+ double mind2 = min_dist * min_dist;
+ double maxd2 = max_dist * max_dist;
+ double d2 = (loc.x*loc.x) + (loc.y*loc.y) + (loc.z*loc.z);
+
+ if (d2 > maxd2)
+ vol = -10000;
+ else if (d2 > mind2)
+ vol -= (d2-mind2)/(maxd2-mind2) * (vol+10000);
+
+ // clamp volume to legal range:
+ if (vol < -10000) vol = -10000;
+ else if (vol > volume) vol = volume;
+
+ /***
+ Print("Localize: ears = (%f, %f, %f)\n", ear_loc.x, ear_loc.y, ear_loc.z);
+ Print(" world = (%f, %f, %f)\n", location.x, location.y, location.z);
+ Print(" view = (%f, %f, %f)\n", loc.x, loc.y, loc.z);
+ Print(" Pan=%f Volume=%f\n", pan, vol);
+ /***/
+
+ HRESULT hr = buffer->SetPan((LONG) pan);
+ if (!SUCCEEDED(hr)) {
+ char warn[512];
+ sprintf(warn, "Warning could not set pan on buffer to %d", pan);
+ SoundD3DError(warn, hr);
+ }
+
+ hr = buffer->SetVolume((LONG) vol);
+ if (!SUCCEEDED(hr)) {
+ char warn[512];
+ sprintf(warn, "Warning: could not set volume on buffer to %d", vol);
+ SoundD3DError(warn, hr);
+ }
+
+ // if not too far to hear...
+ if ((flags & DOPPLER) && (d2 < maxd2)) {
+ // COMPUTE DOPPLER SHIFT:
+ const float c = 10000.0f;
+
+ direction.Normalize();
+ float v_L = ears->velocity * direction;
+ float v_S = velocity * direction;
+
+ DWORD f_shift = wfex.nSamplesPerSec;
+
+ if (v_L != v_S) {
+ // towards listener:
+ if (v_S < 0)
+ f_shift = wfex.nSamplesPerSec + 20;
+ else
+ f_shift = wfex.nSamplesPerSec - 20;
+ }
+
+ // distance rolloff of high frequencies:
+ double dist = sqrt(d2);
+ DWORD roll_off = (DWORD) (80 * dist/max_dist);
+
+ f_shift -= roll_off;
+
+ if (f_shift < 100) f_shift = 100;
+ if (f_shift > 100000) f_shift = 100000;
+
+ hr = buffer->SetFrequency(f_shift);
+ if (!SUCCEEDED(hr)) {
+ char warn[512];
+ sprintf(warn, "Warning: could not set Doppler frequency on buffer to %d", f_shift);
+ SoundD3DError(warn, hr);
+ }
+ }
+ }
+ else {
+ buffer->SetPan((LONG) location.x);
+ buffer->SetVolume((LONG) volume);
+ }
+#endif
+
+ moved = false;
+}
+
+// +--------------------------------------------------------------------+
+
+Sound*
+SoundD3D::Duplicate()
+{
+ Sound* sound = 0;
+
+ if (flags & RESOURCE) {
+ sound = Sound::Create(flags & ~RESOURCE, &wfex);
+
+ if (sound && !(flags & STREAMED)) {
+ sound->SetMinDistance(min_dist);
+ sound->SetMaxDistance(max_dist);
+
+ if (!buffer) {
+ sound->Load(data_len, data);
+ }
+
+ else {
+ SoundD3D* s3d = (SoundD3D*) sound;
+ soundcard->DuplicateSoundBuffer(buffer, &s3d->buffer);
+ sound->Rewind();
+ }
+ }
+ }
+
+ return sound;
+}
+
+// +--------------------------------------------------------------------+
+
+HRESULT
+SoundD3D::StreamFile(const char* name, DWORD offset)
+{
+ DWORD buf_size = wfex.nAvgBytesPerSec / 2;
+ DWORD safety_zone = buf_size * 2;
+
+ if (stream) {
+ delete[] transfer;
+ transfer = 0;
+ fclose(stream);
+ }
+
+ status = UNINITIALIZED;
+ stream_left = 0;
+ stream_offset = offset;
+
+ eos_written = false;
+ eos_latch = 0;
+ min_safety = safety_zone;
+ read_size = buf_size;
+
+ // open the stream:
+ if ((stream = fopen(name, "rb")) == 0) {
+ SoundD3DError("StreamFile: could not open stream", E_FAIL);
+ return E_FAIL;
+ }
+
+ // find the size of the file:
+ fseek(stream, 0, SEEK_END);
+ stream_left = ftell(stream) - offset;
+ fseek(stream, stream_offset, SEEK_SET);
+
+ total_time = (double) stream_left /
+ (double) wfex.nAvgBytesPerSec;
+
+ if (stream_left < read_size) {
+ read_size = stream_left;
+ }
+
+ HRESULT hr = AllocateBuffer(read_size + min_safety);
+
+ if (FAILED(hr))
+ return hr;
+
+ flags |= STREAMED;
+
+ // preload the buffer:
+ w = r = 0;
+ transfer = new(__FILE__,__LINE__) BYTE[read_size + 1024];
+
+ if (!transfer) {
+ hr = E_FAIL;
+ }
+
+ else {
+ ZeroMemory(transfer, read_size+1024);
+ StreamBlock();
+ }
+
+ return hr;
+}
+
+// +--------------------------------------------------------------------+
+
+HRESULT
+SoundD3D::StreamOggFile()
+{
+ DWORD buf_size = wfex.nAvgBytesPerSec / 2;
+ DWORD safety_zone = buf_size * 2;
+
+ if (stream) {
+ delete[] transfer;
+ fclose(stream);
+
+ transfer = 0;
+ stream = 0;
+ }
+
+ status = UNINITIALIZED;
+ stream_left = (DWORD) ov_pcm_total(ov_file,-1);
+ stream_offset = 0;
+
+ eos_written = false;
+ eos_latch = 0;
+ min_safety = safety_zone;
+ read_size = buf_size;
+
+ total_time = (double) stream_left /
+ (double) wfex.nAvgBytesPerSec;
+
+ if (stream_left < read_size) {
+ read_size = stream_left;
+ }
+
+ HRESULT hr = AllocateBuffer(read_size + min_safety);
+
+ if (FAILED(hr))
+ return hr;
+
+ flags |= STREAMED | OGGVORBIS;
+
+ // preload the buffer:
+ w = r = 0;
+ transfer = new(__FILE__,__LINE__) BYTE[read_size + 1024];
+
+ if (!transfer) {
+ hr = E_FAIL;
+ }
+
+ else {
+ ZeroMemory(transfer, read_size+1024);
+ StreamOggBlock();
+ }
+
+ return hr;
+}
+
+// +--------------------------------------------------------------------+
+
+HRESULT
+SoundD3D::Load(DWORD bytes, BYTE* data)
+{
+ status = UNINITIALIZED;
+
+ HRESULT hr;
+
+ if (!buffer) {
+ hr = AllocateBuffer(bytes);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ }
+
+ LPVOID dest1, dest2;
+ DWORD size1, size2;
+
+ hr = buffer->Lock(w, bytes, &dest1, &size1, &dest2, &size2, 0);
+
+ if (hr == DSERR_BUFFERLOST) {
+ buffer->Restore();
+ hr = buffer->Lock(w, bytes, &dest1, &size1, &dest2, &size2, 0);
+ }
+
+ if (SUCCEEDED(hr)) {
+ CopyMemory(dest1, data, size1);
+ if (dest2) {
+ CopyMemory(dest2, data + size1, size2);
+ }
+
+ if (flags & STREAMED)
+ w = (w + size1 + size2) % (read_size + min_safety);
+ else
+ w += size1 + size2;
+
+ hr = buffer->Unlock(dest1, size1, dest2, size2);
+ if (FAILED(hr)) {
+ SoundD3DError("Load: could not unlock buffer", hr);
+ }
+ }
+ else {
+ SoundD3DError("Load: could not lock buffer", hr);
+ }
+
+ if (SUCCEEDED(hr)) {
+ status = READY;
+ }
+
+ return hr;
+}
+
+// +--------------------------------------------------------------------+
+
+HRESULT
+SoundD3D::AllocateBuffer(DWORD bytes)
+{
+ HRESULT hr = S_OK;
+
+ if (!buffer) {
+ dsbd.dwBufferBytes = bytes;
+
+ if (soundcard) {
+ hr = soundcard->CreateSoundBuffer(&dsbd, &buffer, NULL);
+
+ if (FAILED(hr)) {
+ SoundD3DError("AllocateBuffer: could not create buffer", hr);
+
+ Print(" dsbd.dwSize = %8d\n", dsbd.dwSize);
+ Print(" dsbd.dwFlags = %08x\n", dsbd.dwFlags);
+ Print(" dsbd.dwBufferBytes = %8d\n", dsbd.dwBufferBytes);
+ Print(" dsbd.lpwfxFormat = %08x\n", dsbd.lpwfxFormat);
+
+ if (dsbd.lpwfxFormat) {
+ Print(" wfex.wBitsPerSample = %8d\n", dsbd.lpwfxFormat->wBitsPerSample);
+ Print(" wfex.nChannels = %8d\n", dsbd.lpwfxFormat->nChannels);
+ Print(" wfex.nSamplesPerSec = %8d\n", dsbd.lpwfxFormat->nSamplesPerSec);
+ }
+ }
+ }
+ else {
+ SoundD3DError("AllocateBuffer: soundcard is null", E_FAIL);
+ }
+ }
+
+ return hr;
+}
+
+// +--------------------------------------------------------------------+
+
+HRESULT
+SoundD3D::Play()
+{
+ if (IsPlaying()) return S_OK;
+ if (!buffer) return E_FAIL;
+
+ HRESULT hr = E_FAIL;
+
+ if (IsDone())
+ hr = Rewind();
+
+ if (IsReady()) {
+ if (moved)
+ Localize();
+
+ if (flags & LOOP || flags & STREAMED)
+ hr = buffer->Play(0, 0, DSBPLAY_LOOPING);
+ else
+ hr = buffer->Play(0, 0, 0);
+
+ if (SUCCEEDED(hr))
+ status = PLAYING;
+ }
+
+ return hr;
+}
+
+// +--------------------------------------------------------------------+
+
+HRESULT
+SoundD3D::Rewind()
+{
+ if (!buffer) return E_FAIL;
+
+ HRESULT hr = S_OK;
+
+ if (IsPlaying())
+ hr = Stop();
+
+ if (flags & STREAMED) {
+ RewindStream();
+ StreamBlock();
+ }
+
+ else {
+ hr = buffer->SetCurrentPosition(0);
+ }
+
+ if (SUCCEEDED(hr)) {
+ status = READY;
+ looped = 0;
+ }
+
+ return hr;
+}
+
+// +--------------------------------------------------------------------+
+
+HRESULT
+SoundD3D::Pause()
+{
+ if (status == DONE)
+ return S_OK;
+
+ HRESULT hr = Stop();
+
+ if (SUCCEEDED(hr))
+ status = READY;
+
+ return hr;
+}
+
+// +--------------------------------------------------------------------+
+
+HRESULT
+SoundD3D::Stop()
+{
+ if (!buffer)
+ return E_FAIL;
+
+ if (!IsPlaying()) {
+ status = DONE;
+ return S_OK;
+ }
+
+ status = DONE;
+ return buffer->Stop();
+}
+
+// +--------------------------------------------------------------------+
+
+double
+SoundD3D::GetTimeRemaining() const
+{
+ double time_left = -1;
+
+ if (IsPlaying() || IsReady()) {
+ time_left = (double) stream_left /
+ (double) wfex.nAvgBytesPerSec;
+ }
+
+ return time_left;
+}
+
+double
+SoundD3D::GetTimeElapsed() const
+{
+ double time_elapsed = 0;
+
+ if (IsPlaying()) {
+ time_elapsed = total_time - GetTimeRemaining();
+ }
+
+ return time_elapsed;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SoundD3D::SetVolume(long v)
+{
+ if (v > 0) v = 0;
+ else if (v < -10000) v = -10000;
+
+ volume = v;
+ moved = true;
+}
+
+// +--------------------------------------------------------------------+
+
+long
+SoundD3D::GetPan() const
+{
+ long p = 10000;
+
+ if (location.z) p = (long) fabs(location.x/location.z);
+ if (p > 10000) p = 10000;
+ if (location.x < 0) p = -p;
+
+ return p;
+}
+
+void
+SoundD3D::SetPan(long p)
+{
+ if (p > 10000) p = 10000;
+ if (p < -10000) p = -10000;
+
+ location.x = (float) p;
+ location.y = 0.0f;
+ location.z = 1.0f;
+ moved = true;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+SoundD3D::SetLocation(const Vec3& l)
+{
+ location = l;
+ moved = true;
+}
+
+void
+SoundD3D::SetVelocity(const Vec3& v)
+{
+ velocity = v;
+ moved = true;
+}
+
+// +--------------------------------------------------------------------+
+
+float
+SoundD3D::GetMinDistance() const
+{
+ return min_dist;
+}
+
+void
+SoundD3D::SetMinDistance(float f)
+{
+ min_dist = f;
+ moved = true;
+}
+
+// +--------------------------------------------------------------------+
+
+float
+SoundD3D::GetMaxDistance() const
+{
+ return max_dist;
+}
+void
+SoundD3D::SetMaxDistance(float f)
+{
+ max_dist = f;
+ moved = true;
+}
+
+
+// +--------------------------------------------------------------------+
+
+void
+SoundD3DError(const char* msg, HRESULT err)
+{
+ Print(" SoundD3D: %s. [%s]\n", msg, DSErrStr(err));
+}
+
+char*
+DSErrStr(HRESULT err)
+{
+ switch (err) {
+ case DS_OK: return "DS_OK";
+
+ case DSERR_ALLOCATED: return
+ "The call failed because resources (such as a priority level) "
+ "were already being used by another caller.";
+
+ case DSERR_CONTROLUNAVAIL: return
+ "The control (vol,pan,etc.) requested by the caller is not available.";
+
+ case DSERR_INVALIDPARAM: return
+ "An invalid parameter was passed to the returning function.";
+
+ case DSERR_INVALIDCALL: return
+ "This call is not valid for the current state of this object";
+
+ case DSERR_GENERIC: return
+ "An undetermined error occured inside the DirectSound subsystem";
+
+ case DSERR_PRIOLEVELNEEDED: return
+ "The caller does not have the priority level required for the function to succeed.";
+
+ case DSERR_OUTOFMEMORY: return
+ "Not enough free memory is available to complete the operation";
+
+ case DSERR_BADFORMAT: return
+ "The specified WAVE format is not supported";
+
+ case DSERR_UNSUPPORTED: return
+ "The function called is not supported at this time";
+
+ case DSERR_NODRIVER: return
+ "No sound driver is available for use";
+
+ case DSERR_ALREADYINITIALIZED: return
+ "This object is already initialized";
+
+ case DSERR_NOAGGREGATION: return
+ "This object does not support aggregation";
+
+ case DSERR_BUFFERLOST: return
+ "The buffer memory has been lost, and must be restored.";
+
+ case DSERR_OTHERAPPHASPRIO: return
+ "Another app has a higher priority level, preventing this call from succeeding.";
+
+ case DSERR_UNINITIALIZED: return
+ "This object has not been initialized.";
+
+#ifdef DIRECT_SOUND_3D
+ case DSERR_NOINTERFACE: return
+ "The requested COM interface is not available.";
+#endif
+
+ default: return "Unknown Error Code";
+ }
+
+ return "Internal Error";
+}
+
diff --git a/nGenEx/SoundD3D.h b/nGenEx/SoundD3D.h
new file mode 100644
index 0000000..8c09987
--- /dev/null
+++ b/nGenEx/SoundD3D.h
@@ -0,0 +1,162 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: SoundD3D.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ DirectSound3D Audio Output and Buffer classes
+*/
+
+#ifndef SoundD3D_h
+#define SoundD3D_h
+
+//#define DIRECT_SOUND_3D
+#include "SoundCard.h"
+#include "Sound.h"
+#include "Camera.h"
+#include "ThreadSync.h"
+#include <stdio.h>
+#include <dsound.h>
+#include <vorbis/vorbisfile.h>
+
+// +--------------------------------------------------------------------+
+
+class SoundD3D;
+class SoundCardD3D;
+
+// +--------------------------------------------------------------------+
+// Sound Implementation for DirectSound and DirectSound3D
+
+class SoundD3D : public Sound
+{
+public:
+ static const char* TYPENAME() { return "SoundD3D"; }
+
+ SoundD3D(LPDIRECTSOUND card, DWORD flags, LPWAVEFORMATEX format);
+ SoundD3D(LPDIRECTSOUND card, DWORD flags, LPWAVEFORMATEX format, DWORD len, LPBYTE data);
+ virtual ~SoundD3D();
+
+ virtual void Update();
+
+ virtual HRESULT StreamFile(const char* name, DWORD offset);
+ virtual HRESULT Load(DWORD bytes, BYTE* data);
+ virtual HRESULT Play();
+ virtual HRESULT Rewind();
+ virtual HRESULT Pause();
+ virtual HRESULT Stop();
+
+ virtual Sound* Duplicate();
+
+ // (only for streamed sounds)
+ virtual double GetTotalTime() const { return total_time; }
+ virtual double GetTimeRemaining() const;
+ virtual double GetTimeElapsed() const;
+
+ // (only used for localized sounds)
+ virtual void SetVolume(long v);
+ virtual long GetPan() const;
+ virtual void SetPan(long p);
+ virtual void SetLocation(const Vec3& l);
+ virtual void SetVelocity(const Vec3& v);
+
+ virtual float GetMinDistance() const;
+ virtual void SetMinDistance(float f);
+ virtual float GetMaxDistance() const;
+ virtual void SetMaxDistance(float f);
+
+
+protected:
+ void Localize();
+ HRESULT AllocateBuffer(DWORD bytes);
+ HRESULT StreamOggFile();
+
+ void StreamBlock();
+ void StreamOggBlock();
+ void RewindStream();
+ void RewindOggStream();
+
+ LPDIRECTSOUND soundcard;
+ WAVEFORMATEX wfex;
+ DSBUFFERDESC dsbd;
+ LPDIRECTSOUNDBUFFER buffer;
+
+ DWORD data_len;
+ LPBYTE data;
+
+#ifdef DIRECT_SOUND_3D
+ LPDIRECTSOUND3DBUFFER sound3d;
+#endif
+
+ float min_dist;
+ float max_dist;
+
+// STREAMED SOUND SUPPORT:
+ FILE* stream;
+ DWORD stream_left;
+ double total_time;
+ DWORD min_safety;
+ DWORD read_size;
+ BYTE* transfer;
+ DWORD w, r;
+ DWORD stream_offset;
+ bool eos_written;
+ BYTE eos_latch;
+ bool moved;
+
+ ThreadSync sync;
+ OggVorbis_File* ov_file;
+};
+
+// +--------------------------------------------------------------------+
+// Sound Card Implementation for DS and DS3D
+
+class SoundCardD3D : public SoundCard
+{
+ friend class SoundD3D;
+
+public:
+ static const char* TYPENAME() { return "SoundCardD3D"; }
+
+ SoundCardD3D(HWND hwnd);
+ virtual ~SoundCardD3D();
+
+ // Format of the sound card's primary buffer:
+ virtual bool GetFormat(LPWAVEFORMATEX format);
+ virtual bool SetFormat(LPWAVEFORMATEX format);
+ virtual bool SetFormat(int bits, int channels, int hertz);
+
+ virtual void ShowFormat();
+
+ // Get a blank, writable sound buffer:
+ virtual Sound* CreateSound(DWORD flags, LPWAVEFORMATEX format);
+
+ // Create a sound resource:
+ virtual Sound* CreateSound(DWORD flags, LPWAVEFORMATEX format, DWORD len, LPBYTE data);
+
+ virtual void SetListener(const Camera& cam, const Vec3& vel);
+ virtual bool Pause();
+ virtual bool Resume();
+ virtual bool StopSoundEffects();
+
+protected:
+ LPDIRECTSOUND soundcard;
+ LPDIRECTSOUNDBUFFER primary;
+
+#ifdef DIRECT_SOUND_3D
+ LPDIRECTSOUND3DLISTENER listener;
+#else
+ Camera listener;
+ Vec3 velocity;
+#endif
+
+ WAVEFORMATEX wfex;
+ DSBUFFERDESC dsbd;
+};
+
+#endif SoundD3D_h
+
diff --git a/nGenEx/Sprite.cpp b/nGenEx/Sprite.cpp
new file mode 100644
index 0000000..4086314
--- /dev/null
+++ b/nGenEx/Sprite.cpp
@@ -0,0 +1,380 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Sprite.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Sprite (Polygon) Object
+*/
+
+#include "MemDebug.h"
+#include "Sprite.h"
+#include "Bitmap.h"
+#include "Camera.h"
+#include "Polygon.h"
+#include "Video.h"
+#include "Game.h"
+
+// +--------------------------------------------------------------------+
+
+Sprite::Sprite()
+ : w(0), h(0), nframes(0), own_frames(0),
+ frames(0), frame_index(0), frame_time(100), loop(0), shade(1.0),
+ angle(0.0), blend_mode(4), filter(1), vset(4), poly(0)
+{
+ trans = true;
+
+ vset.space = VertexSet::WORLD_SPACE;
+ for (int i = 0; i < 4; i++) {
+ vset.diffuse[i] = Color::White.Value();
+ }
+
+ vset.tu[0] = 0.0f;
+ vset.tv[0] = 0.0f;
+ vset.tu[1] = 1.0f;
+ vset.tv[1] = 0.0f;
+ vset.tu[2] = 1.0f;
+ vset.tv[2] = 1.0f;
+ vset.tu[3] = 0.0f;
+ vset.tv[3] = 1.0f;
+
+ poly.nverts = 4;
+ poly.vertex_set = &vset;
+ poly.material = &mtl;
+ poly.verts[0] = 0;
+ poly.verts[1] = 1;
+ poly.verts[2] = 2;
+ poly.verts[3] = 3;
+}
+
+// +--------------------------------------------------------------------+
+
+Sprite::Sprite(Bitmap* animation, int length, int repeat, int share)
+ : w(0), h(0), nframes(0), own_frames(0),
+ frames(0), frame_index(0), frame_time(67), loop(0), shade(1.0),
+ angle(0.0), blend_mode(4), filter(1), vset(4), poly(0)
+{
+ trans = true;
+ SetAnimation(animation, length, repeat, share);
+
+ vset.space = VertexSet::WORLD_SPACE;
+ for (int i = 0; i < 4; i++) {
+ vset.diffuse[i] = Color::White.Value();
+ }
+
+ vset.tu[0] = 0.0f;
+ vset.tv[0] = 0.0f;
+ vset.tu[1] = 1.0f;
+ vset.tv[1] = 0.0f;
+ vset.tu[2] = 1.0f;
+ vset.tv[2] = 1.0f;
+ vset.tu[3] = 0.0f;
+ vset.tv[3] = 1.0f;
+
+ poly.nverts = 4;
+ poly.vertex_set = &vset;
+ poly.material = &mtl;
+ poly.verts[0] = 0;
+ poly.verts[1] = 1;
+ poly.verts[2] = 2;
+ poly.verts[3] = 3;}
+
+// +--------------------------------------------------------------------+
+
+Sprite::~Sprite()
+{
+ if (own_frames) {
+ if (nframes == 1)
+ delete frames;
+ else
+ delete [] frames;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Sprite::Scale(double scale)
+{
+ if (scale >= 0) {
+ w = (int) (scale * w);
+ h = (int) (scale * h);
+
+ radius = (float) ((w>h) ? w : h) / 2.0f;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Sprite::Rescale(double scale)
+{
+ if (scale >= 0 && Frame()) {
+ w = (int) (scale * Frame()->Width());
+ h = (int) (scale * Frame()->Height());
+
+ radius = (float) ((w>h) ? w : h) / 2.0f;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Sprite::Reshape(int w1, int h1)
+{
+ if (w1 >= 0 && h1 >= 0 && Frame()) {
+ w = w1;
+ h = h1;
+
+ radius = (float) ((w>h) ? w : h) / 2.0f;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Sprite::SetAnimation(Bitmap* animation, int length, int repeat, int share)
+{
+ if (animation) {
+ strncpy(name, animation->GetFilename(), 31);
+ name[31] = 0;
+
+ if (own_frames) {
+ if (nframes == 1)
+ delete frames;
+ else
+ delete [] frames;
+ }
+
+ w = animation->Width();
+ h = animation->Height();
+
+ radius = (float) ((w>h) ? w : h) / 2.0f;
+
+ own_frames = !share;
+ nframes = length;
+ frames = animation;
+ frame_index = 0;
+
+ if (repeat) {
+ loop = 1;
+ life = -1;
+ }
+ else {
+ loop = 0;
+ life = nframes;
+ }
+
+ last_time = Game::RealTime() - frame_time;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Sprite::SetTexCoords(const double* uv_interleaved)
+{
+ if (uv_interleaved) {
+ vset.tu[0] = (float) uv_interleaved[0];
+ vset.tv[0] = (float) uv_interleaved[1];
+ vset.tu[1] = (float) uv_interleaved[2];
+ vset.tv[1] = (float) uv_interleaved[3];
+ vset.tu[2] = (float) uv_interleaved[4];
+ vset.tv[2] = (float) uv_interleaved[5];
+ vset.tu[3] = (float) uv_interleaved[6];
+ vset.tv[3] = (float) uv_interleaved[7];
+ }
+ else {
+ vset.tu[0] = 0.0f;
+ vset.tv[0] = 0.0f;
+ vset.tu[1] = 1.0f;
+ vset.tv[1] = 0.0f;
+ vset.tu[2] = 1.0f;
+ vset.tv[2] = 1.0f;
+ vset.tu[3] = 0.0f;
+ vset.tv[3] = 1.0f;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+double
+Sprite::FrameRate() const
+{
+ return 1000.0 / (double) frame_time;
+}
+
+void
+Sprite::SetFrameRate(double rate)
+{
+ if (rate > 0.001 && rate < 100) {
+ frame_time = (int) (1000.0 / rate);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Sprite::SetFrameIndex(int n)
+{
+ if (n >= 0 && n < nframes)
+ frame_index = n;
+}
+
+// +--------------------------------------------------------------------+
+
+Bitmap*
+Sprite::Frame() const
+{
+ return frames + frame_index;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Sprite::Render(Video* video, DWORD flags)
+{
+ if (shade < 0.001 || hidden || !visible || !video)
+ return;
+
+ if (blend_mode == 2 && !(flags & Graphic::RENDER_ALPHA))
+ return;
+
+ if (blend_mode == 4 && !(flags & Graphic::RENDER_ADDITIVE))
+ return;
+
+ if (life > 0 || loop) {
+ const Camera* camera = video->GetCamera();
+ Matrix orient(camera->Orientation());
+ Vec3 nrm(camera->vpn() * -1);
+ ColorValue white((float) shade, (float) shade, (float) shade, (float) shade);
+ DWORD diff = white.ToColor().Value();
+
+ orient.Roll(angle);
+
+ Vec3 vx = Vec3((float) orient(0,0),
+ (float) orient(0,1),
+ (float) orient(0,2)) * (float) (w/2.0f);
+
+ Vec3 vy = Vec3((float) orient(1,0),
+ (float) orient(1,1),
+ (float) orient(1,2)) * (float) (h/2.0f);
+
+ vset.loc[0] = loc - vx + vy;
+ vset.nrm[0] = nrm;
+ vset.diffuse[0] = diff;
+
+ vset.loc[1] = loc + vx + vy;
+ vset.nrm[1] = nrm;
+ vset.diffuse[1] = diff;
+
+ vset.loc[2] = loc + vx - vy;
+ vset.nrm[2] = nrm;
+ vset.diffuse[2] = diff;
+
+ vset.loc[3] = loc - vx - vy;
+ vset.nrm[3] = nrm;
+ vset.diffuse[3] = diff;
+
+ if (luminous) {
+ mtl.Ka = Color::Black;
+ mtl.Kd = Color::Black;
+ mtl.Ks = Color::Black;
+ mtl.Ke = white;
+ mtl.tex_diffuse = Frame();
+ mtl.tex_emissive = Frame();
+ mtl.blend = blend_mode;
+ mtl.luminous = luminous;
+ }
+
+ else {
+ mtl.Ka = white;
+ mtl.Kd = white;
+ mtl.Ks = Color::Black;
+ mtl.Ke = Color::Black;
+ mtl.tex_diffuse = Frame();
+ mtl.tex_emissive = 0;
+ mtl.blend = blend_mode;
+ mtl.luminous = luminous;
+ }
+
+ video->DrawPolys(1, &poly);
+ }
+
+ memset(&screen_rect, 0, sizeof(Rect));
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Sprite::Render2D(Video* video)
+{
+ if (shade < 0.001 || hidden || !visible || !video)
+ return;
+
+ ColorValue white((float) shade, (float) shade, (float) shade, (float) shade);
+ DWORD diff = white.ToColor().Value();
+
+ double ca = cos(Angle());
+ double sa = sin(Angle());
+
+ double w2 = Width() / 2.0;
+ double h2 = Height() / 2.0;
+
+ vset.s_loc[0].x = (float) (loc.x + (-w2*ca - -h2*sa) - 0.5);
+ vset.s_loc[0].y = (float) (loc.y + (-w2*sa + -h2*ca) - 0.5);
+ vset.s_loc[0].z = 0.0f;
+ vset.rw[0] = 1.0f;
+ vset.diffuse[0] = diff;
+
+ vset.s_loc[1].x = (float) (loc.x + ( w2*ca - -h2*sa) - 0.5);
+ vset.s_loc[1].y = (float) (loc.y + ( w2*sa + -h2*ca) - 0.5);
+ vset.s_loc[1].z = 0.0f;
+ vset.rw[1] = 1.0f;
+ vset.diffuse[1] = diff;
+
+ vset.s_loc[2].x = (float) (loc.x + ( w2*ca - h2*sa) - 0.5);
+ vset.s_loc[2].y = (float) (loc.y + ( w2*sa + h2*ca) - 0.5);
+ vset.s_loc[2].z = 0.0f;
+ vset.rw[2] = 1.0f;
+ vset.diffuse[2] = diff;
+
+ vset.s_loc[3].x = (float) (loc.x + (-w2*ca - h2*sa) - 0.5);
+ vset.s_loc[3].y = (float) (loc.y + (-w2*sa + h2*ca) - 0.5);
+ vset.s_loc[3].z = 0.0f;
+ vset.rw[3] = 1.0f;
+ vset.diffuse[3] = diff;
+
+ mtl.Kd = white;
+ mtl.tex_diffuse = Frame();
+ mtl.blend = blend_mode;
+
+ video->DrawScreenPolys(1, &poly, blend_mode);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Sprite::Update()
+{
+ if (life > 0 || loop) {
+ DWORD time = Game::RealTime();
+ while (time - last_time > frame_time) {
+ life--;
+ frame_index++;
+ if (frame_index >= nframes)
+ frame_index = 0;
+
+ last_time += frame_time;
+ }
+
+ if (life < 0 && !loop)
+ life = 0;
+ }
+}
+
diff --git a/nGenEx/Sprite.h b/nGenEx/Sprite.h
new file mode 100644
index 0000000..a40212a
--- /dev/null
+++ b/nGenEx/Sprite.h
@@ -0,0 +1,90 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Sprite.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Sprite Object
+*/
+
+#ifndef Sprite_h
+#define Sprite_h
+
+#include "Types.h"
+#include "Graphic.h"
+#include "Polygon.h"
+
+// +--------------------------------------------------------------------+
+
+class Bitmap;
+
+class Sprite : public Graphic
+{
+public:
+ static const char* TYPENAME() { return "Sprite"; }
+
+ Sprite();
+ Sprite(Bitmap* animation, int length=1, int repeat=1, int share=1);
+ virtual ~Sprite();
+
+ // operations
+ virtual void Render(Video* video, DWORD flags);
+ virtual void Render2D(Video* video);
+ virtual void Update();
+ virtual void Scale(double scale);
+ virtual void Rescale(double scale);
+ virtual void Reshape(int w1, int h1);
+
+ // accessors / mutators
+ int Width() const { return w; }
+ int Height() const { return h; }
+ int Looping() const { return loop; }
+ int NumFrames() const { return nframes; }
+ double FrameRate() const;
+ void SetFrameRate(double rate);
+
+ double Shade() const { return shade; }
+ void SetShade(double s) { shade = s; }
+ double Angle() const { return angle; }
+ void SetAngle(double a) { angle = a; }
+ int BlendMode() const { return blend_mode; }
+ void SetBlendMode(int a) { blend_mode = a; }
+ int Filter() const { return filter; }
+ void SetFilter(int f) { filter = f; }
+ virtual void SetAnimation(Bitmap* animation, int length=1, int repeat=1, int share=1);
+ virtual void SetTexCoords(const double* uv_interleaved);
+
+ Bitmap* Frame() const;
+ void SetFrameIndex(int n);
+
+ virtual bool IsSprite() const { return true; }
+
+protected:
+ int w, h;
+ int loop;
+
+ int nframes;
+ int own_frames;
+ Bitmap* frames;
+ int frame_index;
+ DWORD frame_time;
+ DWORD last_time;
+ double shade;
+ double angle;
+ int blend_mode;
+ int filter;
+
+ Poly poly;
+ Material mtl;
+ VertexSet vset;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif Sprite_h
+
diff --git a/nGenEx/TexCubeDX9.cpp b/nGenEx/TexCubeDX9.cpp
new file mode 100644
index 0000000..47720a4
--- /dev/null
+++ b/nGenEx/TexCubeDX9.cpp
@@ -0,0 +1,120 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: TexDX9.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Direct3D Texture Cache
+*/
+
+#include "MemDebug.h"
+#include "TexCubeDX9.h"
+#include "VideoDX9.h"
+#include "Bitmap.h"
+#include "Color.h"
+
+// +--------------------------------------------------------------------+
+
+void Print(const char* fmt, ...);
+void VideoDX9Error(const char* msg, HRESULT err);
+
+#ifndef RELEASE
+#define RELEASE(x) if (x) { x->Release(); x=NULL; }
+#endif
+
+// +--------------------------------------------------------------------+
+
+TexCubeDX9::TexCubeDX9(VideoDX9* v)
+ : video(v), texture(0)
+{
+ d3d = video->Direct3D();
+ d3ddevice = video->D3DDevice();
+
+ for (int i = 0; i < 6; i++) {
+ faces[i] = 0;
+ last_modified[i] = 0;
+ }
+}
+
+TexCubeDX9::~TexCubeDX9()
+{
+ RELEASE(texture);
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+TexCubeDX9::LoadTexture(Bitmap* bmp, int face_index)
+{
+ if (!d3ddevice) return false;
+
+ if (faces[face_index] == bmp && last_modified[face_index] >= bmp->LastModified())
+ return true; // already loaded and hasn't been modified
+
+ HRESULT hr = D3D_OK;
+
+ // create the texture, if necessary
+ if (!texture) {
+ hr = d3ddevice->CreateCubeTexture(bmp->Width(),
+ 1, // one mip level
+ 0, // no specific usage
+ D3DFMT_A8R8G8B8, // format matching Color::rgba
+ D3DPOOL_MANAGED,
+ &texture,
+ 0);
+
+ if (FAILED(hr) || !texture) {
+ VideoDX9Error("LoadTexture - could not create cube texture", hr);
+ return false;
+ }
+ }
+
+ // lock the surface for writing
+ D3DLOCKED_RECT locked_rect;
+ D3DCUBEMAP_FACES face = (D3DCUBEMAP_FACES) face_index;
+ hr = texture->LockRect(face, 0, &locked_rect, 0, 0);
+
+ if (FAILED(hr)) {
+ VideoDX9Error("LoadTexture - could not lock texture surface", hr);
+ RELEASE(texture);
+ return false;
+ }
+
+ // load the bitmap into the texture surface
+ for (int i = 0; i < bmp->Height(); i++) {
+ BYTE* src = (BYTE*) (bmp->HiPixels() + i * bmp->Width());
+ BYTE* dst = (BYTE*) locked_rect.pBits + i * locked_rect.Pitch;
+
+ CopyMemory(dst, src, bmp->Width() * sizeof(Color));
+ }
+
+ // unlock the surface
+ texture->UnlockRect(face, 0);
+
+ faces[face_index] = bmp;
+ last_modified[face_index] = bmp->LastModified();
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+IDirect3DCubeTexture9*
+TexCubeDX9::GetTexture()
+{
+ if (texture) {
+ // need to refresh anything?
+ for (int i = 0; i < 6; i++) {
+ if (faces[i] && last_modified[i] < faces[i]->LastModified()) {
+ LoadTexture(faces[i], i);
+ }
+ }
+ }
+
+ return texture;
+}
diff --git a/nGenEx/TexCubeDX9.h b/nGenEx/TexCubeDX9.h
new file mode 100644
index 0000000..1dd54cc
--- /dev/null
+++ b/nGenEx/TexCubeDX9.h
@@ -0,0 +1,49 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2006. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: TexCubeDX9.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Direct 3D Texture Cube for Env Mapping
+*/
+
+#ifndef TexCubeDX9_h
+#define TexCubeDX9_h
+
+#include "Bitmap.h"
+
+// +--------------------------------------------------------------------+
+
+class Video;
+class VideoDX9;
+class Bitmap;
+struct VD3D_texture_format;
+
+// +--------------------------------------------------------------------+
+
+class TexCubeDX9
+{
+public:
+ TexCubeDX9(VideoDX9* video);
+ virtual ~TexCubeDX9();
+
+ IDirect3DCubeTexture9* GetTexture();
+ bool LoadTexture(Bitmap* bmp, int face);
+
+private:
+ VideoDX9* video;
+ IDirect3D9* d3d;
+ IDirect3DDevice9* d3ddevice;
+
+ IDirect3DCubeTexture9* texture;
+ Bitmap* faces[6];
+ DWORD last_modified[6];
+};
+
+#endif // TexCubeDX9_h
+
diff --git a/nGenEx/TexDX9.cpp b/nGenEx/TexDX9.cpp
new file mode 100644
index 0000000..de182e1
--- /dev/null
+++ b/nGenEx/TexDX9.cpp
@@ -0,0 +1,418 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: TexDX9.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Direct3D Texture Cache
+*/
+
+#include "MemDebug.h"
+#include "TexDX9.h"
+#include "VideoDX9.h"
+#include "Bitmap.h"
+#include "Color.h"
+
+// +--------------------------------------------------------------------+
+
+void Print(const char* fmt, ...);
+void VideoDX9Error(const char* msg, HRESULT err);
+
+#define TEXDX9_VERBOSE 0
+#define DX9_TEXTURE_CACHE_SIZE 256
+
+#ifndef RELEASE
+#define RELEASE(x) if (x) { x->Release(); x=NULL; }
+#endif
+
+// +--------------------------------------------------------------------+
+
+void
+TexCacheDX9Entry::Release()
+{
+ RELEASE(texture);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TexCacheDX9Entry::Unload()
+{
+ RELEASE(texture);
+ bitmap_id = 0;
+ used_last = 0;
+ last_modified = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+TexCacheDX9::TexCacheDX9(VideoDX9* v)
+ : video(v), count(0), mru(0)
+{
+ d3d = video->Direct3D();
+ d3ddevice = video->D3DDevice();
+
+ // clear the texture cache
+ cache = new(__FILE__,__LINE__) TexCacheDX9Entry[DX9_TEXTURE_CACHE_SIZE];
+ vidmem = video->VidMemFree();
+}
+
+TexCacheDX9::~TexCacheDX9()
+{
+ int used = 0;
+
+ if (cache) {
+ for (int i = 0; i < DX9_TEXTURE_CACHE_SIZE; i++) {
+ if (cache[i].bitmap_id)
+ used++;
+ cache[i].Unload();
+ }
+
+ delete [] cache;
+ cache = 0;
+ }
+
+ Print("TexCacheDX9: final used count = %d\n", used);
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+TexCacheDX9::LoadTexture(Bitmap* bmp, TexCacheDX9Entry* entry)
+{
+ if (!d3ddevice) return false;
+
+ HRESULT hr = D3D_OK;
+
+ // create the texture, if necessary
+ if (!entry->texture || entry->bitmap_id != bmp->Handle()) {
+ hr = d3ddevice->CreateTexture(bmp->Width(),
+ bmp->Height(),
+ 1, // one mip level
+ 0, // no specific usage
+ D3DFMT_A8R8G8B8, // format matching Color::rgba
+ D3DPOOL_MANAGED,
+ &entry->texture,
+ 0);
+
+ if (FAILED(hr) || !entry->texture) {
+ VideoDX9Error("LoadTexture - could not create texture", hr);
+ return false;
+ }
+ }
+
+ // lock the surface for writing
+ D3DLOCKED_RECT locked_rect;
+ hr = entry->texture->LockRect(0, &locked_rect, 0, 0);
+
+ if (FAILED(hr)) {
+ VideoDX9Error("LoadTexture - could not lock texture surface", hr);
+ entry->Unload();
+ return false;
+ }
+
+ // load the bitmap into the texture surface
+ for (int i = 0; i < bmp->Height(); i++) {
+ BYTE* src = (BYTE*) (bmp->HiPixels() + i * bmp->Width());
+ BYTE* dst = (BYTE*) locked_rect.pBits + i * locked_rect.Pitch;
+
+ CopyMemory(dst, src, bmp->Width() * sizeof(Color));
+ }
+
+ // unlock the surface
+ entry->texture->UnlockRect(0);
+
+ entry->last_modified = bmp->LastModified();
+ vidmem = video->VidMemFree();
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TexCacheDX9::CreateNormalMap(int index, float amp)
+{
+ if (d3ddevice && cache[index].texture && !cache[index].normal) {
+ HRESULT hr = D3D_OK;
+ TexCacheDX9Entry* entry = &cache[index];
+ Bitmap* bmp = Bitmap::GetBitmapByID(entry->bitmap_id);
+
+ IDirect3DTexture9* normal_map = 0;
+
+ // create the normal map texture
+ hr = d3ddevice->CreateTexture(bmp->Width(),
+ bmp->Height(),
+ 1, // one mip levels
+ 0, // no specific usage
+ D3DFMT_A8R8G8B8, // format matching Color::rgba
+ D3DPOOL_MANAGED,
+ &normal_map,
+ 0);
+
+ if (FAILED(hr) || !normal_map) {
+ VideoDX9Error("CreateNormalMap - could not create texture", hr);
+ return;
+ }
+
+ D3DXComputeNormalMap(normal_map, entry->texture ,NULL, 0, D3DX_CHANNEL_RED, amp);
+
+ entry->texture->Release();
+ entry->texture = normal_map;
+ entry->normal = true;
+
+ // The D3DX function destroys the alpha channel data
+ // when it builds the normal map. We want the original
+ // height data stored in the alpha channel, so we need
+ // to add it back in now.
+
+ // lock the surface for writing
+ D3DLOCKED_RECT locked_rect;
+ hr = normal_map->LockRect(0, &locked_rect, 0, 0);
+
+ if (FAILED(hr)) {
+ VideoDX9Error("CreateNormalMap - could not insert height channel", hr);
+ return;
+ }
+
+ // load the bitmap into the texture surface
+ for (int i = 0; i < bmp->Height(); i++) {
+ BYTE* src = (BYTE*) (bmp->HiPixels() + i * bmp->Width());
+ BYTE* dst = (BYTE*) locked_rect.pBits + i * locked_rect.Pitch;
+
+ src += 2; // red channel
+ dst += 3; // alpha channel
+
+ for (int n = 0; n < bmp->Width(); n++) {
+ *dst = *src;
+
+ dst += 4;
+ src += 4;
+ }
+ }
+
+ // unlock the surface
+ normal_map->UnlockRect(0);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+IDirect3DTexture9*
+TexCacheDX9::FindTexture(Bitmap* bmp)
+{
+ int avail = -1;
+
+ if (!bmp)
+ return 0;
+
+ // check most recently used:
+ if (cache[mru].bitmap_id == bmp->Handle()) {
+ cache[mru].used_last = frame_number;
+
+ // need to refresh?
+ if (cache[mru].last_modified < bmp->LastModified()) {
+ LoadTexture(bmp, &cache[mru]);
+ cache[mru].normal = false;
+ }
+
+ return cache[mru].texture;
+ }
+
+ // find cache entry, or first free:
+ for (int i = 0; i < DX9_TEXTURE_CACHE_SIZE; i++)
+ if (cache[i].bitmap_id == bmp->Handle()) {
+ cache[i].used_last = frame_number;
+ mru = i;
+
+ // need to refresh?
+ if (cache[mru].last_modified < bmp->LastModified()) {
+ LoadTexture(bmp, &cache[mru]);
+ cache[mru].normal = false;
+ }
+
+ return cache[i].texture;
+ }
+ else if (avail < 0 && cache[i].bitmap_id == 0)
+ avail = i;
+
+ // no free space
+ if (avail < 0)
+ if (FreeUpCache())
+ return FindTexture(bmp);
+ else
+ return 0;
+
+ TexCacheDX9Entry* entry = &cache[avail];
+ entry->bitmap_id = bmp->Handle();
+ entry->used_last = frame_number;
+
+ if (LoadTexture(bmp, entry)) {
+#if TEXDX9_VERBOSE
+ Print(" Tex %3d: id=%2d, size=%3d, type=%d, hTex=%3d, frame=%6d vmf=%8d\n",
+ avail, bmp->Handle(), bmp->Width(), bmp->Type(),
+ cache[avail].texture, cache[avail].used_last, vidmem);
+#endif
+ mru = avail;
+ cache[mru].normal = false;
+ return entry->texture;
+ }
+ else {
+ // failed to load texture,
+ // erase cache entry:
+ entry->Unload();
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+IDirect3DTexture9*
+TexCacheDX9::FindNormalMap(Bitmap* bmp, float amp)
+{
+ int avail = -1;
+
+ if (!bmp)
+ return 0;
+
+ // check most recently used:
+ if (cache[mru].bitmap_id == bmp->Handle()) {
+ cache[mru].used_last = frame_number;
+
+ // need to refresh?
+ if (cache[mru].last_modified < bmp->LastModified()) {
+ LoadTexture(bmp, &cache[mru]);
+ cache[mru].normal = false;
+ }
+
+ if (!cache[mru].normal)
+ CreateNormalMap(mru, amp);
+
+ return cache[mru].texture;
+ }
+
+ // find cache entry, or first free:
+ for (int i = 0; i < DX9_TEXTURE_CACHE_SIZE; i++)
+ if (cache[i].bitmap_id == bmp->Handle()) {
+ cache[i].used_last = frame_number;
+ mru = i;
+
+ // need to refresh?
+ if (cache[i].last_modified < bmp->LastModified()) {
+ LoadTexture(bmp, &cache[mru]);
+ cache[i].normal = false;
+ }
+
+ if (!cache[i].normal)
+ CreateNormalMap(i, amp);
+
+ return cache[i].texture;
+ }
+ else if (avail < 0 && cache[i].bitmap_id == 0)
+ avail = i;
+
+ // no free space
+ if (avail < 0)
+ if (FreeUpCache())
+ return FindTexture(bmp);
+ else
+ return 0;
+
+ TexCacheDX9Entry* entry = &cache[avail];
+ entry->bitmap_id = bmp->Handle();
+ entry->used_last = frame_number;
+
+ if (LoadTexture(bmp, entry)) {
+#if TEXDX9_VERBOSE
+ Print(" Tex %3d: id=%2d, size=%3d, type=%d, hTex=%3d, frame=%6d vmf=%8d\n",
+ avail, bmp->Handle(), bmp->Width(), bmp->Type(),
+ cache[avail].texture, cache[avail].used_last, vidmem);
+#endif
+ mru = avail;
+ CreateNormalMap(mru, amp);
+ return entry->texture;
+ }
+ else {
+ // failed to load texture,
+ // erase cache entry:
+ entry->Unload();
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+TexCacheDX9::FreeLRU(int tries)
+{
+ int unloaded = 0;
+
+ while (tries--) {
+ int oldest = -1;
+ DWORD old = frame_number;
+
+ for (int i = 0; i < DX9_TEXTURE_CACHE_SIZE; i++) {
+ DWORD ul = cache[i].used_last;
+
+ if (ul && ul < old && ul != frame_number) {
+ old = ul;
+ oldest = i;
+ }
+ }
+
+ if (oldest >= 0) {
+ cache[oldest].Unload();
+ unloaded++;
+ }
+ else
+ break;
+ }
+
+ vidmem = video->VidMemFree();
+
+#if TEXDX9_VERBOSE
+ Print(" FreeLRU() frame=%6d unloaded=%2d vmf=%8d\n", frame_number, unloaded, vidmem);
+#endif
+
+ return unloaded;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+TexCacheDX9::FreeUpCache()
+{
+ int unloaded = 0;
+
+ for (int i = 0; i < DX9_TEXTURE_CACHE_SIZE; i++) {
+ if (cache[i].used_last && cache[i].used_last < frame_number) {
+ cache[i].Unload();
+ unloaded++;
+ }
+ }
+
+ vidmem = video->VidMemFree();
+
+ Print(" FreeUpCache() frame=%6d unloaded=%2d vmf=%8d\n", frame_number, unloaded, vidmem);
+
+ return unloaded;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+TexCacheDX9::InvalidateCache()
+{
+ for (int i = 0; i < DX9_TEXTURE_CACHE_SIZE; i++) {
+ cache[i].Unload();
+ cache[i].normal = false;
+ }
+
+ vidmem = video->VidMemFree();
+}
diff --git a/nGenEx/TexDX9.h b/nGenEx/TexDX9.h
new file mode 100644
index 0000000..88c6357
--- /dev/null
+++ b/nGenEx/TexDX9.h
@@ -0,0 +1,79 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: TexDX9.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Direct 3D Texture Cache
+*/
+
+#ifndef TexDX9_h
+#define TexDX9_h
+
+#include "Bitmap.h"
+
+// +--------------------------------------------------------------------+
+
+class Video;
+class VideoDX9;
+class Bitmap;
+struct VD3D_texture_format;
+
+// +--------------------------------------------------------------------+
+
+struct TexCacheDX9Entry
+{
+ TexCacheDX9Entry() : bitmap_id(0), texture(0), used_last(0),
+ last_modified(0), normal(false) { }
+
+ void Release();
+ void Unload();
+
+ HANDLE bitmap_id;
+ IDirect3DTexture9* texture;
+ DWORD used_last;
+ DWORD last_modified;
+ bool normal;
+};
+
+// +--------------------------------------------------------------------+
+
+class TexCacheDX9
+{
+public:
+ TexCacheDX9(VideoDX9* video);
+ virtual ~TexCacheDX9();
+
+ void FrameNumber(int n) { frame_number = n; }
+ IDirect3DTexture9* FindTexture(Bitmap* bmp);
+ IDirect3DTexture9* FindNormalMap(Bitmap* bmp, float amp=1);
+ bool LoadTexture(Bitmap* bmp, TexCacheDX9Entry* entry);
+ void InvalidateCache();
+
+ int count;
+
+private:
+ int FreeLRU(int tries=4);
+ int FreeUpCache();
+ void CreateNormalMap(int index, float amp=1);
+
+ VideoDX9* video;
+ IDirect3D9* d3d;
+ IDirect3DDevice9* d3ddevice;
+
+ DWORD vidmem;
+
+ int bad_frame;
+ DWORD frame_number;
+
+ int mru;
+ TexCacheDX9Entry* cache;
+};
+
+#endif // TexDX9_h
+
diff --git a/nGenEx/TimeSnap.h b/nGenEx/TimeSnap.h
new file mode 100644
index 0000000..0f5d2ae
--- /dev/null
+++ b/nGenEx/TimeSnap.h
@@ -0,0 +1,26 @@
+/* Project nGen
+ John DiCamillo
+ Copyright © 1997-2002. All Rights Reserved.
+
+ SUBSYSTEM: nGen.lib
+ FILE: TimeSnap.h
+ AUTHOR: John DiCamillo
+
+*/
+
+#ifndef TimeSnap_h
+#define TimeSnap_h
+
+// +--------------------------------------------------------------------+
+
+#define TIMESNAP(clock) _asm push eax\
+ _asm push edx\
+ _asm _emit 0x0F\
+ _asm _emit 0x31\
+ _asm mov clock, eax\
+ _asm pop edx\
+ _asm pop eax
+
+// +--------------------------------------------------------------------+
+
+#endif TimeSnap_h \ No newline at end of file
diff --git a/nGenEx/Types.h b/nGenEx/Types.h
new file mode 100644
index 0000000..cd695e1
--- /dev/null
+++ b/nGenEx/Types.h
@@ -0,0 +1,66 @@
+/* Project nGen
+ John DiCamillo
+ Copyright © 1997-2002. All Rights Reserved.
+
+ SUBSYSTEM: nGen.lib
+ FILE: Types.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Basic Type Definitions
+*/
+
+#ifndef Types_h
+#define Types_h
+
+// +--------------------------------------------------------------------+
+
+#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers
+#define STRICT 1
+
+// Works with Windows 2000 and later and Windows 98 or later
+#undef _WIN32_IE
+#undef WINVER
+#undef _WIN32_WINDOWS
+#undef _WIN32_WINNT
+#define WINVER 0x0500
+#define _WIN32_WINDOWS 0x0410
+#define _WIN32_WINNT 0x0500
+
+#if !defined(HMONITOR_DECLARED)
+ #define HMONITOR_DECLARED
+ DECLARE_HANDLE(HMONITOR);
+#endif
+
+#include <windows.h>
+#include <windowsx.h>
+#include <assert.h>
+#include <math.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+
+
+// Enable extra D3D debugging in debug builds if using the debug DirectX runtime.
+// This makes D3D objects work well in the debugger watch window, but slows down
+// performance slightly.
+#if defined(DEBUG) | defined(_DEBUG)
+#define D3D_DEBUG_INFO
+#endif
+
+// Direct3D includes
+#include <d3d9.h>
+#include <d3dx9.h>
+
+// DirectSound includes
+#include <mmsystem.h>
+#include <mmreg.h>
+#include <dsound.h>
+
+// +--------------------------------------------------------------------+
+
+#endif Types_h
+
diff --git a/nGenEx/Universe.h b/nGenEx/Universe.h
new file mode 100644
index 0000000..d599586
--- /dev/null
+++ b/nGenEx/Universe.h
@@ -0,0 +1,32 @@
+/* Project nGen
+ John DiCamillo
+ Copyright © 1997-2002. All Rights Reserved.
+
+ SUBSYSTEM: nGen.lib
+ FILE: Universe.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Abstract Universe class
+*/
+
+#ifndef Universe_h
+#define Universe_h
+
+#include "Types.h"
+
+// +--------------------------------------------------------------------+
+
+class Universe
+{
+public:
+ Universe() { }
+ virtual ~Universe() { }
+
+ virtual void ExecFrame(double seconds) { }
+};
+
+#endif Universe_h
+
diff --git a/nGenEx/Video.cpp b/nGenEx/Video.cpp
new file mode 100644
index 0000000..ee4b31f
--- /dev/null
+++ b/nGenEx/Video.cpp
@@ -0,0 +1,65 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Video.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Abstract Video Interface (singleton definition)
+*/
+
+#include "MemDebug.h"
+#include "Video.h"
+#include "VideoSettings.h"
+
+// +--------------------------------------------------------------------+
+
+Video* Video::video_instance = 0;
+
+// +--------------------------------------------------------------------+
+
+Video::Video()
+{
+ status = VIDEO_OK;
+ video_instance = this;
+
+ shadow_enabled = true;
+ bump_enabled = true;
+ spec_enabled = true;
+
+ camera = 0;
+}
+
+Video::~Video()
+{
+ if (video_instance == this)
+ video_instance = 0;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Video::IsWindowed() const
+{
+ const VideoSettings* vs = GetVideoSettings();
+
+ if (vs)
+ return vs->IsWindowed();
+
+ return false;
+}
+
+bool
+Video::IsFullScreen() const
+{
+ const VideoSettings* vs = GetVideoSettings();
+
+ if (vs)
+ return !vs->IsWindowed();
+
+ return true;
+}
diff --git a/nGenEx/Video.h b/nGenEx/Video.h
new file mode 100644
index 0000000..393a704
--- /dev/null
+++ b/nGenEx/Video.h
@@ -0,0 +1,242 @@
+/* Project nGen
+ John DiCamillo
+ Copyright © 1997-2002. All Rights Reserved.
+
+ SUBSYSTEM: nGen.lib
+ FILE: Video.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Abstract Video Interface
+*/
+
+#ifndef Video_h
+#define Video_h
+
+#include "Geometry.h"
+#include "Color.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class Video;
+struct VideoMode;
+class VideoSettings;
+class VideoPrivateData;
+
+class Bitmap;
+class Camera;
+struct Rect;
+struct Poly;
+struct Material;
+struct VertexSet;
+class Light;
+class Solid;
+class Surface;
+
+// +--------------------------------------------------------------------+
+
+struct RenderStats
+{
+ int nframe;
+ int nverts;
+ int npolys;
+ int nlines;
+
+ int ncalls;
+
+ int total_verts;
+ int total_polys;
+ int total_lines;
+
+ void Clear() { nverts = npolys = nlines = ncalls =
+ total_verts = total_polys = total_lines = 0; }
+};
+
+// +--------------------------------------------------------------------+
+
+class Video
+{
+public:
+ enum STATUS { VIDEO_OK, VIDEO_ERR, VIDEO_BAD_PARM };
+
+ enum RENDER_STATE {
+ FILL_MODE,
+ SHADE_MODE,
+ LIGHTING_ENABLE,
+ Z_ENABLE,
+ Z_WRITE_ENABLE,
+ Z_BIAS,
+ TEXTURE_FILTER,
+ DITHER_ENABLE,
+ SPECULAR_ENABLE,
+ FOG_ENABLE,
+ FOG_COLOR,
+ FOG_DENSITY,
+ STENCIL_ENABLE,
+ TEXTURE_WRAP,
+ LIGHTING_PASS,
+
+ RENDER_STATE_MAX
+ };
+
+ enum BLEND_TYPE {
+ BLEND_SOLID = 1,
+ BLEND_ALPHA = 2,
+ BLEND_ADDITIVE = 4,
+ BLEND_FORCE_DWORD = 0x7fffffff,
+ };
+
+ enum SHADE_TYPE {
+ SHADE_FLAT = 1,
+ SHADE_GOURAUD = 2,
+ SHADE_PHONG = 3,
+ SHADE_FORCE_DWORD = 0x7fffffff,
+ };
+
+ enum FILL_TYPE {
+ FILL_POINT = 1,
+ FILL_WIREFRAME = 2,
+ FILL_SOLID = 3,
+ FILL_FORCE_DWORD = 0x7fffffff,
+ };
+
+ enum FILTER_TYPE {
+ FILTER_NONE = 1,
+ FILTER_LINEAR = 2,
+ FILTER_MIPMAP = 3,
+ FILTER_MIPLINEAR = 4,
+ FILTER_TRILINEAR = 6,
+ FILTER_FORCE_DWORD = 0x7fffffff,
+ };
+
+ enum PROJECTION_TYPE {
+ PROJECTION_PERSPECTIVE = 1,
+ PROJECTION_ORTHOGONAL = 2,
+ PROJECTION_FORCE_DWORD = 0x7fffffff,
+ };
+
+ Video();
+ virtual ~Video();
+
+ STATUS Status() const { return status; }
+ virtual const VideoSettings*
+ GetVideoSettings() const { return 0; }
+ virtual bool SetVideoSettings(const VideoSettings* vs) { return false; }
+ virtual bool Reset(const VideoSettings* vs) { return false; }
+
+ virtual bool SetBackgroundColor(Color c) { return false; }
+ virtual bool SetGammaLevel(int g) { return true; }
+ virtual bool SetObjTransform(const Matrix& o, const Point& l){ return false; }
+
+ virtual int Width() const { return 0; }
+ virtual int Height() const { return 0; }
+ virtual int Depth() const { return 0; }
+
+ virtual void RecoverSurfaces() { }
+
+ virtual bool ClearAll() { return false; }
+ virtual bool ClearDepthBuffer() { return false; }
+ virtual bool Present() { return false; }
+ virtual bool Pause() { return false; }
+ virtual bool Resume() { return false; }
+
+ virtual bool IsWindowed() const;
+ virtual bool IsFullScreen() const;
+ virtual bool IsModeSupported(int width, int height, int bpp)
+ const { return true; }
+ virtual bool IsHardware() const { return false; }
+ virtual bool IsHardwareTL() const { return false; }
+ virtual int ZDepth() const { return 0; }
+ virtual DWORD VidMemFree() const { return 0; }
+ virtual int D3DLevel() const { return 0; }
+ virtual int MaxTexSize() const { return 256; }
+ virtual int MaxTexAspect() const { return 0; }
+ virtual int GammaLevel() const { return 190; }
+
+ virtual bool IsShadowEnabled() const { return shadow_enabled; }
+ virtual bool IsBumpMapEnabled() const { return bump_enabled; }
+ virtual bool IsSpecMapEnabled() const { return spec_enabled; }
+
+ virtual void SetShadowEnabled(bool e) { shadow_enabled = e; }
+ virtual void SetBumpMapEnabled(bool e) { bump_enabled = e; }
+ virtual void SetSpecMapEnabled(bool e) { spec_enabled = e; }
+
+ virtual bool Capture(Bitmap& bmp) { return false; }
+ virtual bool GetWindowRect(Rect& r) { return false; }
+ virtual bool SetWindowRect(const Rect& r) { return false; }
+ virtual bool SetViewport(int x, int y, int w, int h) { return false; }
+ virtual bool SetCamera(const Camera* cam) { camera = cam;
+ return false; }
+ virtual bool SetProjection(float fov,
+ float znear=1.0f,
+ float zfar=1.0e6f,
+ DWORD type=PROJECTION_PERSPECTIVE) { return false; }
+ virtual bool SetEnvironment(Bitmap** faces) { return false; }
+ virtual bool SetAmbient(Color c) { return false; }
+ virtual bool SetLights(const List<Light>& lights) { return false; }
+ virtual bool SetRenderState(RENDER_STATE state, DWORD value) { return false; }
+ virtual bool SetBlendType(int blend_type) { return false; }
+ virtual bool StartFrame() { return false; }
+ virtual bool EndFrame() { return false; }
+
+ virtual bool DrawPolys(int npolys, Poly* p) { return false; }
+ virtual bool DrawScreenPolys(int npolys, Poly* p, int blend=0) { return false; }
+ virtual bool DrawSolid(Solid* s, DWORD blend_modes=0xf) { return false; }
+ virtual bool DrawShadow(Solid* s, int nverts, Vec3* verts, bool vis=false)
+ { return false; }
+ virtual bool DrawLines(int nlines, Vec3* v, Color c, int blend=0) { return false; }
+ virtual bool DrawScreenLines(int nlines, float* v, Color c, int blend=0)
+ { return false; }
+ virtual bool DrawPoints(VertexSet* v) { return false; }
+ virtual bool DrawPolyOutline(Poly* p) { return false; }
+ virtual bool UseMaterial(Material* m) { return false; }
+
+ virtual bool UseXFont(const char* name, int size, bool b, bool i) { return false; }
+ virtual bool DrawText(const char* text, int count, const Rect& rect,
+ DWORD format, Color c) { return false; }
+
+ virtual void PreloadTexture(Bitmap* bmp) { }
+ virtual void PreloadSurface(Surface* s) { }
+ virtual void InvalidateCache() { }
+
+ const Camera* GetCamera() const { return camera; }
+ const RenderStats& GetStats() const { return stats; }
+ static Video* GetInstance() { return video_instance; }
+
+protected:
+ STATUS status;
+ RenderStats stats;
+ const Camera* camera;
+
+ bool shadow_enabled;
+ bool bump_enabled;
+ bool spec_enabled;
+
+ static Video* video_instance;
+};
+
+// +--------------------------------------------------------------------+
+
+class VideoPrivateData
+{
+public:
+ VideoPrivateData() : valid(false) { }
+ virtual ~VideoPrivateData() { }
+
+ virtual int GetType() const { return 0; }
+
+ virtual bool IsValid() const { return valid; }
+ virtual void Invalidate() { valid = false; }
+ virtual void Validate() { valid = true; }
+
+protected:
+ bool valid;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif Video_h
+
diff --git a/nGenEx/VideoDX9.cpp b/nGenEx/VideoDX9.cpp
new file mode 100644
index 0000000..a818a2d
--- /dev/null
+++ b/nGenEx/VideoDX9.cpp
@@ -0,0 +1,3675 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: VideoDX9.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Direct3D Video class for DirectX 9
+*/
+
+#include "MemDebug.h"
+#include "VideoDX9.h"
+#include "VideoDX9Enum.h"
+#include "VideoDX9VertexBuffer.h"
+#include "TexDX9.h"
+#include "TexCubeDX9.h"
+#include "Camera.h"
+#include "Color.h"
+#include "DataLoader.h"
+#include "Polygon.h"
+#include "Light.h"
+#include "Solid.h"
+
+// +--------------------------------------------------------------------+
+
+void Print(const char* msg, ...);
+char* D3DErrStr(HRESULT dderr);
+void VideoDX9Error(const char* msg, HRESULT dderr);
+static TexCacheDX9* texcache = 0;
+static TexCubeDX9* environment_cube = 0;
+static bool surface_has_tangent_data = false;
+static Light* main_light;
+static Light* back_light;
+static D3DXMATRIX matrixWorld;
+static D3DXMATRIX matrixView;
+static D3DXMATRIX matrixProj;
+static D3DXMATRIX matrixWorldInverse;
+
+extern int VD3D_describe_things;
+
+#ifndef RELEASE
+#define RELEASE(x) if (x) { x->Release(); x=NULL; }
+#endif
+
+#ifndef F2DW
+#define F2DW(x) (*(DWORD*)(&x))
+#endif
+
+#ifndef DW2I
+#define DW2I(x) (*(int*)(&x))
+#endif
+
+// +--------------------------------------------------------------------+
+
+typedef HRESULT (WINAPI * LPDDCE)(GUID FAR *, LPVOID *, REFIID , IUnknown FAR *);
+
+static D3DMATRIX identity_matrix = {
+ FLOAT(1.0), FLOAT(0.0), FLOAT(0.0), FLOAT(0.0),
+ FLOAT(0.0), FLOAT(1.0), FLOAT(0.0), FLOAT(0.0),
+ FLOAT(0.0), FLOAT(0.0), FLOAT(1.0), FLOAT(0.0),
+ FLOAT(0.0), FLOAT(0.0), FLOAT(0.0), FLOAT(1.0)
+};
+
+// +--------------------------------------------------------------------+
+
+List<Model> model_clients;
+
+class VideoDX9SurfaceData : public VideoPrivateData
+{
+public:
+ VideoDX9SurfaceData(Model* m) : model(m), vertex_buffer(0), index_buffer(0) {
+ if (!model_clients.contains(model))
+ model_clients.append(model);
+ }
+
+ virtual ~VideoDX9SurfaceData() {
+ model_clients.remove(model);
+
+ delete vertex_buffer;
+ delete index_buffer;
+ }
+
+ enum { TYPE = 9001 };
+ virtual int GetType() const { return TYPE; }
+
+ Model* model;
+ VideoDX9VertexBuffer* vertex_buffer;
+ VideoDX9IndexBuffer* index_buffer;
+};
+
+class VideoDX9SegmentData : public VideoPrivateData
+{
+public:
+ VideoDX9SegmentData() : first_vert(0), num_verts(0), first_index(0), num_tris(0) { }
+ virtual ~VideoDX9SegmentData() { }
+
+ enum { TYPE = 9002 };
+ virtual int GetType() const { return TYPE; }
+
+ int first_vert;
+ int num_verts;
+ int first_index;
+ int num_tris;
+};
+
+// +--------------------------------------------------------------------+
+
+static int d3dstate_table[] = {
+ D3DRS_FILLMODE, // FILL_MODE
+ D3DRS_SHADEMODE, // SHADE_MODE
+ D3DRS_LIGHTING, // LIGHTING_ENABLE
+ D3DRS_ZENABLE, // Z_ENABLE
+ D3DRS_ZWRITEENABLE, // Z_WRITE_ENABLE
+ D3DRS_DEPTHBIAS, // Z_BIAS
+ 0, // TEXTURE_FILTER
+ D3DRS_DITHERENABLE, // DITHER_ENABLE
+ D3DRS_SPECULARENABLE, // SPECULAR_ENABLE
+ D3DRS_FOGENABLE, // FOG_ENABLE
+ D3DRS_FOGCOLOR, // FOG_COLOR
+ D3DRS_FOGDENSITY, // FOG_DENSITY
+ D3DRS_STENCILENABLE, // STENCIL_ENABLE
+ 0x11111111, // TEXTURE_WRAP (special case)
+ 0 // LIGHTING_PASS
+};
+
+static const int NUM_SCREEN_VERTS = 1024;
+static const int NUM_SCREEN_INDICES = NUM_SCREEN_VERTS * 2;
+
+// +--------------------------------------------------------------------+
+
+struct VideoDX9ScreenVertex
+{
+ FLOAT sx, sy, sz, rhw;
+ DWORD diffuse;
+ FLOAT tu, tv;
+
+ static DWORD FVF;
+};
+
+DWORD VideoDX9ScreenVertex::FVF = D3DFVF_XYZRHW |
+ D3DFVF_DIFFUSE |
+ D3DFVF_TEX1;
+
+struct VideoDX9NormalVertex
+{
+ FLOAT x, y, z;
+ FLOAT nx, ny, nz;
+ FLOAT t0u, t0v;
+ FLOAT t1u, t1v;
+ FLOAT tx, ty, tz;
+ FLOAT bx, by, bz;
+
+ static DWORD FVF;
+};
+
+DWORD VideoDX9NormalVertex::FVF = 0;
+
+// Global Vertex Declaration shared by shaders
+D3DVERTEXELEMENT9 videoDX9NormalVertexElements[] =
+{
+ { 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
+ { 0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0 },
+ { 0, 24, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 },
+ { 0, 32, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 1 },
+ { 0, 40, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TANGENT, 2 },
+ { 0, 52, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BINORMAL, 3 },
+ D3DDECL_END()
+};
+
+struct VideoDX9SolidVertex
+{
+ FLOAT x, y, z;
+ FLOAT nx, ny, nz;
+ FLOAT tu, tv;
+
+ static DWORD FVF;
+};
+
+DWORD VideoDX9SolidVertex::FVF = D3DFVF_XYZ |
+ D3DFVF_NORMAL |
+ D3DFVF_TEX1 |
+ D3DFVF_TEXCOORDSIZE2(0);
+
+struct VideoDX9LuminousVertex
+{
+ FLOAT x, y, z;
+ DWORD diffuse;
+ FLOAT tu, tv;
+
+ static DWORD FVF;
+};
+
+DWORD VideoDX9LuminousVertex::FVF = D3DFVF_XYZ |
+ D3DFVF_DIFFUSE |
+ D3DFVF_TEX1 |
+ D3DFVF_TEXCOORDSIZE2(0);
+
+struct VideoDX9DetailVertex
+{
+ FLOAT x, y, z;
+ DWORD diffuse;
+ DWORD specular;
+ FLOAT tu, tv;
+ FLOAT tu1, tv1;
+
+ static DWORD FVF;
+};
+
+DWORD VideoDX9DetailVertex::FVF = D3DFVF_XYZ |
+ D3DFVF_DIFFUSE |
+ D3DFVF_SPECULAR |
+ D3DFVF_TEX2;
+
+struct VideoDX9LineVertex
+{
+ FLOAT x, y, z;
+ DWORD diffuse;
+
+ static DWORD FVF;
+};
+
+DWORD VideoDX9LineVertex::FVF = D3DFVF_XYZ |
+ D3DFVF_DIFFUSE;
+
+enum {
+ DX9_STRATEGY_NONE,
+ DX9_STRATEGY_SIMPLE,
+ DX9_STRATEGY_GLOW,
+ DX9_STRATEGY_SPECMAP,
+ DX9_STRATEGY_EMISSIVE,
+ DX9_STRATEGY_SPEC_EMISSIVE,
+ DX9_STRATEGY_BLEND,
+ DX9_STRATEGY_BLEND_DETAIL
+};
+
+// +--------------------------------------------------------------------+
+
+static VideoDX9* video_dx9_instance = 0;
+
+VideoDX9::VideoDX9(const HWND& window, VideoSettings* vs)
+ : width(0), height(0), bpp(0), hwnd(window), surface(0),
+ d3d(0), d3ddevice(0), device_lost(false), fade(0),
+ zdepth(0), gamma(128), num_verts(0), first_vert(0),
+ current_texture(0), screen_vbuf(0), screen_ibuf(0),
+ font_verts(0), font_indices(0), font_nverts(0),
+ nlights(0), use_material(0), d3dx_font(0),
+ segment_material(0), strategy(0), passes(0),
+ screen_line_verts(0), line_verts(0),
+ vertex_declaration(0),
+ magic_fx(0), magic_fx_code(0), magic_fx_code_len(0)
+{
+ video_dx9_instance = this;
+
+ Print("\n********************************\n");
+ Print("* Direct 3D version 9 *\n");
+ Print("********************************\n\n");
+
+ status = VIDEO_ERR;
+ HRESULT err = E_OUTOFMEMORY;
+
+ d3d = Direct3DCreate9(D3D_SDK_VERSION);
+ dx9enum = new(__FILE__,__LINE__) VideoDX9Enum(d3d);
+
+ if (d3d && dx9enum) {
+ if (vs) {
+ dx9enum->req_fullscreen = vs->is_windowed ? false : true;
+ dx9enum->req_windowed = vs->is_windowed ? true : false;
+ dx9enum->min_stencil_bits = vs->shadows ? 8 : 0;
+ dx9enum->uses_depth_buffer = true;
+ }
+ else {
+ dx9enum->req_fullscreen = video_settings.is_windowed ? false : true;
+ dx9enum->req_windowed = video_settings.is_windowed ? true : false;
+ dx9enum->min_stencil_bits = video_settings.shadows ? 8 : 0;
+ dx9enum->uses_depth_buffer = true;
+ }
+
+ err = dx9enum->Enumerate();
+
+ if (FAILED(err)) {
+ VideoDX9Error("(ctor) could not enumerate dx9 properties", err);
+ delete dx9enum;
+ return;
+ }
+ }
+ else {
+ VideoDX9Error("(ctor) could not create enumerator", err);
+ return;
+ }
+
+ SetVideoSettings(vs);
+
+ if (video_settings.is_windowed)
+ dx9enum->SuggestWindowSettings(&video_settings);
+ else
+ dx9enum->SuggestFullscreenSettings(&video_settings);
+
+ SetupParams();
+
+ if (VD3D_describe_things > 2) {
+ Print("\nD3DPRESENT_PARAMETERS:\n");
+ Print(" BackBufferWidth: %d\n", d3dparams.BackBufferWidth);
+ Print(" BackBufferHeight: %d\n", d3dparams.BackBufferHeight);
+ Print(" BackBufferCount: %d\n", d3dparams.BackBufferCount);
+ Print(" BackBufferFormat: %s\n", VideoDX9DisplayMode::D3DFormatToString(d3dparams.BackBufferFormat));
+ Print(" Multisample Type: %d\n", d3dparams.MultiSampleType);
+ Print(" Multisample Qual: %d\n", d3dparams.MultiSampleQuality);
+ Print(" Swap Effect: %d\n", d3dparams.SwapEffect);
+ Print(" Device Window: %08X\n", d3dparams.hDeviceWindow);
+ Print(" Windowed: %s\n", d3dparams.Windowed ? "true" : "false");
+ Print(" Enable Depth/Stencil: %s\n", d3dparams.EnableAutoDepthStencil ? "true" : "false");
+ Print(" Depth/Stencil Format: %s\n", VideoDX9DisplayMode::D3DFormatToString(d3dparams.AutoDepthStencilFormat));
+ Print(" Flags: %08X\n", d3dparams.Flags);
+ Print(" Fullscreen Refresh: %d Hz\n", d3dparams.FullScreen_RefreshRateInHz);
+
+ switch (d3dparams.PresentationInterval) {
+ case D3DPRESENT_INTERVAL_IMMEDIATE:
+ Print(" Present Interval: IMMEDIATE\n");
+ break;
+
+ case D3DPRESENT_INTERVAL_DEFAULT:
+ Print(" Present Interval: DEFAULT\n");
+ break;
+
+ case D3DPRESENT_INTERVAL_ONE:
+ Print(" Present Interval: ONE\n");
+ break;
+
+ case D3DPRESENT_INTERVAL_TWO:
+ Print(" Present Interval: TWO\n");
+ break;
+
+ case D3DPRESENT_INTERVAL_THREE:
+ Print(" Present Interval: THREE\n");
+ break;
+
+ case D3DPRESENT_INTERVAL_FOUR:
+ Print(" Present Interval: FOUR\n");
+ break;
+
+ default:
+ Print(" Present Interval: Unknown (%d)\n", d3dparams.PresentationInterval);
+ break;
+ }
+
+ Print("\n");
+ }
+
+ Print(" Creating Video Device for HWND = %08x\n", window);
+
+ err = d3d->CreateDevice(D3DADAPTER_DEFAULT,
+ D3DDEVTYPE_HAL,
+ window,
+ D3DCREATE_HARDWARE_VERTEXPROCESSING,
+ &d3dparams,
+ &d3ddevice);
+
+ if (FAILED(err)) {
+ VideoDX9Error("(ctor) could not create device", err);
+ return;
+ }
+
+ width = video_settings.GetWidth();
+ height = video_settings.GetHeight();
+ bpp = video_settings.GetDepth();
+
+ shadow_enabled = vs->shadows;
+ bump_enabled = vs->bumpmaps;
+ spec_enabled = vs->specmaps;
+
+ render_state[FILL_MODE] = FILL_SOLID;
+ render_state[SHADE_MODE] = SHADE_GOURAUD;
+ render_state[Z_ENABLE] = false;
+ render_state[Z_WRITE_ENABLE] = false;
+ render_state[Z_BIAS] = 0;
+ render_state[TEXTURE_FILTER] = FILTER_LINEAR;
+ render_state[DITHER_ENABLE] = false;
+ render_state[SPECULAR_ENABLE] = true;
+ render_state[FOG_ENABLE] = false;
+ render_state[FOG_COLOR] = 0;
+ render_state[FOG_DENSITY] = 0;
+ render_state[STENCIL_ENABLE] = false;
+ render_state[TEXTURE_WRAP] = true;
+ render_state[LIGHTING_PASS] = 0;
+
+ ZeroMemory(&rect, sizeof(rect));
+
+ if (!texcache)
+ texcache = new(__FILE__,__LINE__) TexCacheDX9(this);
+
+ if (texcache)
+ texcache->count++;
+
+ if (VD3D_describe_things > 0) {
+ DWORD vmf = VidMemFree() / (1024 * 1024);
+ Print(" Available Texture Memory: %d MB\n\n", vmf);
+ }
+
+ if (CreateBuffers()) {
+ d3ddevice->SetRenderState(D3DRS_ALPHATESTENABLE, false);
+ d3ddevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);
+
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
+ d3ddevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_NONE);
+
+ status = VIDEO_OK;
+ }
+
+ ZeroMemory(font_name, 64);
+ font_size = 0;
+ font_bold = false;
+ font_ital = false;
+}
+
+// +--------------------------------------------------------------------+
+
+VideoDX9::~VideoDX9()
+{
+ DestroyBuffers();
+
+ texcache->count--;
+ if (!texcache->count) {
+ delete texcache;
+ texcache = 0;
+ }
+
+ delete environment_cube;
+ delete dx9enum;
+
+ RELEASE(d3dx_font);
+ RELEASE(d3ddevice);
+ RELEASE(d3d);
+
+ if (magic_fx_code)
+ delete [] magic_fx_code;
+
+ Print(" VideoDX9: shutdown\n");
+ video_dx9_instance = 0;
+}
+
+IDirect3DDevice9*
+VideoDX9::GetD3DDevice9()
+{
+ if (video_dx9_instance)
+ return video_dx9_instance->d3ddevice;
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::SetupParams()
+{
+ if (!dx9enum || dx9enum->NumAdapters() < 1) {
+ status = VIDEO_ERR;
+ return false;
+ }
+
+ int adapter_index = video_settings.GetAdapterIndex();
+
+ if (adapter_index < 0 || adapter_index >= dx9enum->NumAdapters()) {
+ ::Print("WARNING: VideoDX9 could not select adapter %d (max=%d)\n",
+ adapter_index, dx9enum->NumAdapters());
+
+ adapter_index = 0;
+ }
+
+ dx9enum->SelectAdapter(adapter_index);
+
+ d3dparams.Windowed = video_settings.IsWindowed();
+ d3dparams.BackBufferCount = 2;
+ d3dparams.MultiSampleType = D3DMULTISAMPLE_NONE;
+ d3dparams.MultiSampleQuality = 0;
+ d3dparams.SwapEffect = D3DSWAPEFFECT_DISCARD;
+ d3dparams.EnableAutoDepthStencil = dx9enum->uses_depth_buffer;
+ d3dparams.hDeviceWindow = hwnd;
+
+ if (dx9enum->uses_depth_buffer) {
+ d3dparams.Flags = D3DPRESENTFLAG_DISCARD_DEPTHSTENCIL;
+ d3dparams.AutoDepthStencilFormat = (D3DFORMAT) video_settings.GetDepthStencilFormat();
+ }
+ else {
+ d3dparams.Flags = 0;
+ }
+
+ d3dparams.Flags |= D3DPRESENTFLAG_DEVICECLIP;
+
+ if (video_settings.IsWindowed()) {
+ d3dparams.BackBufferWidth = video_settings.window_width;
+ d3dparams.BackBufferHeight = video_settings.window_height;
+ d3dparams.BackBufferFormat = (D3DFORMAT) video_settings.GetBackBufferFormat();
+ d3dparams.FullScreen_RefreshRateInHz = 0;
+ d3dparams.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
+ }
+ else {
+ d3dparams.BackBufferWidth = video_settings.GetWidth();
+ d3dparams.BackBufferHeight = video_settings.GetHeight();
+ d3dparams.BackBufferFormat = (D3DFORMAT) video_settings.GetBackBufferFormat();
+ d3dparams.FullScreen_RefreshRateInHz = video_settings.GetRefreshRate();
+ d3dparams.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
+ }
+
+ return true;
+}
+
+
+bool
+VideoDX9::IsModeSupported(int w, int h, int b) const
+{
+ if (dx9enum)
+ return dx9enum->IsModeSupported(w, h, b);
+
+ return false;
+}
+
+bool
+VideoDX9::SetVideoSettings(const VideoSettings* vs)
+{
+ // custom video settings:
+ if (vs) {
+ if (vs != &video_settings)
+ CopyMemory(&video_settings, vs, sizeof(VideoSettings));
+ }
+
+ // default video settings:
+ else {
+ ZeroMemory(&video_settings, sizeof(VideoSettings));
+
+ video_settings.fullscreen_mode.width = 800;
+ video_settings.fullscreen_mode.height = 600;
+ video_settings.fullscreen_mode.format = VideoMode::FMT_X8R8G8B8;
+ }
+
+ return true;
+}
+
+bool
+VideoDX9::Reset(const VideoSettings* vs)
+{
+ if (!d3ddevice || !SetVideoSettings(vs)) {
+ status = VIDEO_ERR;
+ return false;
+ }
+
+ bool using_x_font = (d3dx_font != 0);
+
+ RELEASE(d3dx_font);
+ InvalidateCache();
+ DestroyBuffers();
+ SetupParams();
+
+ HRESULT hr = d3ddevice->Reset(&d3dparams);
+
+ if (FAILED(hr)) {
+ VideoDX9Error("could not reset d3d device", hr);
+ status = VIDEO_ERR;
+ return false;
+ }
+
+ // Store render target surface desc
+ IDirect3DSurface9* back_buffer;
+ d3ddevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &back_buffer);
+ back_buffer->GetDesc(&back_buffer_desc);
+ RELEASE(back_buffer);
+
+ width = video_settings.GetWidth();
+ height = video_settings.GetHeight();
+ bpp = video_settings.GetDepth();
+
+ shadow_enabled = vs->shadows;
+ bump_enabled = vs->bumpmaps;
+ spec_enabled = vs->specmaps;
+
+
+ if (CreateBuffers()) {
+ d3ddevice->SetRenderState(D3DRS_ALPHATESTENABLE, false);
+ d3ddevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);
+
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
+ d3ddevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_NONE);
+
+ D3DVIEWPORT9 view;
+
+ hr = d3ddevice->GetViewport(&view);
+ if (SUCCEEDED(hr)) {
+ rect.x = view.X;
+ rect.y = view.Y;
+ rect.w = view.Width;
+ rect.h = view.Height;
+ }
+
+ if (using_x_font)
+ UseXFont(font_name, font_size, font_bold, font_ital);
+
+ status = VIDEO_OK;
+ }
+
+ return status == VIDEO_OK;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::CreateBuffers()
+{
+ if (d3ddevice) {
+ UINT vertex_size = sizeof(VideoDX9ScreenVertex);
+ UINT index_size = sizeof(WORD);
+
+ if (!screen_vbuf) {
+ screen_vbuf = new(__FILE__,__LINE__) VideoDX9VertexBuffer(
+ this,
+ NUM_SCREEN_VERTS,
+ vertex_size,
+ VideoDX9ScreenVertex::FVF,
+ D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY);
+ }
+
+ if (!screen_ibuf) {
+ screen_ibuf = new(__FILE__,__LINE__) VideoDX9IndexBuffer(
+ this,
+ NUM_SCREEN_INDICES,
+ D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY);
+ }
+
+ screen_line_verts = new(__FILE__,__LINE__) VideoDX9ScreenVertex[256];
+ line_verts = new(__FILE__,__LINE__) VideoDX9LineVertex[512];
+
+ // create effects:
+ LPD3DXBUFFER code_buffer = 0;
+ DataLoader* loader = DataLoader::GetLoader();
+ HRESULT hr = E_FAIL;
+
+ hr = d3ddevice->CreateVertexDeclaration(videoDX9NormalVertexElements,
+ &vertex_declaration);
+
+ if (video_settings.use_effects && !magic_fx_code) {
+ if (loader) {
+ magic_fx_code_len = loader->LoadBuffer("magic.fx", magic_fx_code, true, true);
+ }
+ else {
+ FILE* f = ::fopen("magic.fx", "rb");
+
+ if (f) {
+ ::fseek(f, 0, SEEK_END);
+ magic_fx_code_len = ftell(f);
+ ::fseek(f, 0, SEEK_SET);
+
+ magic_fx_code = new(__FILE__,__LINE__) BYTE[magic_fx_code_len+1];
+ if (magic_fx_code) {
+ ::fread(magic_fx_code, magic_fx_code_len, 1, f);
+ magic_fx_code[magic_fx_code_len] = 0;
+ }
+
+ ::fclose(f);
+ }
+ }
+ }
+
+ if (video_settings.use_effects && magic_fx_code && magic_fx_code_len) {
+ hr = D3DXCreateEffect(d3ddevice,
+ magic_fx_code,
+ magic_fx_code_len,
+ 0, 0, 0, 0,
+ &magic_fx,
+ &code_buffer);
+
+ if (code_buffer) {
+ ::Print("ERROR - Failed to compile 'magic.fx'\n");
+ ::Print((const char*) code_buffer->GetBufferPointer());
+ ::Print("\n\n");
+ RELEASE(code_buffer);
+ }
+ }
+ }
+
+ return screen_vbuf && screen_ibuf;
+}
+
+bool
+VideoDX9::DestroyBuffers()
+{
+ if (line_verts) {
+ delete line_verts;
+ line_verts = 0;
+ }
+
+ if (screen_line_verts) {
+ delete screen_line_verts;
+ screen_line_verts = 0;
+ }
+
+ if (screen_vbuf) {
+ delete screen_vbuf;
+ screen_vbuf = 0;
+ }
+
+ if (screen_ibuf) {
+ delete screen_ibuf;
+ screen_ibuf = 0;
+ }
+
+ if (font_verts) {
+ delete [] font_verts;
+ font_verts = 0;
+ }
+
+ if (font_indices) {
+ delete [] font_indices;
+ font_indices = 0;
+ }
+
+ font_nverts = 0;
+
+ RELEASE(vertex_declaration);
+ RELEASE(magic_fx);
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+DWORD
+VideoDX9::VidMemFree() const
+{
+ UINT result = 0;
+
+ if (d3ddevice)
+ result = d3ddevice->GetAvailableTextureMem();
+
+ return result;
+}
+
+int
+VideoDX9::MaxTexSize() const
+{
+ if (d3d && dx9enum && dx9enum->GetAdapterInfo()) {
+ VideoDX9DeviceInfo* dev_info = dx9enum->GetDeviceInfo(video_settings.GetDeviceType());
+
+ if (dev_info) {
+ return (int) dev_info->caps.MaxTextureWidth;
+ }
+ }
+
+ return 0;
+}
+
+int
+VideoDX9::MaxTexAspect() const
+{
+ if (d3d && dx9enum && dx9enum->GetAdapterInfo()) {
+ VideoDX9DeviceInfo* dev_info = dx9enum->GetDeviceInfo(video_settings.GetDeviceType());
+
+ if (dev_info) {
+ return (int) dev_info->caps.MaxTextureAspectRatio;
+ }
+ }
+
+ return 0;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+VideoDX9::RecoverSurfaces()
+{
+ Print("VideoDX9::RecoverSurfaces()\n");
+
+ HRESULT hr = D3D_OK;
+
+ surface = 0;
+
+ hr = d3ddevice->TestCooperativeLevel();
+
+ if (hr == D3DERR_DEVICELOST) {
+ // This means that some app took exclusive mode access
+ // we need to sit in a loop till we get back to the right mode.
+ Print("D3DERR_DEVICELOST\n");
+
+ do {
+ Sleep(500);
+ hr = d3ddevice->TestCooperativeLevel();
+ } while (hr == D3DERR_DEVICELOST);
+ }
+
+ if (hr == D3DERR_DEVICENOTRESET) {
+ if (Reset(&video_settings))
+ hr = S_OK;
+ }
+
+ if (SUCCEEDED(hr)) {
+ Print("* Invalidating Texture Cache\n");
+ // Re-fill the contents of textures which just got restored:
+ InvalidateCache();
+
+ device_lost = false;
+ }
+
+ Print("* Vid Mem Free: %8d\n", VidMemFree());
+ Print("* Recover Surfaces Complete.\n\n");
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::SetBackgroundColor(Color c)
+{
+ background = c;
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// RampValue
+//
+// The gamma function with inputs in [0,255], scaled to a range with the
+// default range appropriate for D3DGAMMARAMP.
+//
+inline WORD
+RampValue(UINT i, double recip_gamma, double fade)
+{
+ return (WORD) (65535.0 * fade * pow(i/255.f, recip_gamma));
+}
+
+//-----------------------------------------------------------------------------
+// ReciprocalGamma
+//
+// Given a gamma corrected i in [0,255], return 1/gamma
+//
+inline float
+ReciprocalGamma(UINT i)
+{
+ return logf(i/255.f)/logf(0.5f);
+}
+
+//-----------------------------------------------------------------------------
+// GammaValue
+//
+// Given a gamma corrected color channel value in [0,255], return the gamma.
+//
+inline float
+GammaValue(UINT i)
+{
+ return logf(0.5f)/logf(i/255.f);
+}
+
+bool
+VideoDX9::SetGammaLevel(int g)
+{
+ HRESULT hr = E_FAIL;
+ double f = Color::GetFade();
+
+ if (gamma != g || fade != f) {
+ if (d3ddevice) {
+ //::Print("VideoDX9 - SetGammaLevel(%d) fade = %f\n", g, f);
+
+ // compute 1/gamma
+ float recip_gray = ReciprocalGamma(g);
+
+ // compute i**(1/gamma) for all i and scale to range
+ for (UINT i = 0; i < 256; i++) {
+ int val = RampValue(i, recip_gray, f);
+
+ gamma_ramp.red[i] = val;
+ gamma_ramp.green[i] = val;
+ gamma_ramp.blue[i] = val;
+ }
+
+ d3ddevice->SetGammaRamp(0, D3DSGR_NO_CALIBRATION, &gamma_ramp);
+ hr = D3D_OK;
+ }
+
+ gamma = g;
+ fade = f;
+ }
+
+ return SUCCEEDED(hr);
+}
+
+bool
+VideoDX9::SetObjTransform(const Matrix& orient, const Point& loc)
+{
+ HRESULT hr = E_FAIL;
+
+ if (d3ddevice) {
+ D3DMATRIX world_matrix;
+ CreateD3DMatrix(world_matrix, orient, loc);
+ hr = d3ddevice->SetTransform(D3DTS_WORLD, &world_matrix);
+
+ matrixWorld = world_matrix;
+ D3DXMatrixInverse(&matrixWorldInverse, 0, &matrixWorld);
+ }
+
+ return SUCCEEDED(hr);
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::ClearAll()
+{
+ HRESULT err;
+
+ err = d3ddevice->Clear(0,
+ NULL,
+ D3DCLEAR_TARGET | D3DCLEAR_STENCIL | D3DCLEAR_ZBUFFER,
+ background.Value(),
+ 1.0f,
+ 0);
+
+ if (FAILED(err)) {
+ static int report = 10;
+ if (report > 0) {
+ VideoDX9Error("Failed to clear device", err);
+ report--;
+ }
+ }
+
+ return true;
+}
+
+bool
+VideoDX9::ClearDepthBuffer()
+{
+ HRESULT err;
+
+ err = d3ddevice->Clear(0,
+ NULL,
+ D3DCLEAR_STENCIL | D3DCLEAR_ZBUFFER,
+ 0,
+ 1.0f,
+ 0);
+
+ if (FAILED(err)) {
+ static int report = 10;
+ if (report > 0) {
+ VideoDX9Error("Failed to clear depth buffer", err);
+ report--;
+ }
+ }
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::Present()
+{
+ // Show the frame on the primary surface.
+ HRESULT err = d3ddevice->Present( NULL, NULL, NULL, NULL );
+
+ if (FAILED(err)) {
+ if (err == D3DERR_DEVICELOST) {
+ device_lost = true;
+ }
+
+ else {
+ static int report = 10;
+ if (report > 0) {
+ VideoDX9Error("Could not present frame", err);
+ report--;
+ }
+ }
+ }
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::Pause()
+{
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::Resume()
+{
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+VideoDX9::PreloadSurface(Surface* s)
+{
+ if (s)
+ PrepareSurface(s);
+}
+
+void
+VideoDX9::PreloadTexture(Bitmap* tex)
+{
+ if (texcache && tex)
+ texcache->FindTexture(tex);
+}
+
+void
+VideoDX9::InvalidateCache()
+{
+ ListIter<Model> iter = model_clients;
+ while (++iter) {
+ // remove each model from the list...
+ Model* model = iter.removeItem();
+
+ // ...so that the buffer destructor doesn't
+ // do it and mess up the iterator.
+ model->DeletePrivateData();
+ }
+
+ if (texcache)
+ texcache->InvalidateCache();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+VideoDX9::CreateD3DMatrix(D3DMATRIX& result, const Matrix& m, const Point& p)
+{
+ result._11 = (float) m.elem[0][0];
+ result._12 = (float) m.elem[1][0];
+ result._13 = (float) m.elem[2][0];
+ result._14 = 0.0f;
+
+ result._21 = (float) m.elem[0][1];
+ result._22 = (float) m.elem[1][1];
+ result._23 = (float) m.elem[2][1];
+ result._24 = 0.0f;
+
+ result._31 = (float) m.elem[0][2];
+ result._32 = (float) m.elem[1][2];
+ result._33 = (float) m.elem[2][2];
+ result._34 = 0.0f;
+
+ result._41 = (float) p.x;
+ result._42 = (float) p.y;
+ result._43 = (float) p.z;
+ result._44 = 1.0f;
+}
+
+void
+VideoDX9::CreateD3DMatrix(D3DMATRIX& result, const Matrix& m, const Vec3& v)
+{
+ result._11 = (float) m.elem[0][0];
+ result._12 = (float) m.elem[1][0];
+ result._13 = (float) m.elem[2][0];
+ result._14 = 0.0f;
+
+ result._21 = (float) m.elem[0][1];
+ result._22 = (float) m.elem[1][1];
+ result._23 = (float) m.elem[2][1];
+ result._24 = 0.0f;
+
+ result._31 = (float) m.elem[0][2];
+ result._32 = (float) m.elem[1][2];
+ result._33 = (float) m.elem[2][2];
+ result._34 = 0.0f;
+
+ result._41 = v.x;
+ result._42 = v.y;
+ result._43 = v.z;
+ result._44 = 1.0f;
+}
+
+void
+VideoDX9::CreateD3DMaterial(D3DMATERIAL9& result, const Material& mtl)
+{
+ CopyMemory(&result.Diffuse, &mtl.Kd, sizeof(D3DCOLORVALUE));
+ CopyMemory(&result.Ambient, &mtl.Ka, sizeof(D3DCOLORVALUE));
+ CopyMemory(&result.Specular, &mtl.Ks, sizeof(D3DCOLORVALUE));
+ CopyMemory(&result.Emissive, &mtl.Ke, sizeof(D3DCOLORVALUE));
+
+ result.Power = mtl.power;
+}
+
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::Capture(Bitmap& bmp)
+{
+ if (d3ddevice) {
+ HRESULT hr = E_FAIL;
+ LPDIRECT3DSURFACE9 pSurf=NULL, pTempSurf=NULL;
+ D3DSURFACE_DESC desc;
+ D3DDISPLAYMODE dm;
+
+ // get display dimensions
+ // this will be the dimensions of the front buffer
+ hr = d3ddevice->GetDisplayMode(0, &dm);
+
+ if (FAILED(hr))
+ VideoDX9Error("VideoDX9::Capture - Can't get display mode!", hr);
+
+ desc.Width = dm.Width;
+ desc.Height = dm.Height;
+ desc.Format = D3DFMT_A8R8G8B8;
+
+ hr = d3ddevice->CreateOffscreenPlainSurface(
+ desc.Width,
+ desc.Height,
+ desc.Format,
+ D3DPOOL_SYSTEMMEM,
+ &pTempSurf,
+ NULL);
+
+ if (FAILED(hr)) {
+ VideoDX9Error("VideoDX9::Capture - Cannot create offscreen buffer 1", hr);
+ return false;
+ }
+
+ hr = d3ddevice->GetFrontBufferData(0, pTempSurf);
+
+ if (FAILED(hr)) {
+ RELEASE(pTempSurf);
+ VideoDX9Error("VideoDX9::Capture - Can't get front buffer", hr);
+ return false;
+ }
+
+
+ if (video_settings.IsWindowed()) {
+ POINT pt={0, 0};
+ RECT srcRect;
+
+ // capture only the client area of the screen:
+ ::GetClientRect(hwnd, &srcRect);
+ ::ClientToScreen(hwnd, (LPPOINT) &srcRect);
+ srcRect.right += srcRect.left;
+ srcRect.bottom += srcRect.top;
+
+ desc.Width = srcRect.right - srcRect.left;
+ desc.Height = srcRect.bottom - srcRect.top;
+ desc.Format = D3DFMT_A8R8G8B8; // this is what we get from the screen, so stick with it
+
+ // NB we can't lock the back buffer direct because it's no created that way
+ // and to do so hits performance, so copy to another surface
+ // Must be the same format as the source surface
+ hr = d3ddevice->CreateOffscreenPlainSurface(
+ desc.Width,
+ desc.Height,
+ desc.Format,
+ D3DPOOL_DEFAULT,
+ &pSurf,
+ NULL);
+
+ if (FAILED(hr)) {
+ RELEASE(pSurf);
+ VideoDX9Error("VideoDX9::Capture - Cannot create offscreen buffer 2", hr);
+ return false;
+ }
+
+ // Copy
+ hr = d3ddevice->UpdateSurface(pTempSurf, &srcRect, pSurf, &pt);
+
+ if (FAILED(hr)) {
+ RELEASE(pTempSurf);
+ RELEASE(pSurf);
+ VideoDX9Error("VideoDX9::Capture - Cannot update surface", hr);
+ return false;
+ }
+
+ RELEASE(pTempSurf);
+ pTempSurf = pSurf;
+ pSurf = NULL;
+ }
+
+ D3DLOCKED_RECT lockedRect;
+ hr = pTempSurf->LockRect(&lockedRect, NULL,
+ D3DLOCK_READONLY | D3DLOCK_NOSYSLOCK);
+
+ if (FAILED(hr)) {
+ VideoDX9Error("VideoDX9::Capture - can't lock rect", hr);
+ RELEASE(pTempSurf);
+ return false;
+ }
+
+ // Allocate color buffer
+ DWORD* buffer = new DWORD[desc.Width * desc.Height];
+ BYTE* src = (BYTE*) lockedRect.pBits;
+ BYTE* dst = (BYTE*) buffer;
+ Color clr;
+
+ for (DWORD y = 0; y < desc.Height; y++) {
+ BYTE *pRow = src;
+
+ for (DWORD x = 0; x < desc.Width; x++) {
+ switch(desc.Format) {
+ case D3DFMT_R5G6B5:
+ clr = Color::Unformat(*((WORD*) (pRow)));
+
+ *dst++ = (BYTE) clr.Red();
+ *dst++ = (BYTE) clr.Green();
+ *dst++ = (BYTE) clr.Blue();
+ *dst++ = 255;
+ break;
+
+ case D3DFMT_A8R8G8B8:
+ case D3DFMT_X8R8G8B8:
+ *dst++ = pRow[0]; // R
+ *dst++ = pRow[1]; // G
+ *dst++ = pRow[2]; // B
+ *dst++ = 255;
+
+ pRow += 4;
+ break;
+
+ case D3DFMT_R8G8B8:
+ *dst++ = pRow[0]; // R
+ *dst++ = pRow[1]; // G
+ *dst++ = pRow[2]; // B
+ *dst++ = 255;
+
+ pRow += 3;
+ break;
+ }
+
+ }
+
+ src += lockedRect.Pitch;
+ }
+
+ bmp.CopyHighColorImage(desc.Width, desc.Height, buffer);
+
+ delete [] buffer;
+
+ RELEASE(pTempSurf);
+ RELEASE(pSurf);
+
+ return SUCCEEDED(hr);
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::GetWindowRect(Rect& r)
+{
+ if (d3ddevice && (rect.w < 1 || rect.h < 1)) {
+ D3DVIEWPORT9 view;
+ HRESULT hr = d3ddevice->GetViewport(&view);
+ if (SUCCEEDED(hr)) {
+ rect.x = view.X;
+ rect.y = view.Y;
+ rect.w = view.Width;
+ rect.h = view.Height;
+ }
+ }
+
+ r = rect;
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::SetWindowRect(const Rect& r)
+{
+ return SetViewport(r.x, r.y, r.w, r.h);
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::SetViewport(int x, int y, int w, int h)
+{
+ if (!d3d || !d3ddevice)
+ return false;
+
+ HRESULT hr;
+
+ // set up the viewport according to args:
+ D3DVIEWPORT9 view;
+
+ view.X = x;
+ view.Y = y;
+ view.Width = w;
+ view.Height = h;
+ view.MinZ = 0.0f;
+ view.MaxZ = 1.0f;
+
+ hr = d3ddevice->SetViewport(&view);
+ if (FAILED(hr)) {
+ VideoDX9Error("could not initialize viewport", hr);
+ return false;
+ }
+
+ // set up the render state:
+ for (int i = FILL_MODE; i < TEXTURE_WRAP; i++) {
+ if (d3dstate_table[i]) {
+ d3ddevice->SetRenderState((D3DRENDERSTATETYPE) d3dstate_table[i], render_state[i]);
+ }
+ }
+
+ d3ddevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);
+
+ rect.x = x;
+ rect.y = y;
+ rect.w = w;
+ rect.h = h;
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::SetAmbient(Color c)
+{
+ ambient = c;
+ return true;
+}
+
+bool
+VideoDX9::SetLights(const List<Light>& lights)
+{
+ if (d3ddevice) {
+ main_light = 0;
+ back_light = 0;
+
+ ListIter<Light> iter = (List<Light>&) lights;
+ int index = -1;
+
+ while (++iter) {
+ Light* light = iter.value();
+
+ if (light->IsActive()) {
+ D3DLIGHT9 d3d_light;
+ ZeroMemory(&d3d_light, sizeof(d3d_light));
+ d3d_light.Type = (D3DLIGHTTYPE) light->Type();
+
+ if (light->Type() == Light::LIGHT_DIRECTIONAL) {
+ d3d_light.Direction.x = (float) (-light->Location().x);
+ d3d_light.Direction.y = (float) (-light->Location().y);
+ d3d_light.Direction.z = (float) (-light->Location().z);
+
+ if (d3d_light.Direction.x == 0 &&
+ d3d_light.Direction.y == 0 &&
+ d3d_light.Direction.z == 0) {
+
+ d3d_light.Direction.y = -1;
+ }
+
+ if (light->CastsShadow()) {
+ if (!main_light || light->Intensity() > main_light->Intensity())
+ main_light = light;
+ }
+ else if (!back_light) {
+ back_light = light;
+ }
+ }
+ else {
+ d3d_light.Position.x = (float) ( light->Location().x);
+ d3d_light.Position.y = (float) ( light->Location().y);
+ d3d_light.Position.z = (float) ( light->Location().z);
+ }
+
+ float r = (light->GetColor().Red() / 255.0f) * light->Intensity();
+ float g = (light->GetColor().Green() / 255.0f) * light->Intensity();
+ float b = (light->GetColor().Blue() / 255.0f) * light->Intensity();
+
+ d3d_light.Diffuse.r = r;
+ d3d_light.Diffuse.g = g;
+ d3d_light.Diffuse.b = b;
+
+ d3d_light.Specular.r = r;
+ d3d_light.Specular.g = g;
+ d3d_light.Specular.b = b;
+
+ d3d_light.Range = light->Intensity() * 10.0f;
+ d3d_light.Attenuation0 = 0.1f;
+ d3d_light.Attenuation1 = 0.7f;
+ d3d_light.Attenuation2 = 0.0f;
+
+ index++;
+ d3ddevice->SetLight(index, &d3d_light);
+ d3ddevice->LightEnable(index, TRUE);
+ }
+ }
+
+ // turn off any unused lights from before:
+ while (nlights > index+1) {
+ d3ddevice->LightEnable(--nlights, FALSE);
+ }
+
+ nlights = index + 1;
+
+ return true;
+ }
+
+ return false;
+}
+
+bool
+VideoDX9::SetCamera(const Camera* cam)
+{
+ if (d3ddevice) {
+ camera = cam;
+
+ D3DMATRIX m;
+ CreateD3DMatrix(m, cam->Orientation(), cam->Pos());
+ d3ddevice->SetTransform(D3DTS_VIEW, &m);
+ matrixView = m;
+
+ return true;
+ }
+
+ return false;
+}
+
+bool
+VideoDX9::SetProjection(float fov, float znear, float zfar, DWORD type)
+{
+ if (d3ddevice && zfar > znear) {
+ D3DMATRIX m;
+ float h, w, Q;
+
+ double width = (float) (rect.w);
+ double height = (float) (rect.h);
+ ZeroMemory(&m, sizeof(m));
+
+ /***
+ *** PERSPECTIVE PROJECTION:
+ ***/
+
+ if (type == PROJECTION_PERSPECTIVE) {
+ double xscale = width / fov;
+ double yscale = height / fov;
+
+ double maxscale = xscale;
+ if (yscale > xscale) maxscale = yscale;
+
+ double xangle = atan(fov/2 * maxscale/xscale);
+
+ w = (float) (2/tan(xangle)); // 1/tan(x) == cot(x)
+ h = (float) (w * width/height);
+ Q = zfar/(zfar - znear);
+
+ m._11 = w;
+ m._22 = h;
+ m._33 = Q;
+ m._43 = -Q*znear;
+ m._34 = 1;
+ }
+
+ /***
+ *** ORTHOGONAL PROJECTION:
+ ***/
+
+ else if (type == PROJECTION_ORTHOGONAL) {
+ m._11 = (float) (fov/width);
+ m._22 = (float) (fov/height);
+ m._33 = (float) (1/(zfar-znear));
+ m._43 = (float) (znear/(znear-zfar));
+ m._44 = (float) (1);
+ }
+
+ else {
+ return false;
+ }
+
+ d3ddevice->SetTransform(D3DTS_PROJECTION, &m);
+ matrixProj = m;
+
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::SetEnvironment(Bitmap** faces)
+{
+ if (environment_cube && !faces) {
+ delete environment_cube;
+ environment_cube = 0;
+ return true;
+ }
+
+ if (!environment_cube) {
+ environment_cube = new(__FILE__,__LINE__) TexCubeDX9(this);
+ }
+
+ if (environment_cube) {
+ bool ok = true;
+ for (int i = 0; i < 6; i++)
+ ok = ok && environment_cube->LoadTexture(faces[i], i);
+ return ok;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::SetRenderState(RENDER_STATE state, DWORD value)
+{
+ if (!d3ddevice)
+ return false;
+
+ if (render_state[state] == value || d3dstate_table[state] == 0) {
+ render_state[state] = value;
+ return true;
+ }
+
+ HRESULT hr = E_FAIL;
+
+ // special case for texture wrapping:
+ if (state == TEXTURE_WRAP) {
+ DWORD wrap = D3DTADDRESS_CLAMP;
+
+ if (value)
+ wrap = D3DTADDRESS_WRAP;
+
+ hr = d3ddevice->SetSamplerState(0, D3DSAMP_ADDRESSU, wrap);
+ hr = d3ddevice->SetSamplerState(0, D3DSAMP_ADDRESSV, wrap);
+ }
+
+ // special case for fog enable:
+ else if (state == FOG_ENABLE) {
+ if (value) {
+ hr = d3ddevice->SetRenderState(D3DRS_FOGVERTEXMODE, D3DFOG_EXP);
+ hr = d3ddevice->SetRenderState(D3DRS_FOGTABLEMODE, D3DFOG_NONE);
+ }
+
+ hr = d3ddevice->SetRenderState(D3DRS_FOGENABLE, value);
+ }
+
+ // special case for z bias
+ else if (state == Z_BIAS) {
+ if (value) {
+ FLOAT bias_scale = 1.0f;
+ FLOAT depth_bias = (FLOAT) (DW2I(value) / -10000.0);
+
+ hr = d3ddevice->SetRenderState(D3DRS_SLOPESCALEDEPTHBIAS, F2DW(bias_scale));
+ hr = d3ddevice->SetRenderState(D3DRS_DEPTHBIAS, F2DW(depth_bias));
+ }
+ else {
+ hr = d3ddevice->SetRenderState(D3DRS_SLOPESCALEDEPTHBIAS, 0);
+ hr = d3ddevice->SetRenderState(D3DRS_DEPTHBIAS, 0);
+ }
+ }
+
+ // set default z func along with z enable
+ else if (state == Z_ENABLE) {
+ hr = d3ddevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);
+ hr = d3ddevice->SetRenderState(D3DRS_ZENABLE, value);
+ }
+
+ // all other render states:
+ else {
+ hr = d3ddevice->SetRenderState((D3DRENDERSTATETYPE) d3dstate_table[state], value);
+ }
+
+ if (FAILED(hr)) {
+ VideoDX9Error("could not SetRenderState", hr);
+ return false;
+ }
+ else {
+ render_state[state] = value;
+ }
+
+ return true;
+}
+
+bool
+VideoDX9::SetBlendType(int blend_type)
+{
+ if (blend_type == current_blend_state)
+ return true;
+
+ switch (blend_type) {
+ default:
+ // map misc blend types to SOLID
+ // and fall through to that case
+
+ blend_type = BLEND_SOLID;
+
+ case BLEND_SOLID:
+ d3ddevice->SetRenderState(D3DRS_ALPHABLENDENABLE, false);
+ break;
+
+ case BLEND_ALPHA:
+ d3ddevice->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
+ d3ddevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
+ d3ddevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
+ break;
+
+ case BLEND_ADDITIVE:
+ d3ddevice->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
+ d3ddevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
+ d3ddevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);
+ break;
+ }
+
+ current_blend_state = blend_type;
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::StartFrame()
+{
+ if (device_lost) {
+ RecoverSurfaces();
+
+ if (status != VIDEO_OK)
+ return false;
+ }
+
+ stats.Clear();
+
+ HRESULT err = 0;
+ static int frame_number = 1;
+ static int report_errs = 100;
+
+ stats.nframe = frame_number;
+ texcache->FrameNumber(frame_number++);
+
+ // update gamma ramp for global fade value:
+ SetGammaLevel(gamma);
+
+ ClearAll();
+
+ err = d3ddevice->BeginScene();
+
+ if (FAILED(err)) {
+ if (report_errs > 0) {
+ report_errs--;
+ VideoDX9Error("could not begin scene", err);
+ }
+
+ return false;
+ }
+
+ scene_active = 1;
+ current_blend_state = -1;
+
+ SetRenderState(LIGHTING_PASS, 0);
+ SetRenderState(STENCIL_ENABLE, false);
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::EndFrame()
+{
+ HRESULT err = 0;
+
+ if (scene_active) {
+ err = d3ddevice->EndScene();
+
+ if (FAILED(err)) {
+ VideoDX9Error("could not end scene", err);
+ return false;
+ }
+ }
+
+ scene_active = 0;
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+static DWORD ColorModulate(DWORD a, DWORD b)
+{
+ float a0 = (float) ((a ) & 0xff)/255.0f;
+ float a1 = (float) ((a>> 8) & 0xff)/255.0f;
+ float a2 = (float) ((a>>16) & 0xff)/255.0f;
+ float a3 = (float) ((a>>24) & 0xff)/255.0f;
+
+ float b0 = (float) ((b ) & 0xff)/255.0f;
+ float b1 = (float) ((b>> 8) & 0xff)/255.0f;
+ float b2 = (float) ((b>>16) & 0xff)/255.0f;
+ float b3 = (float) ((b>>24) & 0xff)/255.0f;
+
+ return (DWORD) ((BYTE)(a3*b3*255.0f) << 24) |
+ ((BYTE)(a2*b2*255.0f) << 16) |
+ ((BYTE)(a1*b1*255.0f) << 8) |
+ ((BYTE)(a0*b0*255.0f));
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::PopulateScreenVerts(VertexSet* vset)
+{
+ if (!vset || !screen_vbuf)
+ return false;
+
+ num_verts = vset->nverts;
+
+ VideoDX9ScreenVertex* v = (VideoDX9ScreenVertex*) screen_vbuf->Lock(num_verts);
+
+ if (v) {
+ first_vert = screen_vbuf->GetNextVert();
+
+ for (int i = 0; i < num_verts; i++) {
+ v->sx = vset->s_loc[i].x;
+ v->sy = vset->s_loc[i].y;
+ v->sz = vset->s_loc[i].z;
+ v->rhw = vset->rw[i];
+
+ v->diffuse = vset->diffuse[i];
+
+ v->tu = vset->tu[i];
+ v->tv = vset->tv[i];
+
+ v++;
+ }
+
+ screen_vbuf->Unlock();
+ return true;
+ }
+
+ Print(" VideoDX9: could not lock screen vbuf for %d verts.\n", num_verts);
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::DrawPolys(int npolys, Poly* polys)
+{
+ bool result = false;
+
+ if (d3ddevice && polys && npolys > 0) {
+ // screen space polys:
+ if (polys->vertex_set->space == VertexSet::SCREEN_SPACE)
+ return DrawScreenPolys(npolys, polys);
+
+ // world space polys:
+ stats.ncalls++;
+
+ VertexSet* vset = polys->vertex_set;
+ int nverts = vset->nverts;
+ bool luminous = false;
+ bool detail = false;
+ DWORD fvf = 0;
+ void* verts = 0;
+ int vsize = 0;
+ WORD* indices = new(__FILE__,__LINE__) WORD[npolys*6];
+
+ if (polys->material) {
+ luminous = polys->material->luminous;
+ }
+
+ if (vset->tu1 != 0) {
+ VideoDX9DetailVertex* v = new(__FILE__,__LINE__) VideoDX9DetailVertex[nverts];
+ verts = v;
+ vsize = sizeof(VideoDX9DetailVertex);
+ fvf = VideoDX9DetailVertex::FVF;
+
+ for (int i = 0; i < nverts; i++) {
+ v->x = vset->loc[i].x;
+ v->y = vset->loc[i].y;
+ v->z = vset->loc[i].z;
+
+ v->diffuse = vset->diffuse[i];
+ v->specular = vset->specular[i];
+
+ v->tu = vset->tu[i];
+ v->tv = vset->tv[i];
+ v->tu1 = vset->tu1[i];
+ v->tv1 = vset->tv1[i];
+
+ v++;
+ }
+ }
+
+ else if (luminous) {
+ VideoDX9LuminousVertex* v = new(__FILE__,__LINE__) VideoDX9LuminousVertex[nverts];
+ verts = v;
+ vsize = sizeof(VideoDX9LuminousVertex);
+ fvf = VideoDX9LuminousVertex::FVF;
+
+ for (int i = 0; i < nverts; i++) {
+ v->x = vset->loc[i].x;
+ v->y = vset->loc[i].y;
+ v->z = vset->loc[i].z;
+
+ v->diffuse = vset->diffuse[i];
+
+ v->tu = vset->tu[i];
+ v->tv = vset->tv[i];
+
+ v++;
+ }
+ }
+
+ else {
+ VideoDX9SolidVertex* v = new(__FILE__,__LINE__) VideoDX9SolidVertex[nverts];
+ verts = v;
+ vsize = sizeof(VideoDX9SolidVertex);
+ fvf = VideoDX9SolidVertex::FVF;
+
+ for (int i = 0; i < nverts; i++) {
+ v->x = vset->loc[i].x;
+ v->y = vset->loc[i].y;
+ v->z = vset->loc[i].z;
+
+ v->nx = vset->nrm[i].x;
+ v->ny = vset->nrm[i].y;
+ v->nz = vset->nrm[i].z;
+
+ v->tu = vset->tu[i];
+ v->tv = vset->tv[i];
+
+ v++;
+ }
+ }
+
+ if (verts && indices) {
+ HRESULT hr = E_FAIL;
+
+ // fill index array
+ int num_indices = 0;
+ int num_tris = 0;
+
+ WORD* s = indices;
+ Poly* p = polys;
+
+ for (int i = 0; i < npolys; i++) {
+ if (p->nverts == 3) {
+ num_indices += 3;
+ num_tris += 1;
+
+ *s++ = p->verts[0];
+ *s++ = p->verts[1];
+ *s++ = p->verts[2];
+ }
+
+ else if (p->nverts == 4) {
+ num_indices += 6;
+ num_tris += 2;
+
+ *s++ = p->verts[0];
+ *s++ = p->verts[1];
+ *s++ = p->verts[2];
+
+ *s++ = p->verts[0];
+ *s++ = p->verts[2];
+ *s++ = p->verts[3];
+ }
+
+ p++;
+ }
+
+ hr = d3ddevice->SetTransform(D3DTS_WORLD, &identity_matrix);
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetFVF(fvf);
+
+ // send primitives to the device
+ Material* mtl = polys->material;
+ IDirect3DTexture9* texture = 0;
+
+ if (mtl && texcache && mtl->tex_diffuse) {
+ texture = texcache->FindTexture(mtl->tex_diffuse);
+ }
+
+ if (current_texture != texture) {
+ hr = d3ddevice->SetTexture(0, texture);
+ current_texture = texture;
+ }
+
+ if (mtl && texcache && mtl->tex_detail) {
+ texture = texcache->FindTexture(mtl->tex_detail);
+ hr = d3ddevice->SetTexture(1, texture);
+ }
+
+ if (mtl && !luminous) {
+ D3DMATERIAL9 d3dmtl;
+ CreateD3DMaterial(d3dmtl, *mtl);
+
+ hr = d3ddevice->SetMaterial(&d3dmtl);
+ hr = d3ddevice->SetRenderState(D3DRS_AMBIENT, ambient.Value());
+ }
+
+ // set render states and select buffers
+ SetRenderState(FILL_MODE, D3DFILL_SOLID);
+ SetRenderState(LIGHTING_ENABLE, luminous ? FALSE : TRUE);
+ SetBlendType(mtl->blend);
+
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
+
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
+
+
+ hr = d3ddevice->DrawIndexedPrimitiveUP(
+ D3DPT_TRIANGLELIST,
+ 0,
+ nverts,
+ num_tris,
+ indices,
+ D3DFMT_INDEX16,
+ verts,
+ vsize);
+
+ if (FAILED(hr)) {
+ static int report = 10;
+ if (report-- > 0)
+ VideoDX9Error("Could not draw 3D polys.", hr);
+ }
+
+ delete [] verts;
+ delete [] indices;
+
+ if (SUCCEEDED(hr)) {
+ stats.nverts += nverts;
+ stats.npolys += num_tris;
+ result = true;
+ }
+ }
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::DrawScreenPolys(int npolys, Poly* polys, int blend)
+{
+ bool result = false;
+ HRESULT hr = E_FAIL;
+
+ if (d3ddevice && polys && npolys > 0 && screen_vbuf && screen_ibuf) {
+ stats.ncalls++;
+
+ // fill screen vertex buffer
+ if (!PopulateScreenVerts(polys->vertex_set))
+ return false;
+
+ // fill screen index buffer
+ int num_indices = 0;
+ int num_tris = 0;
+
+ // count the number of indices needed for these polys
+ for (int i = 0; i < npolys; i++) {
+ Poly* p = polys + i;
+
+ if (p->nverts == 3) {
+ num_indices += 3;
+ num_tris += 1;
+ }
+
+ else if (p->nverts == 4) {
+ num_indices += 6;
+ num_tris += 2;
+ }
+ }
+
+ WORD* screen_indices = screen_ibuf->Lock(num_indices);
+ int first_index = screen_ibuf->GetNextIndex();
+
+ if (!screen_indices) {
+ Print(" VideoDX9: could not lock screen ibuf for %d indices.\n", num_indices);
+ return false;
+ }
+
+ // copy the indices into the locked index buffer
+ WORD* s = screen_indices;
+ Poly* p = polys;
+
+ for (i = 0; i < npolys; i++) {
+ if (p->nverts == 3) {
+ *s++ = p->verts[0] + first_vert;
+ *s++ = p->verts[1] + first_vert;
+ *s++ = p->verts[2] + first_vert;
+ }
+ else if (p->nverts == 4) {
+ *s++ = p->verts[0] + first_vert;
+ *s++ = p->verts[1] + first_vert;
+ *s++ = p->verts[2] + first_vert;
+
+ *s++ = p->verts[0] + first_vert;
+ *s++ = p->verts[2] + first_vert;
+ *s++ = p->verts[3] + first_vert;
+ }
+
+ p++;
+ }
+
+ screen_ibuf->Unlock();
+
+ // set render states and select buffers
+ SetRenderState(FILL_MODE, D3DFILL_SOLID);
+ SetRenderState(Z_ENABLE, D3DZB_FALSE);
+ SetRenderState(Z_WRITE_ENABLE, D3DZB_FALSE);
+ SetRenderState(LIGHTING_ENABLE, FALSE);
+ SetBlendType(blend);
+
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
+
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
+
+ // send primitives to the device
+ Material* mtl = polys->material;
+ IDirect3DTexture9* texture = 0;
+
+ if (mtl && texcache && mtl->tex_diffuse) {
+ texture = texcache->FindTexture(mtl->tex_diffuse);
+ }
+
+ if (current_texture != texture) {
+ hr = d3ddevice->SetTexture(0, texture);
+ current_texture = texture;
+ }
+
+ screen_vbuf->Select(0);
+ screen_ibuf->Select();
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetFVF(VideoDX9ScreenVertex::FVF);
+
+ hr = d3ddevice->DrawIndexedPrimitive(
+ D3DPT_TRIANGLELIST,
+ 0,
+ first_vert,
+ num_verts,
+ first_index,
+ num_tris);
+
+ if (FAILED(hr)) {
+ static int report = 10;
+ if (report-- > 0)
+ VideoDX9Error("Could not draw screen polys.", hr);
+ }
+ else {
+ stats.nverts += num_verts;
+ stats.npolys += num_tris;
+ result = true;
+ }
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::DrawSolid(Solid* s, DWORD blend_modes)
+{
+ bool result = false;
+ HRESULT hr = E_FAIL;
+
+ if (d3ddevice && s && s->GetModel()) {
+ Model* model = s->GetModel();
+ Matrix orient = s->Orientation();
+ orient.Transpose();
+
+ D3DMATRIX world_matrix;
+ CreateD3DMatrix(world_matrix, orient, s->Location());
+ d3ddevice->SetTransform(D3DTS_WORLD, &world_matrix);
+ matrixWorld = world_matrix;
+ D3DXMatrixInverse(&matrixWorldInverse, 0, &matrixWorld);
+
+ ListIter<Surface> surf_iter = model->GetSurfaces();
+ while (++surf_iter) {
+ Surface* surf = surf_iter.value();
+
+ if (surf->IsHidden() || surf->IsSimplified())
+ continue;
+
+ if (PrepareSurface(surf)) {
+ result = true;
+
+ VideoDX9SurfaceData* surf_data = (VideoDX9SurfaceData*) surf->GetVideoPrivateData();
+ surf_data->vertex_buffer->Select(0);
+ surf_data->index_buffer->Select();
+
+ ListIter<Segment> seg_iter = surf->GetSegments();
+ while (++seg_iter) {
+ Segment* segment = seg_iter.value();
+ Material* mtl = segment->material;
+
+ if (mtl && (blend_modes & mtl->blend)) {
+ result = result && DrawSegment(segment);
+ }
+ }
+ }
+ }
+ }
+
+ surface_has_tangent_data = false;
+
+ return result;
+}
+
+bool
+VideoDX9::DrawSegment(Segment* segment)
+{
+ bool result = false;
+ bool detail = false;
+ bool luminous = false;
+ HRESULT hr = E_FAIL;
+
+ if (segment && segment->video_data) {
+ stats.ncalls++;
+
+ VideoDX9SegmentData* seg_data = (VideoDX9SegmentData*) segment->video_data;
+ int first_vert = seg_data->first_vert;
+ int num_verts = seg_data->num_verts;
+ int first_index = seg_data->first_index;
+ int num_tris = seg_data->num_tris;
+
+ if (segment->model)
+ luminous = segment->model->IsLuminous();
+
+ // set render states and select buffers
+ d3ddevice->SetRenderState(D3DRS_AMBIENT, ambient.Value());
+
+ Material* mtl = segment->material;
+
+ if (use_material)
+ mtl = use_material;
+
+ if (segment->polys && segment->polys->vertex_set && segment->polys->vertex_set->tu1)
+ detail = true;
+
+ // send primitives to the device
+ if (detail) {
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetFVF(VideoDX9DetailVertex::FVF);
+ }
+
+ else if (luminous) {
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetFVF(VideoDX9LuminousVertex::FVF);
+ }
+
+ else if (surface_has_tangent_data && vertex_declaration) {
+ hr = d3ddevice->SetVertexDeclaration(vertex_declaration);
+ hr = d3ddevice->SetVertexShader(NULL);
+ }
+
+ else {
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetFVF(VideoDX9SolidVertex::FVF);
+ }
+
+ if (render_state[FILL_MODE] == FILL_WIREFRAME) {
+ PrepareMaterial(mtl);
+ SetupPass(0);
+
+ for (int i = 0; i < segment->npolys; i++) {
+ DrawPolyOutline(segment->polys + i);
+ }
+ }
+
+ else if (luminous) {
+ PrepareMaterial(mtl);
+
+ SetRenderState(FILL_MODE, D3DFILL_SOLID);
+ SetRenderState(LIGHTING_ENABLE, FALSE);
+ SetBlendType(mtl->blend);
+
+ for (int pass = 0; pass < passes; pass++) {
+ SetupPass(pass);
+
+ hr = d3ddevice->DrawIndexedPrimitive(
+ D3DPT_TRIANGLELIST,
+ 0,
+ first_vert,
+ num_verts,
+ first_index,
+ num_tris);
+ }
+
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
+ }
+
+ else {
+ PrepareMaterial(mtl);
+
+ if (strategy == DX9_STRATEGY_GLOW && render_state[LIGHTING_PASS] > 0) {
+ hr = 0;
+ }
+
+ else if (magic_fx && strategy < DX9_STRATEGY_BLEND && !detail) {
+ DWORD vs_version = 0;
+ DWORD ps_version = 0;
+ bool shaders_ok = false;
+
+ VideoDX9DeviceInfo* dev_info = dx9enum->GetDeviceInfo(video_settings.GetDeviceType());
+
+ if (dev_info) {
+ vs_version = video_settings.enable_vs ? dev_info->caps.VertexShaderVersion : 0;
+ ps_version = video_settings.enable_ps ? dev_info->caps.PixelShaderVersion : 0;
+
+ if (vs_version >= D3DVS_VERSION(1,1))
+ shaders_ok = true;
+ }
+
+ if (surface_has_tangent_data && vertex_declaration) {
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetVertexDeclaration(vertex_declaration);
+ }
+
+ else {
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetFVF(VideoDX9SolidVertex::FVF);
+ }
+
+ bool would_bump = !IsBumpMapEnabled() &&
+ mtl->bump > 0.5;
+
+ bool will_bump = IsBumpMapEnabled() &&
+ surface_has_tangent_data &&
+ shaders_ok &&
+ camera;
+
+ bool do_pix = will_bump && render_state[LIGHTING_PASS] == 0;
+ bool do_bump = will_bump && render_state[LIGHTING_PASS] > 0;
+
+ D3DXMATRIX matrixWVP = matrixWorld * matrixView * matrixProj;
+
+ D3DXVECTOR4 eyePos( (float) camera->Pos().x,
+ (float) camera->Pos().y,
+ (float) camera->Pos().z,
+ 1.0f);
+
+ D3DXVECTOR4 eyeObj;
+ D3DXVec4Transform(&eyeObj, &eyePos, &matrixWorldInverse);
+
+ D3DXVECTOR4 lightPos(100.0f, 100.0f, 100.0f, 1.0f);
+ D3DXVECTOR4 lightColor(1,1,1,1);
+ D3DXVECTOR4 ambientColor(0,0,0,1);
+
+ ambientColor.x = ambient.fRed();
+ ambientColor.y = ambient.fGreen();
+ ambientColor.z = ambient.fBlue();
+
+ if (main_light && (render_state[LIGHTING_PASS] > 0 || mtl->blend > Material::MTL_SOLID)) {
+ lightPos.x = (float) main_light->Location().x;
+ lightPos.y = (float) main_light->Location().y;
+ lightPos.z = (float) main_light->Location().z;
+
+ if (mtl->tex_bumpmap && do_bump)
+ D3DXVec4Transform(&lightPos, &lightPos, &matrixWorldInverse);
+
+ lightColor.x = main_light->GetColor().fRed() * main_light->Intensity();
+ lightColor.y = main_light->GetColor().fGreen() * main_light->Intensity();
+ lightColor.z = main_light->GetColor().fBlue() * main_light->Intensity();
+ lightColor.w = 1.0f;
+ }
+
+ else if (back_light && render_state[LIGHTING_PASS] == 0) {
+ lightPos.x = (float) back_light->Location().x;
+ lightPos.y = (float) back_light->Location().y;
+ lightPos.z = (float) back_light->Location().z;
+
+ lightColor.x = back_light->GetColor().fRed() * back_light->Intensity();
+ lightColor.y = back_light->GetColor().fGreen() * back_light->Intensity();
+ lightColor.z = back_light->GetColor().fBlue() * back_light->Intensity();
+ lightColor.w = 1.0f;
+ }
+
+ D3DXVECTOR4 lightDir = D3DXVECTOR4(0.0f, 0.0f, 0.0f, 0.0f) - lightPos;
+ D3DXVec4Normalize(&lightDir, &lightDir);
+
+ magic_fx->SetMatrix("wvp", &matrixWVP);
+ magic_fx->SetMatrix("world", &matrixWorld);
+ magic_fx->SetMatrix("view", &matrixView);
+ magic_fx->SetMatrix("proj", &matrixProj);
+ magic_fx->SetMatrix("worldInv", &matrixWorldInverse);
+
+ magic_fx->SetVector("light1Pos", &lightPos);
+ magic_fx->SetVector("light1Dir", &lightDir);
+ magic_fx->SetVector("light1Color", &lightColor);
+ magic_fx->SetVector("ambientColor", &ambientColor);
+ magic_fx->SetVector("eyeObj", &eyeObj);
+
+ FLOAT base_bias = (FLOAT) (DW2I(render_state[Z_BIAS]) / -10000.0);
+ magic_fx->SetFloat("bias", base_bias + video_settings.depth_bias);
+
+ ColorValue orig_ks = mtl->Ks;
+
+ if (would_bump && mtl->specular_value >= 0.5)
+ mtl->Ks = mtl->Ks * 0.3;
+
+ magic_fx->SetValue("Ka", &mtl->Ka, sizeof(ColorValue));
+ magic_fx->SetValue("Kd", &mtl->Kd, sizeof(ColorValue));
+ magic_fx->SetValue("Ke", &mtl->Ke, sizeof(ColorValue));
+ magic_fx->SetValue("Ks", &mtl->Ks, sizeof(ColorValue));
+ magic_fx->SetFloat("Ns", mtl->power);
+
+ if (would_bump && mtl->specular_value >= 0.5)
+ mtl->Ks = orig_ks;
+
+ if (mtl->tex_diffuse) {
+ IDirect3DTexture9* texture = texcache->FindTexture(mtl->tex_diffuse);
+ magic_fx->SetTexture("tex_d", texture);
+ }
+ else {
+ magic_fx->SetTexture("tex_d", 0);
+ }
+
+ if (mtl->tex_emissive) {
+ IDirect3DTexture9* texture = texcache->FindTexture(mtl->tex_emissive);
+ magic_fx->SetTexture("tex_e", texture);
+ }
+ else {
+ magic_fx->SetTexture("tex_e", 0);
+ }
+
+ if (mtl->tex_specular && IsSpecMapEnabled()) {
+ IDirect3DTexture9* texture = texcache->FindTexture(mtl->tex_specular);
+ magic_fx->SetTexture("tex_s", texture);
+ }
+ else {
+ magic_fx->SetTexture("tex_s", 0);
+ }
+
+ if (mtl->tex_bumpmap && do_bump) {
+ IDirect3DTexture9* texture = texcache->FindNormalMap(mtl->tex_bumpmap, mtl->bump);
+ magic_fx->SetTexture("tex_n", texture);
+ magic_fx->SetFloat("offsetAmp", mtl->bump / mtl->tex_bumpmap->Height());
+ }
+
+ else if (mtl->tex_bumpmap && !do_bump) {
+ IDirect3DTexture9* texture = texcache->FindTexture(mtl->tex_bumpmap);
+ magic_fx->SetTexture("tex_x", texture);
+ }
+ else {
+ magic_fx->SetTexture("tex_x", 0);
+ }
+
+ const char* mtl_shader = mtl->GetShader(render_state[LIGHTING_PASS]);
+ D3DXHANDLE hnd_shader = 0;
+
+ if (mtl_shader) {
+ if (!strcmp(mtl_shader, "null"))
+ return true;
+
+ hnd_shader = magic_fx->GetTechniqueByName(mtl_shader);
+ }
+
+ if (hnd_shader) {
+ hr = magic_fx->SetTechnique(hnd_shader);
+ }
+ else {
+ if (will_bump) {
+ if (mtl->tex_specular && IsSpecMapEnabled()) {
+ if (mtl->tex_bumpmap && do_bump) {
+ if (ps_version >= D3DPS_VERSION(2,0))
+ hr = magic_fx->SetTechnique("BumpSpecMapPix");
+ else
+ hr = magic_fx->SetTechnique("BumpSpecMap");
+ }
+ else if (mtl->tex_emissive && render_state[LIGHTING_PASS] == 0) {
+ if (ps_version >= D3DPS_VERSION(2,0))
+ hr = magic_fx->SetTechnique("EmissiveSpecMapPix");
+ else
+ hr = magic_fx->SetTechnique("EmissiveSpecularTexture");
+ }
+ else {
+ if (ps_version >= D3DPS_VERSION(2,0))
+ hr = magic_fx->SetTechnique("SpecMapPix");
+ else
+ hr = magic_fx->SetTechnique("SpecularTexture");
+ }
+ }
+
+ else {
+ if (mtl->tex_bumpmap && do_bump) {
+ if (ps_version >= D3DPS_VERSION(2,0))
+ hr = magic_fx->SetTechnique("BumpMapPix");
+ else
+ hr = magic_fx->SetTechnique("BumpMap");
+ }
+ else if (mtl->tex_emissive && render_state[LIGHTING_PASS] == 0) {
+ if (ps_version >= D3DPS_VERSION(2,0))
+ hr = magic_fx->SetTechnique("EmissivePix");
+ else
+ hr = magic_fx->SetTechnique("EmissiveTexture");
+ }
+ else {
+ if (ps_version >= D3DPS_VERSION(2,0))
+ hr = magic_fx->SetTechnique("SimplePix");
+ else
+ hr = magic_fx->SetTechnique("SimpleTexture");
+ }
+ }
+ }
+
+ else if (texcache && mtl->tex_diffuse) {
+ if (mtl->tex_specular && IsSpecMapEnabled()) {
+ if (mtl->tex_emissive && render_state[LIGHTING_PASS] == 0) {
+ hr = magic_fx->SetTechnique("EmissiveSpecularTexture");
+ }
+ else {
+ hr = magic_fx->SetTechnique("SpecularTexture");
+ }
+ }
+
+ else {
+ if (mtl->tex_emissive && render_state[LIGHTING_PASS] == 0) {
+ hr = magic_fx->SetTechnique("EmissiveTexture");
+ }
+ else {
+ hr = magic_fx->SetTechnique("SimpleTexture");
+ }
+ }
+ }
+
+ else {
+ hr = magic_fx->SetTechnique("SimpleMaterial");
+ }
+ }
+
+ if (environment_cube != 0 && magic_fx->IsParameterUsed("env_cube", hnd_shader)) {
+ /*
+ D3DXMATRIX matP;
+ D3DXMatrixInverse(&matP, NULL, &matrixView);
+ matP._41 = matP._42 = matP._43 = 0.0f;
+ */
+
+ D3DXMATRIX env_matrix;
+ D3DXMatrixIdentity(&env_matrix);
+ //D3DXMatrixScaling(&env_matrix, 1.0f, 1.0f, -1.0f);
+
+ //D3DXMatrixMultiply(&env_matrix, &matP, &env_matrix);
+ magic_fx->SetMatrix("env_matrix", &env_matrix);
+ magic_fx->SetTexture("env_cube", environment_cube->GetTexture());
+ }
+
+ if (render_state[STENCIL_ENABLE]) {
+ d3ddevice->SetRenderState(D3DRS_STENCILENABLE, TRUE);
+ d3ddevice->SetRenderState(D3DRS_STENCILREF, 0x01);
+ d3ddevice->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_GREATER);
+ }
+ else {
+ d3ddevice->SetRenderState(D3DRS_STENCILENABLE, FALSE);
+ }
+
+ if (render_state[LIGHTING_PASS] > 0) {
+ current_blend_state = 100;
+ SetBlendType(BLEND_ADDITIVE);
+ SetRenderState(Z_WRITE_ENABLE, FALSE);
+ }
+ else {
+ current_blend_state = 100;
+ SetBlendType(mtl->blend);
+ }
+
+ UINT nPasses = 0;
+
+ hr = magic_fx->Begin(&nPasses, 0);
+
+ for (UINT i = 0; i < nPasses; i++) {
+ hr = magic_fx->BeginPass(i);
+
+ hr = d3ddevice->DrawIndexedPrimitive(
+ D3DPT_TRIANGLELIST,
+ 0,
+ first_vert,
+ num_verts,
+ first_index,
+ num_tris);
+
+ hr = magic_fx->EndPass();
+ }
+
+ hr = magic_fx->End();
+ }
+
+ else {
+ for (int pass = 0; pass < passes; pass++) {
+ SetupPass(pass);
+
+ hr = d3ddevice->DrawIndexedPrimitive(
+ D3DPT_TRIANGLELIST,
+ 0,
+ first_vert,
+ num_verts,
+ first_index,
+ num_tris);
+
+ if (detail) {
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetFVF(VideoDX9DetailVertex::FVF);
+ }
+
+ else if (luminous) {
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetFVF(VideoDX9LuminousVertex::FVF);
+ }
+
+ else if (surface_has_tangent_data && vertex_declaration) {
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetVertexDeclaration(vertex_declaration);
+ }
+
+ else {
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetFVF(VideoDX9SolidVertex::FVF);
+ }
+ }
+ }
+ }
+
+ if (FAILED(hr)) {
+ static int report = 10;
+ if (report-- > 0)
+ VideoDX9Error("Could not draw solid polys.", hr);
+ }
+ else {
+ stats.nverts += num_verts;
+ stats.npolys += num_tris;
+ result = true;
+ }
+ }
+
+ return result;
+}
+
+bool
+VideoDX9::DrawPolyOutline(Poly* p)
+{
+ if (d3ddevice && p && p->nverts >= 3) {
+ static VideoDX9LineVertex verts[8];
+
+ int nlines = p->nverts;
+ VertexSet* vset = p->vertex_set;
+ WORD index = 0;
+ Color color = Color::Black;
+ HRESULT hr = E_FAIL;
+
+ ZeroMemory(verts, sizeof(verts));
+
+ if (p->material)
+ color = p->material->Kd.ToColor();
+
+ for (int i = 0; i < p->nverts; i++) {
+ index = p->verts[i];
+
+ verts[i].x = vset->loc[index].x;
+ verts[i].y = vset->loc[index].y;
+ verts[i].z = vset->loc[index].z;
+ verts[i].diffuse = color.Value();
+ }
+
+ // last vertex, to close the loop
+ index = p->verts[0];
+ i = p->nverts;
+
+ verts[i].x = vset->loc[index].x;
+ verts[i].y = vset->loc[index].y;
+ verts[i].z = vset->loc[index].z;
+ verts[i].diffuse = color.Value();
+
+ current_texture = 0;
+
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetFVF(VideoDX9LineVertex::FVF);
+ hr = d3ddevice->SetTexture(0, 0);
+ hr = d3ddevice->DrawPrimitiveUP(
+ D3DPT_LINESTRIP,
+ nlines,
+ verts,
+ sizeof(VideoDX9LineVertex));
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::DrawShadow(Solid* s, int nverts, Vec3* shadow_verts, bool visible)
+{
+ bool result = false;
+ HRESULT hr = E_FAIL;
+
+ if (d3ddevice && s && nverts && shadow_verts && IsShadowEnabled()) {
+ Matrix orient = s->Orientation();
+ orient.Transpose();
+
+ D3DMATRIX world_matrix;
+ CreateD3DMatrix(world_matrix, orient, s->Location());
+ d3ddevice->SetTransform(D3DTS_WORLD, &world_matrix);
+ matrixWorld = world_matrix;
+
+ // show shadow outlines:
+ if (visible) {
+ static VideoDX9LineVertex verts[4];
+
+ d3ddevice->SetRenderState(D3DRS_ZENABLE, TRUE);
+ d3ddevice->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
+ d3ddevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);
+ d3ddevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_FLAT);
+ d3ddevice->SetRenderState(D3DRS_LIGHTING, FALSE);
+ d3ddevice->SetVertexShader(NULL);
+ d3ddevice->SetFVF(VideoDX9LineVertex::FVF);
+ d3ddevice->SetTexture(0, 0);
+
+ SetBlendType(BLEND_ALPHA);
+
+ for (int i = 0; i < nverts; i+=3) {
+ DWORD c = 0xa0ffff80;
+
+ verts[0].x = shadow_verts[i+0].x;
+ verts[0].y = shadow_verts[i+0].y;
+ verts[0].z = shadow_verts[i+0].z;
+ verts[0].diffuse = c;
+
+ verts[1].x = shadow_verts[i+1].x;
+ verts[1].y = shadow_verts[i+1].y;
+ verts[1].z = shadow_verts[i+1].z;
+ verts[1].diffuse = c;
+
+ verts[2].x = shadow_verts[i+2].x;
+ verts[2].y = shadow_verts[i+2].y;
+ verts[2].z = shadow_verts[i+2].z;
+ verts[2].diffuse = c;
+
+ verts[3].x = shadow_verts[i+0].x;
+ verts[3].y = shadow_verts[i+0].y;
+ verts[3].z = shadow_verts[i+0].z;
+ verts[3].diffuse = c;
+
+ hr = d3ddevice->DrawPrimitiveUP(
+ D3DPT_LINESTRIP,
+ 3,
+ verts,
+ sizeof(VideoDX9LineVertex));
+ }
+
+ // restore lighting state
+ d3ddevice->SetRenderState(D3DRS_LIGHTING, render_state[LIGHTING_ENABLE]);
+ }
+
+ // render shadows into stencil buffer:
+
+ // Disable z-buffer writes (note: z-testing still occurs), and enable the
+ // stencil-buffer
+ d3ddevice->SetRenderState(D3DRS_ZENABLE, TRUE);
+ d3ddevice->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
+ d3ddevice->SetRenderState(D3DRS_STENCILENABLE, TRUE);
+
+ // Dont bother with interpolating color
+ d3ddevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_FLAT);
+
+ // Set up stencil compare fuction, reference value, and masks.
+ // Stencil test passes if ((ref & mask) cmpfn (stencil & mask)) is true.
+ // Note: since we set up the stencil-test to always pass, the STENCILFAIL
+ // renderstate is really not needed.
+ d3ddevice->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_ALWAYS );
+ d3ddevice->SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP );
+ d3ddevice->SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP );
+
+ // If z-test passes, inc/decrement stencil buffer value
+ d3ddevice->SetRenderState(D3DRS_STENCILREF, 0x1 );
+ d3ddevice->SetRenderState(D3DRS_STENCILMASK, 0xff );
+ d3ddevice->SetRenderState(D3DRS_STENCILWRITEMASK, 0xff );
+ d3ddevice->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_INCR );
+
+ // Make sure that no pixels get drawn to the frame buffer
+ d3ddevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE );
+ d3ddevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO );
+ d3ddevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE );
+
+ d3ddevice->SetVertexShader(NULL);
+ d3ddevice->SetFVF(D3DFVF_XYZ);
+
+ // Draw front-side of shadow volume in stencil/z only
+ hr = d3ddevice->DrawPrimitiveUP(D3DPT_TRIANGLELIST, nverts/3, shadow_verts, sizeof(Vec3));
+
+ // Now reverse cull order so back sides of shadow volume are written.
+ d3ddevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW );
+
+ // Decrement stencil buffer value
+ d3ddevice->SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_DECR );
+
+ // Draw back-side of shadow volume in stencil/z only
+ hr = d3ddevice->DrawPrimitiveUP(D3DPT_TRIANGLELIST, nverts/3, shadow_verts, sizeof(Vec3));
+
+ // restore render states
+ d3ddevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
+ d3ddevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);
+ d3ddevice->SetRenderState(D3DRS_ZWRITEENABLE, TRUE);
+ d3ddevice->SetRenderState(D3DRS_STENCILENABLE, FALSE);
+
+ // force restore of current blend type
+ int type = current_blend_state;
+ current_blend_state = 100;
+ SetBlendType(type);
+
+ result = SUCCEEDED(hr);
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::DrawLines(int nlines, Vec3* points, Color c, int blend)
+{
+ bool result = false;
+
+ if (d3ddevice && points && nlines > 0 && nlines <= 256) {
+ stats.ncalls++;
+
+ VideoDX9LineVertex* verts = line_verts;
+
+ if (verts) {
+ HRESULT hr = E_FAIL;
+
+ for (int i = 0; i < 2*nlines; i++) {
+ VideoDX9LineVertex* v = verts + i;
+ Vec3* p = points + i;
+
+ v->x = p->x;
+ v->y = p->y;
+ v->z = p->z;
+ v->diffuse = c.Value();
+ }
+
+ hr = d3ddevice->SetTransform(D3DTS_WORLD, &identity_matrix);
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetFVF(VideoDX9LineVertex::FVF);
+
+ DWORD old_lighting = render_state[LIGHTING_ENABLE];
+
+ // untextured lines:
+ if (current_texture) {
+ d3ddevice->SetTexture(0, 0);
+ current_texture = 0;
+ }
+
+ SetRenderState(LIGHTING_ENABLE, FALSE);
+ SetBlendType(blend);
+
+ hr = d3ddevice->DrawPrimitiveUP(
+ D3DPT_LINELIST,
+ nlines,
+ verts,
+ sizeof(VideoDX9LineVertex));
+
+ if (FAILED(hr)) {
+ static int report = 10;
+ if (report-- > 0)
+ VideoDX9Error("Could not draw 3D lines.", hr);
+ }
+
+ SetRenderState(LIGHTING_ENABLE, old_lighting);
+
+ if (SUCCEEDED(hr)) {
+ stats.nverts += 2*nlines;
+ stats.nlines += nlines;
+ result = true;
+ }
+ }
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::DrawScreenLines(int nlines, float* points, Color c, int blend)
+{
+ bool result = false;
+
+ if (d3ddevice && points && nlines > 0 && nlines <= 64) {
+ stats.ncalls++;
+
+ VideoDX9ScreenVertex* verts = screen_line_verts;
+
+ if (verts) {
+ HRESULT hr = E_FAIL;
+
+ for (int i = 0; i < 2*nlines; i++) {
+ VideoDX9ScreenVertex* v = verts + i;
+
+ v->sx = points[2*i + 0];
+ v->sy = points[2*i + 1];
+ v->sz = 0.0f;
+ v->rhw = 1.0f;
+ v->diffuse = c.Value();
+ v->tu = 0.0f;
+ v->tv = 0.0f;
+ }
+
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetFVF(VideoDX9ScreenVertex::FVF);
+
+ if (FAILED(hr)) {
+ static int report = 10;
+ if (report-- > 0)
+ VideoDX9Error("Could not set FVF for screen lines.", hr);
+ }
+
+ else {
+ if (current_texture != 0) {
+ hr = d3ddevice->SetTexture(0, 0);
+ current_texture = 0;
+ }
+
+ SetRenderState(FILL_MODE, D3DFILL_SOLID);
+ SetRenderState(Z_ENABLE, D3DZB_FALSE);
+ SetRenderState(LIGHTING_ENABLE, FALSE);
+ SetBlendType(blend);
+
+ hr = d3ddevice->DrawPrimitiveUP(
+ D3DPT_LINELIST,
+ nlines,
+ verts,
+ sizeof(VideoDX9ScreenVertex));
+
+ if (FAILED(hr)) {
+ static int report = 10;
+ if (report-- > 0)
+ VideoDX9Error("Could not draw screen lines.", hr);
+ }
+ }
+
+ if (SUCCEEDED(hr)) {
+ stats.nverts += 2*nlines;
+ stats.nlines += nlines;
+ result = true;
+ }
+ }
+ }
+
+ return result;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::DrawPoints(VertexSet* vset)
+{
+ if (vset && vset->nverts) {
+ HRESULT hr = E_FAIL;
+
+ int nverts = vset->nverts;
+ VideoDX9LineVertex* verts = new(__FILE__,__LINE__) VideoDX9LineVertex[nverts];
+
+ if (verts) {
+ for (int i = 0; i < nverts; i++) {
+ VideoDX9LineVertex* v = verts + i;
+ Vec3* p = vset->loc + i;
+
+ v->x = p->x;
+ v->y = p->y;
+ v->z = p->z;
+ v->diffuse = vset->diffuse[i];
+ }
+
+ SetRenderState(LIGHTING_ENABLE, FALSE);
+
+ hr = d3ddevice->SetTransform(D3DTS_WORLD, &identity_matrix);
+ hr = d3ddevice->SetVertexShader(NULL);
+ hr = d3ddevice->SetFVF(VideoDX9LineVertex::FVF);
+ hr = d3ddevice->SetTexture(0, 0);
+ hr = d3ddevice->DrawPrimitiveUP(
+ D3DPT_POINTLIST,
+ nverts,
+ verts,
+ sizeof(VideoDX9LineVertex));
+
+ delete [] verts;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::UseMaterial(Material* m)
+{
+ use_material = m;
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::UseXFont(const char* name, int size, bool bold, bool ital)
+{
+ if (d3ddevice && name && *name && size > 4) {
+ RELEASE(d3dx_font);
+
+ strcpy(font_name, name);
+ font_size = size;
+ font_bold = bold;
+ font_ital = ital;
+
+ HRESULT hr = E_FAIL;
+ HDC hdc = GetDC(NULL);
+ int nLogPixelsY = GetDeviceCaps(hdc, LOGPIXELSY);
+
+ ReleaseDC(NULL, hdc);
+
+ int nHeight = -size * nLogPixelsY / 72;
+
+ hr = D3DXCreateFont(d3ddevice, // D3D device
+ nHeight, // Height
+ 0, // Width
+ bold ? FW_BOLD : FW_NORMAL, // Weight
+ 1, // MipLevels, 0 = autogen mipmaps
+ ital, // Italic
+ DEFAULT_CHARSET, // CharSet
+ OUT_DEFAULT_PRECIS, // OutputPrecision
+ DEFAULT_QUALITY, // Quality
+ DEFAULT_PITCH | FF_DONTCARE, // PitchAndFamily
+ name, // pFaceName
+ &d3dx_font); // ppFont
+
+ if (SUCCEEDED(hr)) {
+ return true;
+ }
+ }
+
+ RELEASE(d3dx_font);
+ return false;
+}
+
+bool
+VideoDX9::DrawText(const char* text, int count, const Rect& rect, DWORD format, Color c)
+{
+ if (d3ddevice && text && *text && d3dx_font) {
+ RECT r;
+ r.left = rect.x;
+ r.top = rect.y;
+ r.right = rect.x + rect.w;
+ r.bottom = rect.y + rect.h;
+
+ d3dx_font->DrawText(0, text, count, &r, format, c.Value());
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoDX9::PrepareSurface(Surface* surf)
+{
+ if (surf) {
+ int nverts = surf->NumVerts();
+ int nindices = surf->NumIndices();
+ bool detail = surf->GetVertexSet()->tu1 != 0;
+ bool luminous = false;
+ DWORD dynamic = 0;
+
+ if (surf->GetModel()) {
+ luminous = surf->GetModel()->IsLuminous();
+ dynamic = surf->GetModel()->IsDynamic() ? D3DUSAGE_DYNAMIC : 0;
+ }
+
+ surface_has_tangent_data = !luminous && (surf->GetVertexSet()->tangent && surf->GetVertexSet()->binormal);
+
+ VideoDX9SurfaceData* surf_data = (VideoDX9SurfaceData*) surf->GetVideoPrivateData();
+
+ if (!surf_data) {
+ surf_data = new(__FILE__,__LINE__) VideoDX9SurfaceData(surf->GetModel());
+
+ surface_has_tangent_data = false;
+
+ if (surf->GetVertexSet()->tangent && surf->GetVertexSet()->binormal) {
+ surface_has_tangent_data = true;
+ surf_data->vertex_buffer = new(__FILE__,__LINE__) VideoDX9VertexBuffer(
+ this,
+ nverts,
+ sizeof(VideoDX9NormalVertex),
+ 0, // not an FVF vertex buffer
+ dynamic | D3DUSAGE_WRITEONLY);
+ }
+
+ else if (detail) {
+ surf_data->vertex_buffer = new(__FILE__,__LINE__) VideoDX9VertexBuffer(
+ this,
+ nverts,
+ sizeof(VideoDX9DetailVertex),
+ VideoDX9DetailVertex::FVF,
+ dynamic | D3DUSAGE_WRITEONLY);
+ }
+
+ else if (luminous) {
+ surf_data->vertex_buffer = new(__FILE__,__LINE__) VideoDX9VertexBuffer(
+ this,
+ nverts,
+ sizeof(VideoDX9LuminousVertex),
+ VideoDX9LuminousVertex::FVF,
+ dynamic | D3DUSAGE_WRITEONLY);
+ }
+
+ else {
+ surf_data->vertex_buffer = new(__FILE__,__LINE__) VideoDX9VertexBuffer(
+ this,
+ nverts,
+ sizeof(VideoDX9SolidVertex),
+ VideoDX9SolidVertex::FVF,
+ dynamic | D3DUSAGE_WRITEONLY);
+ }
+
+ surf_data->index_buffer = new(__FILE__,__LINE__) VideoDX9IndexBuffer(
+ this,
+ nindices,
+ dynamic | D3DUSAGE_WRITEONLY);
+
+ if (!surf_data->vertex_buffer || !surf_data->index_buffer) {
+ Print("VideoDX9: Unable to prepare surface '%s'\n", surf->Name());
+ delete surf_data;
+ return false;
+ }
+
+ surf->SetVideoPrivateData(surf_data);
+ }
+
+ if (surf_data && !surf_data->IsValid()) {
+ if (detail) {
+ VideoDX9DetailVertex* v = (VideoDX9DetailVertex*) surf_data->vertex_buffer->Lock(nverts);
+
+ if (v) {
+ const VertexSet* vset = surf->GetVertexSet();
+ for (int i = 0; i < nverts; i++) {
+ v->x = vset->loc[i].x;
+ v->y = vset->loc[i].y;
+ v->z = vset->loc[i].z;
+
+ v->diffuse = vset->diffuse[i];
+ v->specular = vset->specular[i];
+
+ v->tu = vset->tu[i];
+ v->tv = vset->tv[i];
+ v->tu1 = vset->tu1[i];
+ v->tv1 = vset->tv1[i];
+
+ v++;
+ }
+
+ surf_data->vertex_buffer->Unlock();
+ }
+ }
+
+ else if (luminous) {
+ VideoDX9LuminousVertex* v = (VideoDX9LuminousVertex*) surf_data->vertex_buffer->Lock(nverts);
+
+ if (v) {
+ const VertexSet* vset = surf->GetVertexSet();
+ for (int i = 0; i < nverts; i++) {
+ v->x = vset->loc[i].x;
+ v->y = vset->loc[i].y;
+ v->z = vset->loc[i].z;
+
+ v->diffuse = vset->diffuse[i];
+
+ v->tu = vset->tu[i];
+ v->tv = vset->tv[i];
+
+ v++;
+ }
+
+ surf_data->vertex_buffer->Unlock();
+ }
+ }
+
+ else if (surface_has_tangent_data) {
+ VideoDX9NormalVertex* v = (VideoDX9NormalVertex*) surf_data->vertex_buffer->Lock(nverts);
+
+ if (v) {
+ const VertexSet* vset = surf->GetVertexSet();
+ for (int i = 0; i < nverts; i++) {
+ v->x = vset->loc[i].x;
+ v->y = vset->loc[i].y;
+ v->z = vset->loc[i].z;
+
+ v->nx = vset->nrm[i].x;
+ v->ny = vset->nrm[i].y;
+ v->nz = vset->nrm[i].z;
+
+ v->t0u = vset->tu[i];
+ v->t0v = vset->tv[i];
+ v->t1u = vset->tu[i];
+ v->t1v = vset->tv[i];
+
+ v->tx = vset->tangent[i].x;
+ v->ty = vset->tangent[i].y;
+ v->tz = vset->tangent[i].z;
+
+ v->bx = vset->binormal[i].x;
+ v->by = vset->binormal[i].y;
+ v->bz = vset->binormal[i].z;
+
+ v++;
+ }
+
+ surf_data->vertex_buffer->Unlock();
+ }
+ }
+
+ else {
+ VideoDX9SolidVertex* v = (VideoDX9SolidVertex*) surf_data->vertex_buffer->Lock(nverts);
+
+ if (v) {
+ const VertexSet* vset = surf->GetVertexSet();
+ for (int i = 0; i < nverts; i++) {
+ v->x = vset->loc[i].x;
+ v->y = vset->loc[i].y;
+ v->z = vset->loc[i].z;
+
+ v->nx = vset->nrm[i].x;
+ v->ny = vset->nrm[i].y;
+ v->nz = vset->nrm[i].z;
+
+ v->tu = vset->tu[i];
+ v->tv = vset->tv[i];
+
+ v++;
+ }
+
+ surf_data->vertex_buffer->Unlock();
+ }
+ }
+
+ WORD* indices = surf_data->index_buffer->Lock(nindices);
+
+ if (indices) {
+ // copy the indices into the locked index buffer
+ WORD* s = indices;
+ Poly* p = surf->GetPolys();
+
+ for (int i = 0; i < surf->NumPolys(); i++) {
+ if (p->nverts == 3) {
+ *s++ = p->verts[0];
+ *s++ = p->verts[2];
+ *s++ = p->verts[1];
+ }
+ else if (p->nverts == 4) {
+ *s++ = p->verts[0];
+ *s++ = p->verts[2];
+ *s++ = p->verts[1];
+
+ *s++ = p->verts[0];
+ *s++ = p->verts[3];
+ *s++ = p->verts[2];
+ }
+
+ p++;
+ }
+
+ surf_data->index_buffer->Unlock();
+ }
+
+ surf_data->Validate();
+ }
+
+ int first_index = 0;
+
+ ListIter<Segment> seg_iter = surf->GetSegments();
+ while (++seg_iter) {
+ Segment* segment = seg_iter.value();
+
+ if (!segment->video_data) {
+ VideoDX9SegmentData* seg_data = new(__FILE__,__LINE__) VideoDX9SegmentData;
+
+ int num_tris = 0;
+ for (int i = 0; i < segment->npolys; i++)
+ num_tris += segment->polys[i].nverts-2;
+
+ seg_data->first_vert = 0;
+ seg_data->num_verts = surf->NumVerts();
+ seg_data->first_index = first_index;
+ seg_data->num_tris = num_tris;
+
+ segment->video_data = seg_data;
+
+ first_index += num_tris * 3;
+ }
+ }
+ }
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+int
+VideoDX9::PrepareMaterial(Material* m)
+{
+ segment_material = m;
+ strategy = 0;
+ passes = 0;
+
+ if (m) {
+ int max_stages = 1;
+ int max_textures = 1;
+ bool multiply_add = false;
+ bool dotproduct3 = false;
+ bool vertexshader = false;
+ bool pixelshader = false;
+ DWORD vs_version = 0;
+ DWORD ps_version = 0;
+
+ VideoDX9DeviceInfo* dev_info = dx9enum->GetDeviceInfo(video_settings.GetDeviceType());
+
+ if (dev_info) {
+ max_stages = (int) dev_info->caps.MaxTextureBlendStages;
+ max_textures = (int) dev_info->caps.MaxSimultaneousTextures;
+ multiply_add = (dev_info->caps.TextureOpCaps & D3DTEXOPCAPS_MULTIPLYADD) ? true : false;
+ dotproduct3 = (dev_info->caps.TextureOpCaps & D3DTEXOPCAPS_DOTPRODUCT3) ? true : false;
+
+ vs_version = video_settings.enable_vs ? dev_info->caps.VertexShaderVersion : 0;
+ ps_version = video_settings.enable_ps ? dev_info->caps.PixelShaderVersion : 0;
+
+ vertexshader = vs_version >= D3DVS_VERSION(1,1);
+ pixelshader = ps_version >= D3DPS_VERSION(2,0);
+ }
+
+ strategy = DX9_STRATEGY_SIMPLE;
+ passes = 1;
+
+ if (m->tex_alternate) {
+ if (m->tex_detail && max_textures > 2 && max_stages > 4)
+ strategy = DX9_STRATEGY_BLEND_DETAIL;
+
+ else if (max_textures > 1 && max_stages > 3)
+ strategy = DX9_STRATEGY_BLEND;
+ }
+
+ else if (m->tex_emissive && (!m->tex_diffuse || m->tex_diffuse == m->tex_emissive)) {
+ strategy = DX9_STRATEGY_GLOW;
+ }
+
+ else if (IsSpecMapEnabled() && m->tex_specular && !m->tex_emissive) {
+ strategy = DX9_STRATEGY_SPECMAP;
+
+ if (max_textures < 2 || max_stages < 2 || !multiply_add)
+ passes = 2;
+ }
+
+ else if ((!IsSpecMapEnabled() || !m->tex_specular) && m->tex_emissive) {
+ strategy = DX9_STRATEGY_EMISSIVE;
+
+ if (max_textures < 2 || max_stages < 2)
+ passes = 2;
+ }
+
+ else if (IsSpecMapEnabled() && m->tex_specular && m->tex_emissive) {
+ strategy = DX9_STRATEGY_SPEC_EMISSIVE;
+
+ if (max_textures < 2 || max_stages < 2)
+ passes = 3;
+
+ else if (max_textures < 3 || max_stages < 3 || !multiply_add)
+ passes = 2;
+ }
+ }
+
+ return passes;
+}
+
+bool
+VideoDX9::SetupPass(int pass)
+{
+ if (pass < 0 || pass >= passes)
+ return false;
+
+ if (pass == 0) {
+ D3DMATERIAL9 d3dmtl;
+ IDirect3DTexture9* texture_0 = 0;
+ IDirect3DTexture9* texture_1 = 0;
+ IDirect3DTexture9* texture_2 = 0;
+ Bitmap* tex_bmp_0 = 0;
+ Bitmap* tex_bmp_1 = 0;
+ Bitmap* tex_bmp_2 = 0;
+ ColorValue orig_spec = segment_material->Ks;
+ HRESULT hr = E_FAIL;
+
+ if (segment_material->tex_specular && passes > 1)
+ segment_material->Ks = Color::Black;
+
+ CreateD3DMaterial(d3dmtl, *segment_material);
+ segment_material->Ks = orig_spec;
+
+ hr = d3ddevice->SetMaterial(&d3dmtl);
+
+ if (strategy == DX9_STRATEGY_SIMPLE) {
+ tex_bmp_0 = segment_material->tex_diffuse;
+ }
+
+ else if (strategy == DX9_STRATEGY_BLEND) {
+ tex_bmp_0 = segment_material->tex_diffuse;
+ tex_bmp_1 = segment_material->tex_alternate;
+ }
+
+ else if (strategy == DX9_STRATEGY_BLEND_DETAIL) {
+ tex_bmp_0 = segment_material->tex_diffuse;
+ tex_bmp_1 = segment_material->tex_alternate;
+ tex_bmp_2 = segment_material->tex_detail;
+ }
+
+ else if (strategy == DX9_STRATEGY_SPECMAP) {
+ if (passes == 1) {
+ tex_bmp_0 = segment_material->tex_diffuse;
+ tex_bmp_1 = segment_material->tex_specular;
+ }
+ else {
+ tex_bmp_0 = segment_material->tex_diffuse;
+ }
+ }
+
+ else if (strategy == DX9_STRATEGY_EMISSIVE && passes == 1 ||
+ strategy == DX9_STRATEGY_SPEC_EMISSIVE && passes == 2) {
+ if (segment_material->tex_diffuse) {
+ tex_bmp_0 = segment_material->tex_diffuse;
+ tex_bmp_1 = segment_material->tex_emissive;
+ }
+ else {
+ tex_bmp_0 = segment_material->tex_emissive;
+ }
+ }
+
+ else {
+ tex_bmp_0 = segment_material->tex_emissive;
+ }
+
+ if (texcache && tex_bmp_0) {
+ texture_0 = texcache->FindTexture(tex_bmp_0);
+
+ hr = d3ddevice->SetTexture(0, texture_0);
+ current_texture = texture_0;
+
+ if (tex_bmp_1) {
+ if (strategy >= DX9_STRATEGY_BLEND) {
+ texture_1 = texcache->FindTexture(tex_bmp_1);
+ hr = d3ddevice->SetTexture(1, texture_1);
+
+ if (tex_bmp_2) {
+ texture_2 = texcache->FindTexture(tex_bmp_2);
+ hr = d3ddevice->SetTexture(2, texture_2);
+ }
+ }
+
+ else {
+ texture_1 = texcache->FindTexture(tex_bmp_1);
+ hr = d3ddevice->SetTexture(1, texture_1);
+
+ if (tex_bmp_2) {
+ texture_2 = texcache->FindTexture(tex_bmp_2);
+ hr = d3ddevice->SetTexture(2, texture_2);
+ }
+ }
+ }
+ }
+ else {
+ hr = d3ddevice->SetTexture(0, 0);
+ current_texture = 0;
+ }
+
+ SetBlendType(segment_material->blend);
+
+ if (texture_0 && texture_1 && strategy == DX9_STRATEGY_BLEND) {
+ d3ddevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(1, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
+
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);
+
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_BLENDCURRENTALPHA);
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_CURRENT);
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
+ d3ddevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);
+
+ d3ddevice->SetTextureStageState(2, D3DTSS_COLOROP, D3DTOP_MODULATE);
+ d3ddevice->SetTextureStageState(2, D3DTSS_COLORARG1, D3DTA_CURRENT);
+ d3ddevice->SetTextureStageState(2, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
+ d3ddevice->SetTextureStageState(2, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(2, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
+
+ d3ddevice->SetTextureStageState(3, D3DTSS_COLOROP, D3DTOP_DISABLE);
+ }
+
+ else if (texture_0 && texture_1 && texture_2 && strategy == DX9_STRATEGY_BLEND_DETAIL) {
+ d3ddevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(1, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(2, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(2, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(2, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
+
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
+
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_BLENDCURRENTALPHA);
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_CURRENT);
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
+ d3ddevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);
+
+ d3ddevice->SetTextureStageState(2, D3DTSS_COLOROP, D3DTOP_BLENDTEXTUREALPHA);
+ d3ddevice->SetTextureStageState(2, D3DTSS_COLORARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(2, D3DTSS_COLORARG2, D3DTA_CURRENT);
+ d3ddevice->SetTextureStageState(2, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(2, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
+ d3ddevice->SetTextureStageState(2, D3DTSS_TEXCOORDINDEX, 1);
+
+ d3ddevice->SetTextureStageState(3, D3DTSS_COLOROP, D3DTOP_MODULATE);
+ d3ddevice->SetTextureStageState(3, D3DTSS_COLORARG1, D3DTA_CURRENT);
+ d3ddevice->SetTextureStageState(3, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
+ d3ddevice->SetTextureStageState(3, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(3, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
+ }
+
+ else if (texture_0 && strategy == DX9_STRATEGY_GLOW) {
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_CURRENT);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
+ }
+
+ else if (texture_0) {
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_CURRENT);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
+ }
+
+ if (texture_1 && strategy == DX9_STRATEGY_SPECMAP) {
+ d3ddevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(1, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
+
+ d3ddevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MULTIPLYADD);
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_SPECULAR);
+ d3ddevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
+ }
+
+ else if (texture_1 && strategy == DX9_STRATEGY_EMISSIVE) {
+ d3ddevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(1, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
+
+ d3ddevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_ADD);
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT);
+ d3ddevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
+ }
+
+ else if (texture_1 && strategy == DX9_STRATEGY_SPEC_EMISSIVE) {
+ d3ddevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
+ d3ddevice->SetSamplerState(1, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
+
+ d3ddevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_ADD);
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT);
+ d3ddevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(1, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
+ }
+
+ else if (strategy < DX9_STRATEGY_BLEND) {
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
+ }
+ }
+
+ else if (pass == 1) {
+ D3DMATERIAL9 d3dmtl;
+ IDirect3DTexture9* texture = 0;
+ Bitmap* tex_bmp = 0;
+ ColorValue orig_Ka = segment_material->Ka;
+ ColorValue orig_Kd = segment_material->Kd;
+ HRESULT hr = E_FAIL;
+
+ if (segment_material->tex_specular) {
+ segment_material->Ka = Color::Black;
+ segment_material->Kd = Color::Black;
+ }
+
+ CreateD3DMaterial(d3dmtl, *segment_material);
+
+ segment_material->Ka = orig_Ka;
+ segment_material->Kd = orig_Kd;
+
+ hr = d3ddevice->SetMaterial(&d3dmtl);
+
+ if (strategy == DX9_STRATEGY_SPECMAP ||
+ strategy == DX9_STRATEGY_SPEC_EMISSIVE) {
+ tex_bmp = segment_material->tex_specular;
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_SPECULAR);
+ }
+
+ else if (strategy == DX9_STRATEGY_EMISSIVE) {
+ tex_bmp = segment_material->tex_emissive;
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
+ d3ddevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
+ }
+
+ d3ddevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);
+
+ if (texcache && tex_bmp) {
+ texture = texcache->FindTexture(tex_bmp);
+ hr = d3ddevice->SetTexture(0, texture);
+ current_texture = texture;
+
+ SetBlendType(BLEND_ADDITIVE);
+ }
+ }
+
+ if (render_state[STENCIL_ENABLE]) {
+ d3ddevice->SetRenderState(D3DRS_STENCILENABLE, TRUE);
+ d3ddevice->SetRenderState(D3DRS_STENCILREF, 0x01);
+ d3ddevice->SetRenderState(D3DRS_STENCILFUNC, D3DCMP_GREATER);
+ }
+ else {
+ d3ddevice->SetRenderState(D3DRS_STENCILENABLE, FALSE);
+ }
+
+ if (render_state[LIGHTING_PASS] > 0) {
+ SetBlendType(BLEND_ADDITIVE);
+ SetRenderState(Z_WRITE_ENABLE, FALSE);
+ }
+
+ return true;
+}
+
+
+// +--------------------------------------------------------------------+
+
+void
+VideoDX9Error(const char* msg, HRESULT err)
+{
+ Print(" VideoDX9: %s. [%s]\n", msg, D3DErrStr(err));
+}
+
+char* D3DErrStr(HRESULT hr)
+{
+ static char errstrbuf[128];
+
+ switch (hr) {
+ default:
+ sprintf(errstrbuf, "Unrecognized error value = %08x.", hr);
+ return errstrbuf;
+
+ case D3D_OK:
+ return "No error.";
+
+ case D3DERR_WRONGTEXTUREFORMAT:
+ return "Wrong texture format.";
+
+ case D3DERR_UNSUPPORTEDCOLOROPERATION:
+ return "Unsupported color operation.";
+
+ case D3DERR_UNSUPPORTEDCOLORARG:
+ return "Unsupported color argument.";
+
+ case D3DERR_UNSUPPORTEDALPHAOPERATION:
+ return "Unsupported alpha operation.";
+
+ case D3DERR_UNSUPPORTEDALPHAARG:
+ return "Unsupported alpha argument.";
+
+ case D3DERR_TOOMANYOPERATIONS:
+ return "Too many operations.";
+
+ case D3DERR_CONFLICTINGTEXTUREFILTER:
+ return "Conflicting texture filter.";
+
+ case D3DERR_UNSUPPORTEDFACTORVALUE:
+ return "Unsupported factor value.";
+
+ case D3DERR_CONFLICTINGRENDERSTATE:
+ return "Conflicting render state.";
+
+ case D3DERR_UNSUPPORTEDTEXTUREFILTER:
+ return "Unsupported texture filter.";
+
+ case D3DERR_CONFLICTINGTEXTUREPALETTE:
+ return "Conflicting texture palette.";
+
+ case D3DERR_DRIVERINTERNALERROR:
+ return "Driver internal error.";
+
+
+ case D3DERR_NOTFOUND:
+ return "Resource was not found.";
+
+ case D3DERR_MOREDATA:
+ return "More data?";
+
+ case D3DERR_DEVICELOST:
+ return "Device lost.";
+
+ case D3DERR_DEVICENOTRESET:
+ return "Device is not reset.";
+
+ case D3DERR_NOTAVAILABLE:
+ return "Not available.";
+
+ case D3DERR_OUTOFVIDEOMEMORY:
+ return "Out of video memory.";
+
+ case E_OUTOFMEMORY:
+ return "Out of system memory.";
+
+ case D3DERR_INVALIDDEVICE:
+ return "Invalid device selection.";
+
+ case D3DERR_INVALIDCALL:
+ return "Invalid call or parameter.";
+
+ case D3DERR_DRIVERINVALIDCALL:
+ return "Driver invalid call.";
+
+ case D3DERR_WASSTILLDRAWING:
+ return "The device was still drawing.";
+
+ case D3DOK_NOAUTOGEN:
+ return "Autogeneration is not supported by this device.";
+
+ }
+}
+
+ \ No newline at end of file
diff --git a/nGenEx/VideoDX9.h b/nGenEx/VideoDX9.h
new file mode 100644
index 0000000..d89bb77
--- /dev/null
+++ b/nGenEx/VideoDX9.h
@@ -0,0 +1,195 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: VideoDX9.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Direct3D and Direct3D Video classes for DirectX 7
+*/
+
+#ifndef VideoDX9_h
+#define VideoDX9_h
+
+#include "Video.h"
+#include "VideoSettings.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class VideoDX9;
+class VideoDX9Enum;
+class VideoDX9VertexBuffer;
+class VideoDX9IndexBuffer;
+struct VideoDX9ScreenVertex;
+class Surface;
+class Segment;
+
+struct VideoDX9ScreenVertex;
+struct VideoDX9SolidVertex;
+struct VideoDX9LuminousVertex;
+struct VideoDX9LineVertex;
+
+
+// +--------------------------------------------------------------------+
+
+class VideoDX9 : public Video
+{
+public:
+ VideoDX9(const HWND& window, VideoSettings* vs);
+ virtual ~VideoDX9();
+
+ virtual const VideoSettings*
+ GetVideoSettings() const { return &video_settings; }
+ virtual bool SetVideoSettings(const VideoSettings* vs);
+
+ virtual bool SetBackgroundColor(Color c);
+ virtual bool SetGammaLevel(int g);
+ virtual bool SetObjTransform(const Matrix& o, const Point& l);
+
+ virtual bool SetupParams();
+ virtual bool Reset(const VideoSettings* vs);
+
+ virtual bool StartFrame();
+ virtual bool EndFrame();
+
+ virtual int Width() const { return width; }
+ virtual int Height() const { return height; }
+ virtual int Depth() const { return bpp; }
+
+ virtual void RecoverSurfaces();
+
+ virtual bool ClearAll();
+ virtual bool ClearDepthBuffer();
+ virtual bool Present();
+ virtual bool Pause();
+ virtual bool Resume();
+
+ virtual IDirect3D9* Direct3D() const { return d3d; }
+ virtual IDirect3DDevice9* D3DDevice() const { return d3ddevice; }
+ static IDirect3DDevice9* GetD3DDevice9();
+
+ virtual bool IsModeSupported(int width, int height, int bpp) const;
+ virtual bool IsHardware() const { return true; }
+ virtual int ZDepth() const { return zdepth; }
+ virtual DWORD VidMemFree() const;
+ virtual int D3DLevel() const { return 9; }
+ virtual int MaxTexSize() const;
+ virtual int MaxTexAspect() const;
+ virtual int GammaLevel() const { return gamma; }
+
+ virtual bool Capture(Bitmap& bmp);
+ virtual bool GetWindowRect(Rect& r);
+ virtual bool SetWindowRect(const Rect& r);
+ virtual bool SetViewport(int x, int y, int w, int h);
+ virtual bool SetCamera(const Camera* cam);
+ virtual bool SetEnvironment(Bitmap** faces);
+ virtual bool SetAmbient(Color c);
+ virtual bool SetLights(const List<Light>& lights);
+ virtual bool SetProjection(float fov,
+ float znear=1.0f,
+ float zfar=1.0e6f,
+ DWORD type=PROJECTION_PERSPECTIVE);
+ virtual bool SetRenderState(RENDER_STATE state, DWORD value);
+ virtual bool SetBlendType(int blend_type);
+
+ virtual bool DrawPolys(int npolys, Poly* p);
+ virtual bool DrawScreenPolys(int npolys, Poly* p, int blend=0);
+ virtual bool DrawSolid(Solid* s, DWORD blend_modes=0xf);
+ virtual bool DrawShadow(Solid* s, int nverts, Vec3* verts, bool vis=false);
+ virtual bool DrawLines(int nlines, Vec3* v, Color c, int blend=0);
+ virtual bool DrawScreenLines(int nlines, float* v, Color c, int blend=0);
+ virtual bool DrawPoints(VertexSet* v);
+ virtual bool DrawPolyOutline(Poly* p);
+ virtual bool UseMaterial(Material* m);
+
+ virtual bool UseXFont(const char* name, int size, bool b, bool i);
+ virtual bool DrawText(const char* text, int count, const Rect& rect,
+ DWORD format, Color c);
+
+ virtual void PreloadTexture(Bitmap* bmp);
+ virtual void PreloadSurface(Surface* s);
+ virtual void InvalidateCache();
+
+ static void CreateD3DMatrix(D3DMATRIX& result, const Matrix& m, const Point& p);
+ static void CreateD3DMatrix(D3DMATRIX& result, const Matrix& m, const Vec3& v);
+ static void CreateD3DMaterial(D3DMATERIAL9& result, const Material& mtl);
+
+private:
+ bool CreateBuffers();
+ bool DestroyBuffers();
+ bool PopulateScreenVerts(VertexSet* vset);
+ bool PrepareSurface(Surface* s);
+ bool DrawSegment(Segment* s);
+
+ int PrepareMaterial(Material* m);
+ bool SetupPass(int n);
+
+ HWND hwnd;
+ int width;
+ int height;
+ int bpp;
+ int gamma;
+ int zdepth;
+ Color background;
+
+ VideoDX9Enum* dx9enum;
+ VideoSettings video_settings;
+
+ IDirect3D9* d3d;
+ IDirect3DDevice9* d3ddevice;
+ D3DPRESENT_PARAMETERS d3dparams;
+ D3DSURFACE_DESC back_buffer_desc;
+ bool device_lost;
+
+ BYTE* surface;
+
+ DWORD texture_format[3];
+ D3DGAMMARAMP gamma_ramp;
+ double fade;
+
+ Rect rect;
+
+ IDirect3DVertexDeclaration9* vertex_declaration;
+ ID3DXEffect* magic_fx;
+ BYTE* magic_fx_code;
+ int magic_fx_code_len;
+
+ IDirect3DTexture9* current_texture;
+ int current_blend_state;
+ int scene_active;
+ DWORD render_state[RENDER_STATE_MAX];
+ Material* use_material;
+
+ Material* segment_material;
+ int strategy;
+ int passes;
+
+ ID3DXFont* d3dx_font;
+ char font_name[64];
+ int font_size;
+ bool font_bold;
+ bool font_ital;
+
+ Color ambient;
+ int nlights;
+
+ int first_vert;
+ int num_verts;
+
+ VideoDX9VertexBuffer* screen_vbuf;
+ VideoDX9IndexBuffer* screen_ibuf;
+ VideoDX9ScreenVertex* font_verts;
+ WORD* font_indices;
+ int font_nverts;
+
+ VideoDX9ScreenVertex* screen_line_verts;
+ VideoDX9LineVertex* line_verts;
+};
+
+#endif VideoDX9_h
+
diff --git a/nGenEx/VideoDX9Enum.cpp b/nGenEx/VideoDX9Enum.cpp
new file mode 100644
index 0000000..ca2ca7a
--- /dev/null
+++ b/nGenEx/VideoDX9Enum.cpp
@@ -0,0 +1,1057 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: VideoDX9Enum.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Direct3D Video class for DirectX 9
+*/
+
+#include "MemDebug.h"
+#include "VideoDX9Enum.h"
+#include "VideoSettings.h"
+#include "Color.h"
+
+// +--------------------------------------------------------------------+
+
+void Print(const char* msg, ...);
+char* DDErrStr(HRESULT dderr);
+void VideoDX9Error(const char* msg, HRESULT dderr);
+int VD3D_describe_things;
+
+#ifndef RELEASE
+#define RELEASE(x) if (x) { x->Release(); x=NULL; }
+#endif
+
+
+// +--------------------------------------------------------------------+
+
+static UINT GetBitsPerPixel(D3DFORMAT fmt)
+{
+ switch (fmt) {
+ case D3DFMT_A8R8G8B8: return 32;
+ case D3DFMT_X8R8G8B8: return 32;
+ case D3DFMT_A2B10G10R10: return 32;
+ case D3DFMT_A2R10G10B10: return 32;
+ case D3DFMT_R8G8B8: return 24;
+ case D3DFMT_R5G6B5: return 16;
+ case D3DFMT_X1R5G5B5: return 16;
+ case D3DFMT_A1R5G5B5: return 16;
+ case D3DFMT_A4R4G4B4: return 16;
+ case D3DFMT_A8R3G3B2: return 16;
+ case D3DFMT_X4R4G4B4: return 16;
+ case D3DFMT_R3G3B2: return 8;
+ default: return 0;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+static UINT GetColorChannelBits(D3DFORMAT fmt)
+{
+ switch (fmt) {
+ case D3DFMT_A2B10G10R10: return 10;
+ case D3DFMT_A2R10G10B10: return 10;
+ case D3DFMT_R8G8B8: return 8;
+ case D3DFMT_A8R8G8B8: return 8;
+ case D3DFMT_X8R8G8B8: return 8;
+ case D3DFMT_R5G6B5: return 6;
+ case D3DFMT_X1R5G5B5: return 5;
+ case D3DFMT_A1R5G5B5: return 5;
+ case D3DFMT_A4R4G4B4: return 4;
+ case D3DFMT_R3G3B2: return 2;
+ case D3DFMT_A8R3G3B2: return 2;
+ case D3DFMT_X4R4G4B4: return 4;
+ default: return 0;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+static UINT GetAlphaChannelBits(D3DFORMAT fmt)
+{
+ switch (fmt) {
+ case D3DFMT_R8G8B8: return 0;
+ case D3DFMT_A8R8G8B8: return 8;
+ case D3DFMT_X8R8G8B8: return 0;
+ case D3DFMT_R5G6B5: return 0;
+ case D3DFMT_X1R5G5B5: return 0;
+ case D3DFMT_A1R5G5B5: return 1;
+ case D3DFMT_A4R4G4B4: return 4;
+ case D3DFMT_R3G3B2: return 0;
+ case D3DFMT_A8R3G3B2: return 8;
+ case D3DFMT_X4R4G4B4: return 0;
+ case D3DFMT_A2B10G10R10: return 2;
+ case D3DFMT_A2R10G10B10: return 2;
+ default: return 0;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+static UINT GetDepthBits(D3DFORMAT fmt)
+{
+ switch (fmt) {
+ case D3DFMT_D16: return 16;
+ case D3DFMT_D15S1: return 15;
+ case D3DFMT_D24X8: return 24;
+ case D3DFMT_D24S8: return 24;
+ case D3DFMT_D24X4S4: return 24;
+ case D3DFMT_D32: return 32;
+ default: return 0;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+static UINT GetStencilBits(D3DFORMAT fmt)
+{
+ switch (fmt) {
+ case D3DFMT_D16: return 0;
+ case D3DFMT_D15S1: return 1;
+ case D3DFMT_D24X8: return 0;
+ case D3DFMT_D24S8: return 8;
+ case D3DFMT_D24X4S4: return 4;
+ case D3DFMT_D32: return 0;
+ default: return 0;
+ }
+}
+
+// +--------------------------------------------------------------------+
+//
+// This routine prints a text description of the indicated driver
+// into the error log file.
+
+static void DescribeGUID(GUID* lpGUID)
+{
+ if (lpGUID)
+ Print(" GUID: %08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
+ lpGUID->Data1, lpGUID->Data2, lpGUID->Data3,
+ lpGUID->Data4[0], lpGUID->Data4[1],
+ lpGUID->Data4[2], lpGUID->Data4[3],
+ lpGUID->Data4[4], lpGUID->Data4[5],
+ lpGUID->Data4[6], lpGUID->Data4[7]);
+ else
+ Print(" GUID IS NULL!\n");
+}
+
+
+// +--------------------------------------------------------------------+
+
+VideoDX9Enum::VideoDX9Enum(IDirect3D9* d3d9)
+{
+ if (d3d9) {
+ d3d = d3d9;
+ d3d->AddRef();
+ }
+
+ min_width = 640;
+ min_height = 480;
+ min_color_bits = 5;
+ min_alpha_bits = 0;
+ min_depth_bits = 16;
+ min_stencil_bits = 0;
+
+ uses_depth_buffer = false;
+ uses_mixed_vp = false;
+ req_windowed = false;
+ req_fullscreen = false;
+
+ adapter_index = 0;
+}
+
+VideoDX9Enum::~VideoDX9Enum()
+{
+ adapter_info_list.destroy();
+ RELEASE(d3d);
+}
+
+void
+VideoDX9Enum::SetDirect3D9(IDirect3D9* d3d9)
+{
+ RELEASE(d3d);
+
+ if (d3d9) {
+ d3d = d3d9;
+ d3d->AddRef();
+ }
+}
+
+
+// +--------------------------------------------------------------------+
+
+void
+VideoDX9Enum::SelectAdapter(int index)
+{
+ if (index >= 0 && index < adapter_info_list.size())
+ adapter_index = index;
+}
+
+VideoDX9AdapterInfo*
+VideoDX9Enum::GetAdapterInfo()
+{
+ if (adapter_index >= 0 && adapter_index < adapter_info_list.size())
+ return adapter_info_list[adapter_index];
+
+ return 0;
+}
+
+VideoDX9DeviceInfo*
+VideoDX9Enum::GetDeviceInfo(DWORD devtype)
+{
+ if (adapter_index >= 0 && adapter_index < adapter_info_list.size()) {
+ VideoDX9AdapterInfo* adapter = adapter_info_list[adapter_index];
+
+ if (adapter) {
+ ListIter<VideoDX9DeviceInfo> iter = adapter->device_info_list;
+ while (++iter) {
+ VideoDX9DeviceInfo* dev_info = iter.value();
+
+ if (dev_info->device_type == (D3DDEVTYPE) devtype)
+ return dev_info;
+ }
+ }
+ }
+
+ return 0;
+}
+
+bool
+VideoDX9Enum::IsModeSupported(int w, int h, int b) const
+{
+ if (adapter_index >= 0 && adapter_index < adapter_info_list.size()) {
+ VideoDX9AdapterInfo* adapter = adapter_info_list[adapter_index];
+
+ ListIter<VideoDX9DisplayMode> mode_iter = adapter->display_mode_list;
+ while (++mode_iter) {
+ VideoDX9DisplayMode* mode = mode_iter.value();
+
+ if (mode->width == (UINT) w &&
+ mode->height == (UINT) h &&
+ GetBitsPerPixel(mode->format) == (UINT) b) {
+
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+HRESULT
+VideoDX9Enum::Enumerate()
+{
+ HRESULT hr = E_FAIL;
+
+ if (!d3d)
+ return hr;
+
+ if (VD3D_describe_things > 0) {
+ Print("Video DX9 Enumerate Adapters and Devices\n");
+ Print("----------------------------------------\n\n");
+ }
+
+ allowed_adapter_format_list.append(D3DFMT_X8R8G8B8);
+ allowed_adapter_format_list.append(D3DFMT_X1R5G5B5);
+ allowed_adapter_format_list.append(D3DFMT_R5G6B5);
+ allowed_adapter_format_list.append(D3DFMT_A2R10G10B10);
+
+ VideoDX9AdapterInfo* adapter_info = 0;
+ ArrayList adapter_format_list;
+ UINT num_adapters = d3d->GetAdapterCount();
+
+ for (UINT ordinal = 0; ordinal < num_adapters; ordinal++) {
+ adapter_info = new(__FILE__,__LINE__) VideoDX9AdapterInfo;
+ if (!adapter_info)
+ return E_OUTOFMEMORY;
+
+ adapter_info->adapter_ordinal = ordinal;
+ d3d->GetAdapterIdentifier(ordinal, 0, &adapter_info->adapter_identifier);
+
+ // Get list of all display modes on this adapter.
+ // Also build a temporary list of all display adapter formats.
+ adapter_format_list.clear();
+
+ for (int iaaf = 0; iaaf < allowed_adapter_format_list.size(); iaaf++) {
+ D3DFORMAT allowed_adapter_format = (D3DFORMAT) allowed_adapter_format_list[iaaf];
+ UINT num_adapter_modes = d3d->GetAdapterModeCount(ordinal, allowed_adapter_format);
+
+ for (UINT mode = 0; mode < num_adapter_modes; mode++) {
+ D3DDISPLAYMODE display_mode;
+ d3d->EnumAdapterModes(ordinal, allowed_adapter_format, mode, &display_mode);
+
+ if (display_mode.Width < min_width ||
+ display_mode.Height < min_height ||
+ GetColorChannelBits(display_mode.Format) < min_color_bits) {
+ continue;
+ }
+
+ VideoDX9DisplayMode* dx9_display_mode = new(__FILE__,__LINE__) VideoDX9DisplayMode(display_mode);
+
+ if (!dx9_display_mode) {
+ delete adapter_info;
+ return E_OUTOFMEMORY;
+ }
+
+ adapter_info->display_mode_list.append(dx9_display_mode);
+
+ if (!adapter_format_list.contains(display_mode.Format))
+ adapter_format_list.append(display_mode.Format);
+ }
+ }
+
+ // Sort displaymode list
+ adapter_info->display_mode_list.sort();
+
+ if (VD3D_describe_things > 0) {
+ Print("Adapter %d. %s\n", ordinal, adapter_info->adapter_identifier.Description);
+ DescribeGUID(&adapter_info->adapter_identifier.DeviceIdentifier);
+
+ if (VD3D_describe_things > 4) {
+ ListIter<VideoDX9DisplayMode> m_iter = adapter_info->display_mode_list;
+ while (++m_iter) {
+ VideoDX9DisplayMode* m = m_iter.value();
+
+ Print(" Mode %3d %s\n", m_iter.index(), m->GetDescription());
+ }
+
+ Print("\n");
+ }
+ }
+
+ // Get info for each device on this adapter
+ if (FAILED(hr = EnumerateDevices(adapter_info, adapter_format_list))) {
+ delete adapter_info;
+ return hr;
+ }
+
+ // If at least one device on this adapter is available and compatible
+ // with the app, add the adapterInfo to the list
+ if (adapter_info->device_info_list.size() == 0)
+ delete adapter_info;
+ else
+ adapter_info_list.append(adapter_info);
+
+ if (VD3D_describe_things > 0) {
+ Print("\n");
+ }
+ }
+
+ return S_OK;
+}
+
+// +--------------------------------------------------------------------+
+
+HRESULT
+VideoDX9Enum::EnumerateDevices(VideoDX9AdapterInfo* adapter_info, ArrayList& adapter_format_list)
+{
+ HRESULT hr = E_FAIL;
+ const D3DDEVTYPE dtypes[3] = { D3DDEVTYPE_HAL, D3DDEVTYPE_SW, D3DDEVTYPE_REF };
+ const char* dtypestr[3] = { "D3DDEVTYPE_HAL", "D3DDEVTYPE_SW", "D3DDEVTYPE_REF" };
+ VideoDX9DeviceInfo* device_info = 0;
+
+ for (int i = 0; i < 3; i++ ) {
+ device_info = new(__FILE__,__LINE__) VideoDX9DeviceInfo;
+ if (!device_info)
+ return E_OUTOFMEMORY;
+
+ device_info->adapter_ordinal = adapter_info->adapter_ordinal;
+ device_info->device_type = dtypes[i];
+
+ if (FAILED(d3d->GetDeviceCaps(adapter_info->adapter_ordinal,
+ device_info->device_type,
+ &device_info->caps))) {
+ delete device_info;
+ continue;
+ }
+
+ if (VD3D_describe_things > 1) {
+ Print(" Device %d - %s\n", i, dtypestr[i]);
+ Print(" Max Texture Width: %d\n", device_info->caps.MaxTextureWidth);
+ Print(" Max Texture Height: %d\n", device_info->caps.MaxTextureHeight);
+ }
+
+ // Get info for each device combo on this device
+ if (FAILED(hr = EnumerateDeviceCombos(device_info, adapter_format_list))) {
+ delete device_info;
+ return hr;
+ }
+
+ // make sure at least one devicecombo for this device was found
+ if (device_info->device_combo_list.size() < 1) {
+ delete device_info;
+ continue;
+ }
+
+ adapter_info->device_info_list.append(device_info);
+ }
+
+ return S_OK;
+}
+
+// +--------------------------------------------------------------------+
+
+HRESULT
+VideoDX9Enum::EnumerateDeviceCombos(VideoDX9DeviceInfo* device_info, ArrayList& adapter_format_list)
+{
+ const D3DFORMAT back_buffer_formats[] = {
+ D3DFMT_A8R8G8B8,
+ D3DFMT_X8R8G8B8,
+ D3DFMT_R8G8B8,
+ D3DFMT_R5G6B5,
+ D3DFMT_A1R5G5B5,
+ D3DFMT_X1R5G5B5
+ };
+
+ bool is_windowed[] = { false, true };
+
+ // See which adapter formats are supported by this device
+ D3DFORMAT a_fmt;
+ for (int i = 0; i < adapter_format_list.size(); i++) {
+ a_fmt = (D3DFORMAT) adapter_format_list[i];
+
+ D3DFORMAT b_fmt;
+ for (int n = 0; n < 6; n++) {
+ b_fmt = back_buffer_formats[n];
+
+ if (GetAlphaChannelBits(b_fmt) < min_alpha_bits)
+ continue;
+
+ bool win;
+ for (int w = 0; w < 2; w++) {
+ win = is_windowed[w];
+
+ if (!win && req_windowed)
+ continue;
+
+ if (win && req_fullscreen)
+ continue;
+
+ if (FAILED(d3d->CheckDeviceType(device_info->adapter_ordinal,
+ device_info->device_type,
+ a_fmt,
+ b_fmt,
+ win))) {
+ continue;
+ }
+
+ // At this point, we have an adapter/device/adapterformat/backbufferformat/iswindowed
+ // DeviceCombo that is supported by the system. We still need to confirm that it's
+ // compatible with the app, and find one or more suitable depth/stencil buffer format,
+ // multisample type, vertex processing type, and present interval.
+
+ VideoDX9DeviceCombo* device_combo = 0;
+ device_combo = new(__FILE__,__LINE__) VideoDX9DeviceCombo;
+ if (!device_combo)
+ return E_OUTOFMEMORY;
+
+ device_combo->adapter_ordinal = device_info->adapter_ordinal;
+ device_combo->device_type = device_info->device_type;
+ device_combo->adapter_format = a_fmt;
+ device_combo->back_buffer_format = b_fmt;
+ device_combo->is_windowed = win;
+
+ if (uses_depth_buffer) {
+ BuildDepthStencilFormatList(device_combo);
+ if (device_combo->depth_stencil_fmt_list.size() < 1) {
+ delete device_combo;
+ continue;
+ }
+ }
+
+ BuildMultiSampleTypeList(device_combo);
+ if (device_combo->multisample_type_list.size() < 1) {
+ delete device_combo;
+ continue;
+ }
+
+ BuildDSMSConflictList(device_combo);
+
+ BuildVertexProcessingTypeList(device_info, device_combo);
+ if (device_combo->vertex_processing_list.size() < 1) {
+ delete device_combo;
+ continue;
+ }
+
+ BuildPresentIntervalList(device_info, device_combo);
+
+ device_info->device_combo_list.append(device_combo);
+ }
+ }
+ }
+
+ return S_OK;
+}
+
+void
+VideoDX9Enum::BuildDepthStencilFormatList(VideoDX9DeviceCombo* device_combo)
+{
+ const D3DFORMAT depth_stencil_formats[] = {
+ D3DFMT_D32,
+ D3DFMT_D24S8,
+ D3DFMT_D24X4S4,
+ D3DFMT_D24X8,
+ D3DFMT_D16,
+ D3DFMT_D15S1,
+ };
+
+ for (int i = 0; i < 6; i++) {
+ D3DFORMAT fmt = depth_stencil_formats[i];
+
+ if (GetDepthBits(fmt) < min_depth_bits)
+ continue;
+
+ if (GetStencilBits(fmt) < min_stencil_bits)
+ continue;
+
+ if (SUCCEEDED(d3d->CheckDeviceFormat(device_combo->adapter_ordinal,
+ device_combo->device_type,
+ device_combo->adapter_format,
+ D3DUSAGE_DEPTHSTENCIL,
+ D3DRTYPE_SURFACE,
+ fmt))) {
+
+ if (SUCCEEDED(d3d->CheckDepthStencilMatch(device_combo->adapter_ordinal,
+ device_combo->device_type,
+ device_combo->adapter_format,
+ device_combo->back_buffer_format,
+ fmt))) {
+
+ device_combo->depth_stencil_fmt_list.append(fmt);
+ }
+ }
+ }
+}
+
+void
+VideoDX9Enum::BuildMultiSampleTypeList(VideoDX9DeviceCombo* device_combo)
+{
+ const D3DMULTISAMPLE_TYPE multisample_type_array[] = {
+ D3DMULTISAMPLE_NONE,
+ D3DMULTISAMPLE_NONMASKABLE,
+ D3DMULTISAMPLE_2_SAMPLES,
+ D3DMULTISAMPLE_3_SAMPLES,
+ D3DMULTISAMPLE_4_SAMPLES,
+ D3DMULTISAMPLE_5_SAMPLES,
+ D3DMULTISAMPLE_6_SAMPLES,
+ D3DMULTISAMPLE_7_SAMPLES,
+ D3DMULTISAMPLE_8_SAMPLES,
+ D3DMULTISAMPLE_9_SAMPLES,
+ D3DMULTISAMPLE_10_SAMPLES,
+ D3DMULTISAMPLE_11_SAMPLES,
+ D3DMULTISAMPLE_12_SAMPLES,
+ D3DMULTISAMPLE_13_SAMPLES,
+ D3DMULTISAMPLE_14_SAMPLES,
+ D3DMULTISAMPLE_15_SAMPLES,
+ D3DMULTISAMPLE_16_SAMPLES,
+ };
+
+ for (int i = 0; i < 17; i++) {
+ D3DMULTISAMPLE_TYPE multisample_type = multisample_type_array[i];
+ DWORD multisample_qual = 0;
+
+ if (SUCCEEDED(d3d->CheckDeviceMultiSampleType(device_combo->adapter_ordinal,
+ device_combo->device_type,
+ device_combo->back_buffer_format,
+ device_combo->is_windowed,
+ multisample_type,
+ &multisample_qual))) {
+
+ device_combo->multisample_type_list.append(multisample_type);
+ device_combo->multisample_qual_list.append(multisample_qual);
+ }
+ }
+}
+
+void
+VideoDX9Enum::BuildDSMSConflictList(VideoDX9DeviceCombo* device_combo)
+{
+ for (int i = 0; i < device_combo->depth_stencil_fmt_list.size(); i++) {
+ D3DFORMAT depth_format = (D3DFORMAT) device_combo->depth_stencil_fmt_list[i];
+
+ for (int n = 0; n < device_combo->multisample_type_list.size(); n++) {
+ D3DMULTISAMPLE_TYPE multisample_type = (D3DMULTISAMPLE_TYPE) device_combo->multisample_type_list[n];
+
+ if (FAILED(d3d->CheckDeviceMultiSampleType(device_combo->adapter_ordinal,
+ device_combo->device_type,
+ depth_format,
+ device_combo->is_windowed,
+ multisample_type,
+ NULL))) {
+
+ VideoDX9FormatConflict* conflict = new(__FILE__,__LINE__) VideoDX9FormatConflict;
+
+ conflict->ds_format = depth_format;
+ conflict->multisample_type = multisample_type;
+
+ device_combo->conflict_list.append(conflict);
+ }
+ }
+ }
+}
+
+void
+VideoDX9Enum::BuildVertexProcessingTypeList(VideoDX9DeviceInfo* device_info, VideoDX9DeviceCombo* device_combo)
+{
+ if ((device_info->caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) != 0) {
+ if ((device_info->caps.DevCaps & D3DDEVCAPS_PUREDEVICE) != 0) {
+ device_combo->vertex_processing_list.append(PURE_HARDWARE_VP);
+ }
+
+ device_combo->vertex_processing_list.append(HARDWARE_VP);
+
+ if (uses_mixed_vp) {
+ device_combo->vertex_processing_list.append(MIXED_VP);
+ }
+ }
+
+ device_combo->vertex_processing_list.append(SOFTWARE_VP);
+}
+
+void
+VideoDX9Enum::BuildPresentIntervalList(VideoDX9DeviceInfo* device_info, VideoDX9DeviceCombo* device_combo)
+{
+ const DWORD present_interval_array[] = {
+ D3DPRESENT_INTERVAL_IMMEDIATE,
+ D3DPRESENT_INTERVAL_DEFAULT,
+ D3DPRESENT_INTERVAL_ONE,
+ D3DPRESENT_INTERVAL_TWO,
+ D3DPRESENT_INTERVAL_THREE,
+ D3DPRESENT_INTERVAL_FOUR,
+ };
+
+ for (int i = 0; i < 6; i++) {
+ DWORD interval = present_interval_array[i];
+
+ if (device_combo->is_windowed && i > 2) {
+ // none of the remaining intervals are supported in windowed mode.
+ break;
+ }
+
+ // Note that D3DPRESENT_INTERVAL_DEFAULT is zero, so you
+ // can't do a caps check for it -- it is always available.
+
+ if (interval == D3DPRESENT_INTERVAL_DEFAULT ||
+ (device_info->caps.PresentationIntervals & interval)) {
+
+ device_combo->present_interval_list.append(interval);
+ }
+ }
+}
+
+bool
+VideoDX9Enum::SuggestWindowSettings(VideoSettings* vs)
+{
+ if (!vs)
+ return false;
+
+ // Get display mode of primary adapter (which is assumed to be where the window
+ // will appear)
+ D3DDISPLAYMODE desktop_display_mode;
+ d3d->GetAdapterDisplayMode(0, &desktop_display_mode);
+
+ VideoDX9AdapterInfo* best_adapter_info = 0;
+ VideoDX9DeviceInfo* best_device_info = 0;
+ VideoDX9DeviceCombo* best_device_combo = 0;
+ int best_adapter_index = 0;
+ int best_device_index = 0;
+
+ ListIter<VideoDX9AdapterInfo> a_iter = adapter_info_list;
+ while (++a_iter) {
+ VideoDX9AdapterInfo* adapter_info = a_iter.value();
+
+ ListIter<VideoDX9DeviceInfo> d_iter = adapter_info->device_info_list;
+ while (++d_iter) {
+ VideoDX9DeviceInfo* device_info = d_iter.value();
+
+ ListIter<VideoDX9DeviceCombo> c_iter = device_info->device_combo_list;
+ while (++c_iter) {
+ VideoDX9DeviceCombo* device_combo = c_iter.value();
+
+ bool formats_match = (device_combo->back_buffer_format == device_combo->adapter_format);
+
+ if (!device_combo->is_windowed)
+ continue;
+
+ if (device_combo->adapter_format != desktop_display_mode.Format)
+ continue;
+
+ // If we haven't found a compatible DeviceCombo yet, or if this set
+ // is better (because it's a HAL, and/or because formats match better),
+ // save it
+ if (best_device_combo == NULL ||
+ best_device_combo->device_type != D3DDEVTYPE_HAL && device_combo->device_type == D3DDEVTYPE_HAL ||
+ device_combo->device_type == D3DDEVTYPE_HAL && formats_match) {
+
+ best_adapter_info = adapter_info;
+ best_adapter_index = a_iter.index();
+ best_device_info = device_info;
+ best_device_index = d_iter.index();
+ best_device_combo = device_combo;
+
+ if (device_combo->device_type == D3DDEVTYPE_HAL && formats_match) {
+ // This windowed device combo looks great -- take it
+ goto EndWindowedDeviceComboSearch;
+ }
+
+ // Otherwise keep looking for a better windowed device combo
+ }
+ }
+ }
+ }
+
+EndWindowedDeviceComboSearch:
+ if (best_device_combo == NULL)
+ return false;
+
+ VideoDeviceInfo* win_device = &vs->windowed_device;
+
+ vs->is_windowed = true;
+ vs->windowed_mode.width = desktop_display_mode.Width;
+ vs->windowed_mode.height = desktop_display_mode.Height;
+ vs->windowed_mode.refresh = desktop_display_mode.RefreshRate;
+ vs->windowed_mode.format = desktop_display_mode.Format;
+
+ win_device->adapter_index = best_adapter_index;
+ win_device->device_index = best_device_index;
+ win_device->device_type = best_device_info->device_type;
+ win_device->back_buffer_format = best_device_combo->back_buffer_format;
+ win_device->depth_buffer_bits = GetDepthBits((D3DFORMAT) best_device_combo->depth_stencil_fmt_list[0]);
+ win_device->depth_stencil_format = best_device_combo->depth_stencil_fmt_list[0];
+ win_device->multisample_type = best_device_combo->multisample_type_list[0];
+ win_device->multisample_qual = best_device_combo->multisample_qual_list[0];
+ win_device->vertex_processing = best_device_combo->vertex_processing_list[0];
+
+ return true;
+}
+
+bool
+VideoDX9Enum::SuggestFullscreenSettings(VideoSettings* vs)
+{
+ if (!vs)
+ return false;
+
+ WORD desired_width = vs->fullscreen_mode.width;
+ WORD desired_height = vs->fullscreen_mode.height;
+
+ // For fullscreen, default to first HAL DeviceCombo that supports the current desktop
+ // display mode, or any display mode if HAL is not compatible with the desktop mode, or
+ // non-HAL if no HAL is available
+ D3DDISPLAYMODE desktop_display_mode;
+ D3DDISPLAYMODE best_desktop_display_mode;
+
+ best_desktop_display_mode.Width = 0;
+ best_desktop_display_mode.Height = 0;
+ best_desktop_display_mode.Format = D3DFMT_UNKNOWN;
+ best_desktop_display_mode.RefreshRate = 0;
+
+ VideoDX9AdapterInfo* best_adapter_info = 0;
+ VideoDX9DeviceInfo* best_device_info = 0;
+ VideoDX9DeviceCombo* best_device_combo = 0;
+ int best_adapter_index = 0;
+ int best_device_index = 0;
+
+ ListIter<VideoDX9AdapterInfo> a_iter = adapter_info_list;
+ while (++a_iter) {
+ VideoDX9AdapterInfo* adapter_info = a_iter.value();
+ d3d->GetAdapterDisplayMode(adapter_info->adapter_ordinal, &desktop_display_mode);
+
+ ListIter<VideoDX9DeviceInfo> d_iter = adapter_info->device_info_list;
+ while (++d_iter) {
+ VideoDX9DeviceInfo* device_info = d_iter.value();
+
+ ListIter<VideoDX9DeviceCombo> c_iter = device_info->device_combo_list;
+ while (++c_iter) {
+ VideoDX9DeviceCombo* device_combo = c_iter.value();
+
+ bool bAdapterMatchesBB = (device_combo->back_buffer_format == device_combo->adapter_format);
+ bool bAdapterMatchesDesktop = (device_combo->adapter_format == desktop_display_mode.Format);
+
+ if (device_combo->is_windowed)
+ continue;
+
+ // If we haven't found a compatible set yet, or if this set
+ // is better (because it's a HAL, and/or because formats match better),
+ // save it
+ if (best_device_combo == NULL ||
+ best_device_combo->device_type != D3DDEVTYPE_HAL && device_info->device_type == D3DDEVTYPE_HAL ||
+ device_combo->device_type == D3DDEVTYPE_HAL && best_device_combo->adapter_format != desktop_display_mode.Format && bAdapterMatchesDesktop ||
+ device_combo->device_type == D3DDEVTYPE_HAL && bAdapterMatchesDesktop && bAdapterMatchesBB ) {
+
+ best_desktop_display_mode = desktop_display_mode;
+ best_adapter_info = adapter_info;
+ best_device_info = device_info;
+ best_device_combo = device_combo;
+
+ if (device_info->device_type == D3DDEVTYPE_HAL && bAdapterMatchesDesktop && bAdapterMatchesBB) {
+ // This fullscreen device combo looks great -- take it
+ goto EndFullscreenDeviceComboSearch;
+ }
+
+ // Otherwise keep looking for a better fullscreen device combo
+ }
+ }
+ }
+ }
+
+EndFullscreenDeviceComboSearch:
+ if (best_device_combo == NULL)
+ return false;
+
+ // Need to find a display mode on the best adapter that uses best_device_combo->adapter_format
+ // and is as close to best_desktop_display_mode's res as possible
+ VideoDX9DisplayMode best_display_mode;
+
+ ListIter<VideoDX9DisplayMode> m_iter = best_adapter_info->display_mode_list;
+ while (++m_iter) {
+ VideoDX9DisplayMode* display_mode = m_iter.value();
+
+ if (display_mode->format != best_device_combo->adapter_format)
+ continue;
+
+ if (display_mode->width == desired_width && //best_desktop_display_mode.Width &&
+ display_mode->height == desired_height && //best_desktop_display_mode.Height &&
+ display_mode->refresh == best_desktop_display_mode.RefreshRate) {
+
+ // found a perfect match, so stop
+ best_display_mode = *display_mode;
+ break;
+ }
+ else if (display_mode->width == desired_width && //best_desktop_display_mode.Width &&
+ display_mode->height == desired_height && //best_desktop_display_mode.Height &&
+ display_mode->refresh > best_desktop_display_mode.RefreshRate) {
+ // refresh rate doesn't match, but width/height match, so keep this
+ // and keep looking
+ best_display_mode = *display_mode;
+ }
+ else if (display_mode->width == desired_width) { //best_desktop_display_mode.Width) {
+ // width matches, so keep this and keep looking
+ best_display_mode = *display_mode;
+ }
+ else if (best_display_mode.width == 0)
+ {
+ // we don't have anything better yet, so keep this and keep looking
+ best_display_mode = *display_mode;
+ }
+ }
+
+ VideoDeviceInfo* fs_device = &vs->fullscreen_device;
+
+ vs->is_windowed = false;
+ vs->fullscreen_mode.width = best_display_mode.width;
+ vs->fullscreen_mode.height = best_display_mode.height;
+ vs->fullscreen_mode.refresh = best_display_mode.refresh;
+ vs->fullscreen_mode.format = best_display_mode.format;
+
+ fs_device->adapter_index = best_adapter_index;
+ fs_device->device_index = best_device_index;
+ fs_device->device_type = best_device_info->device_type;
+ fs_device->back_buffer_format = best_device_combo->back_buffer_format;
+ fs_device->depth_buffer_bits = GetDepthBits((D3DFORMAT) best_device_combo->depth_stencil_fmt_list[0]);
+ fs_device->depth_stencil_format = best_device_combo->depth_stencil_fmt_list[0];
+ fs_device->multisample_type = best_device_combo->multisample_type_list[0];
+ fs_device->multisample_qual = best_device_combo->multisample_qual_list[0];
+ fs_device->vertex_processing = best_device_combo->vertex_processing_list[0];
+
+ return true;
+}
+
+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+
+VideoDX9DisplayMode::VideoDX9DisplayMode()
+ : width(0), height(0), refresh(0), format(D3DFMT_UNKNOWN)
+{ }
+
+VideoDX9DisplayMode::VideoDX9DisplayMode(const VideoDX9DisplayMode& m)
+ : width(m.width), height(m.height), refresh(m.refresh), format(m.format)
+{ }
+
+VideoDX9DisplayMode::VideoDX9DisplayMode(const D3DDISPLAYMODE& m)
+ : width(m.Width), height(m.Height), refresh(m.RefreshRate), format(m.Format)
+{ }
+
+int
+VideoDX9DisplayMode::operator<(const VideoDX9DisplayMode& m) const
+{
+ if (width < m.width)
+ return 1;
+
+ if (width > m.width)
+ return 0;
+
+ if (height < m.height)
+ return 1;
+
+ if (height > m.height)
+ return 0;
+
+ if (format < m.format)
+ return 1;
+
+ if (format > m.format)
+ return 0;
+
+ if (refresh < m.refresh)
+ return 1;
+
+ return 0;
+}
+
+int
+VideoDX9DisplayMode::operator<=(const VideoDX9DisplayMode& m) const
+{
+ // if less than ...
+ if (*this < m)
+ return 1;
+
+ // ... or equal to ...
+ if (width == m.width &&
+ height == m.height &&
+ format == m.format &&
+ refresh == m.refresh)
+ return 1;
+
+ // must be greater than
+ return 0;
+}
+
+const char*
+VideoDX9DisplayMode::GetDescription() const
+{
+ static char desc[32];
+
+ sprintf(desc, "%4d x %4d %-12s %d Hz",
+ width,
+ height,
+ D3DFormatToString(format),
+ refresh);
+
+ return desc;
+}
+
+const char*
+VideoDX9DisplayMode::D3DFormatToString(D3DFORMAT format)
+{
+ const char* str = "Unknown Format";
+
+ switch (format) {
+ case D3DFMT_UNKNOWN: str = "UNKNOWN"; break;
+ case D3DFMT_R8G8B8: str = "R8G8B8"; break;
+ case D3DFMT_A8R8G8B8: str = "A8R8G8B8"; break;
+ case D3DFMT_X8R8G8B8: str = "X8R8G8B8"; break;
+ case D3DFMT_R5G6B5: str = "R5G6B5"; break;
+ case D3DFMT_X1R5G5B5: str = "X1R5G5B5"; break;
+ case D3DFMT_A1R5G5B5: str = "A1R5G5B5"; break;
+ case D3DFMT_A4R4G4B4: str = "A4R4G4B4"; break;
+ case D3DFMT_R3G3B2: str = "R3G3B2"; break;
+ case D3DFMT_A8: str = "A8"; break;
+ case D3DFMT_A8R3G3B2: str = "A8R3G3B2"; break;
+ case D3DFMT_X4R4G4B4: str = "X4R4G4B4"; break;
+ case D3DFMT_A2B10G10R10: str = "A2B10G10R10"; break;
+ case D3DFMT_A8B8G8R8: str = "A8B8G8R8"; break;
+ case D3DFMT_X8B8G8R8: str = "X8B8G8R8"; break;
+ case D3DFMT_G16R16: str = "G16R16"; break;
+ case D3DFMT_A2R10G10B10: str = "A2R10G10B10"; break;
+ case D3DFMT_A16B16G16R16: str = "A16B16G16R16"; break;
+ case D3DFMT_A8P8: str = "A8P8"; break;
+ case D3DFMT_P8: str = "P8"; break;
+ case D3DFMT_L8: str = "L8"; break;
+ case D3DFMT_A8L8: str = "A8L8"; break;
+ case D3DFMT_A4L4: str = "A4L4"; break;
+ case D3DFMT_V8U8: str = "V8U8"; break;
+ case D3DFMT_L6V5U5: str = "L6V5U5"; break;
+ case D3DFMT_X8L8V8U8: str = "X8L8V8U8"; break;
+ case D3DFMT_Q8W8V8U8: str = "Q8W8V8U8"; break;
+ case D3DFMT_V16U16: str = "V16U16"; break;
+ case D3DFMT_A2W10V10U10: str = "A2W10V10U10"; break;
+ case D3DFMT_UYVY: str = "UYVY"; break;
+ case D3DFMT_YUY2: str = "YUY2"; break;
+ case D3DFMT_DXT1: str = "DXT1"; break;
+ case D3DFMT_DXT2: str = "DXT2"; break;
+ case D3DFMT_DXT3: str = "DXT3"; break;
+ case D3DFMT_DXT4: str = "DXT4"; break;
+ case D3DFMT_DXT5: str = "DXT5"; break;
+ case D3DFMT_D16_LOCKABLE: str = "D16_LOCKABLE"; break;
+ case D3DFMT_D32: str = "D32"; break;
+ case D3DFMT_D15S1: str = "D15S1"; break;
+ case D3DFMT_D24S8: str = "D24S8"; break;
+ case D3DFMT_D24X8: str = "D24X8"; break;
+ case D3DFMT_D24X4S4: str = "D24X4S4"; break;
+ case D3DFMT_D16: str = "D16"; break;
+ case D3DFMT_L16: str = "L16"; break;
+ case D3DFMT_VERTEXDATA: str = "VERTEXDATA"; break;
+ case D3DFMT_INDEX16: str = "INDEX16"; break;
+ case D3DFMT_INDEX32: str = "INDEX32"; break;
+ case D3DFMT_Q16W16V16U16: str = "Q16W16V16U16"; break;
+ case D3DFMT_MULTI2_ARGB8: str = "MULTI2_ARGB8"; break;
+ case D3DFMT_R16F: str = "R16F"; break;
+ case D3DFMT_G16R16F: str = "G16R16F"; break;
+ case D3DFMT_A16B16G16R16F: str = "A16B16G16R16F"; break;
+ case D3DFMT_R32F: str = "R32F"; break;
+ case D3DFMT_G32R32F: str = "G32R32F"; break;
+ case D3DFMT_A32B32G32R32F: str = "A32B32G32R32F"; break;
+ case D3DFMT_CxV8U8: str = "CxV8U8"; break;
+ default: str = "Unknown format"; break;
+ }
+
+ return str;
+}
+
+
+// +--------------------------------------------------------------------+
+
+VideoDX9AdapterInfo::VideoDX9AdapterInfo()
+ : adapter_ordinal(0)
+{
+ ZeroMemory(&adapter_identifier, sizeof(adapter_identifier));
+}
+
+VideoDX9AdapterInfo::~VideoDX9AdapterInfo()
+{
+ display_mode_list.destroy();
+ device_info_list.destroy();
+}
+
+const char*
+VideoDX9AdapterInfo::GetDescription() const
+{
+ return adapter_identifier.Description;
+}
+
+// +--------------------------------------------------------------------+
+
+VideoDX9DeviceInfo::VideoDX9DeviceInfo()
+ : adapter_ordinal(0), device_type(D3DDEVTYPE_HAL)
+{
+ ZeroMemory(&caps, sizeof(caps));
+}
+
+VideoDX9DeviceInfo::~VideoDX9DeviceInfo()
+{
+ device_combo_list.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+VideoDX9DeviceCombo::VideoDX9DeviceCombo()
+ : adapter_ordinal(0), device_type(D3DDEVTYPE_HAL),
+ adapter_format((D3DFORMAT) 0),
+ back_buffer_format((D3DFORMAT) 0),
+ is_windowed(false)
+{
+}
+
+VideoDX9DeviceCombo::~VideoDX9DeviceCombo()
+{
+ conflict_list.destroy();
+}
diff --git a/nGenEx/VideoDX9Enum.h b/nGenEx/VideoDX9Enum.h
new file mode 100644
index 0000000..f00664f
--- /dev/null
+++ b/nGenEx/VideoDX9Enum.h
@@ -0,0 +1,185 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: VideoDX9Enum.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Direct3D and Direct3D Video classes for DirectX 7
+*/
+
+#ifndef VideoDX9Enum_h
+#define VideoDX9Enum_h
+
+#include <d3d9.h>
+
+#include "Video.h"
+#include "ArrayList.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class VideoDX9Enum;
+struct VideoDX9DisplayMode;
+struct VideoDX9AdapterInfo;
+struct VideoDX9DeviceInfo;
+struct VideoDX9DSMSConflict;
+struct VideoDX9DeviceCombo;
+class VideoSettings;
+
+// +--------------------------------------------------------------------+
+
+class VideoDX9Enum
+{
+public:
+ enum VP_TYPE {
+ SOFTWARE_VP,
+ MIXED_VP,
+ HARDWARE_VP,
+ PURE_HARDWARE_VP
+ };
+
+ VideoDX9Enum(IDirect3D9* d3d9);
+ ~VideoDX9Enum();
+
+ void SetDirect3D9(IDirect3D9* d3d9);
+
+ HRESULT Enumerate();
+ bool SuggestWindowSettings(VideoSettings* vs);
+ bool SuggestFullscreenSettings(VideoSettings* vs);
+
+ int NumAdapters() const { return adapter_info_list.size(); }
+ void SelectAdapter(int index);
+ VideoDX9AdapterInfo* GetAdapterInfo();
+ VideoDX9DeviceInfo* GetDeviceInfo(DWORD devtype);
+
+ bool IsModeSupported(int width, int height, int bpp) const;
+
+ UINT min_width;
+ UINT min_height;
+ UINT min_color_bits;
+ UINT min_alpha_bits;
+ UINT min_depth_bits;
+ UINT min_stencil_bits;
+
+ bool uses_depth_buffer;
+ bool uses_mixed_vp;
+ bool req_windowed;
+ bool req_fullscreen;
+
+private:
+ HRESULT EnumerateDevices(VideoDX9AdapterInfo* adapter_info, ArrayList& adapter_format_list);
+ HRESULT EnumerateDeviceCombos(VideoDX9DeviceInfo* device_info, ArrayList& adapter_format_list);
+
+ void BuildDepthStencilFormatList(VideoDX9DeviceCombo* device_combo);
+ void BuildMultiSampleTypeList(VideoDX9DeviceCombo* device_combo);
+ void BuildDSMSConflictList(VideoDX9DeviceCombo* device_combo);
+ void BuildVertexProcessingTypeList(VideoDX9DeviceInfo* device_info, VideoDX9DeviceCombo* device_combo);
+ void BuildPresentIntervalList(VideoDX9DeviceInfo* device_info, VideoDX9DeviceCombo* device_combo);
+
+ IDirect3D9* d3d;
+ ArrayList allowed_adapter_format_list;
+
+ List<VideoDX9AdapterInfo> adapter_info_list;
+ int adapter_index;
+};
+
+
+// +--------------------------------------------------------------------+
+
+struct VideoDX9AdapterInfo
+{
+ static const char* TYPENAME() { return "VideoDX9AdapterInfo"; }
+
+ VideoDX9AdapterInfo();
+ ~VideoDX9AdapterInfo();
+
+ const char* GetDescription() const;
+
+ int adapter_ordinal;
+ D3DADAPTER_IDENTIFIER9 adapter_identifier;
+ List<VideoDX9DisplayMode> display_mode_list;
+ List<VideoDX9DeviceInfo> device_info_list;
+};
+
+
+// +--------------------------------------------------------------------+
+
+struct VideoDX9DisplayMode
+{
+ static const char* TYPENAME() { return "VideoDX9DisplayMode"; }
+
+ VideoDX9DisplayMode();
+ VideoDX9DisplayMode(const VideoDX9DisplayMode& m);
+ VideoDX9DisplayMode(const D3DDISPLAYMODE& m);
+
+ int operator<(const VideoDX9DisplayMode& m) const;
+ int operator<=(const VideoDX9DisplayMode& m) const;
+
+ const char* GetDescription() const;
+ static const char* D3DFormatToString(D3DFORMAT format);
+
+ UINT width;
+ UINT height;
+ UINT refresh;
+ D3DFORMAT format;
+};
+
+
+// +--------------------------------------------------------------------+
+
+struct VideoDX9DeviceInfo
+{
+ static const char* TYPENAME() { return "VideoDX9DeviceInfo"; }
+
+ VideoDX9DeviceInfo();
+ ~VideoDX9DeviceInfo();
+
+ int adapter_ordinal;
+ D3DDEVTYPE device_type;
+ D3DCAPS9 caps;
+ List<VideoDX9DeviceCombo> device_combo_list;
+};
+
+
+// +--------------------------------------------------------------------+
+
+struct VideoDX9FormatConflict
+{
+ static const char* TYPENAME() { return "VideoDX9FormatConflict"; }
+
+ D3DFORMAT ds_format;
+ D3DMULTISAMPLE_TYPE multisample_type;
+};
+
+
+// +--------------------------------------------------------------------+
+
+struct VideoDX9DeviceCombo
+{
+ VideoDX9DeviceCombo();
+ ~VideoDX9DeviceCombo();
+
+ int adapter_ordinal;
+ D3DDEVTYPE device_type;
+ D3DFORMAT adapter_format;
+ D3DFORMAT back_buffer_format;
+ bool is_windowed;
+
+ ArrayList vertex_processing_list;
+ ArrayList depth_stencil_fmt_list;
+ ArrayList multisample_type_list;
+ ArrayList multisample_qual_list;
+ ArrayList present_interval_list;
+
+ List<VideoDX9FormatConflict> conflict_list;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif VideoDX9Enum_h
+
diff --git a/nGenEx/VideoDX9VertexBuffer.cpp b/nGenEx/VideoDX9VertexBuffer.cpp
new file mode 100644
index 0000000..08d3bb2
--- /dev/null
+++ b/nGenEx/VideoDX9VertexBuffer.cpp
@@ -0,0 +1,281 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: VideoDX9VertexBuffer.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Direct3D Video class for DirectX 9
+*/
+
+#include "MemDebug.h"
+#include "VideoDX9VertexBuffer.h"
+#include "Color.h"
+
+// +--------------------------------------------------------------------+
+
+void VideoDX9Error(const char* msg, HRESULT dderr);
+extern int VD3D_describe_things;
+
+#ifndef RELEASE
+#define RELEASE(x) if (x) { x->Release(); x=NULL; }
+#endif
+
+// +--------------------------------------------------------------------+
+
+VideoDX9VertexBuffer::VideoDX9VertexBuffer(VideoDX9* dx9,
+ UINT nverts,
+ UINT vsize,
+ DWORD format,
+ DWORD usage)
+ : video(dx9), vertex_buffer(0),
+ num_verts(nverts), num_locked(0), vert_size(vsize), next_vert(0),
+ is_dynamic(false)
+{
+ UINT len = num_verts * vert_size;
+
+ if (video && len) {
+ is_dynamic = (usage & D3DUSAGE_DYNAMIC) ? true : false;
+ D3DPOOL pool = is_dynamic ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED;
+
+ HRESULT hr = video->D3DDevice()->CreateVertexBuffer(len,
+ usage,
+ format,
+ pool,
+ &vertex_buffer,
+ 0);
+
+ if (FAILED(hr)) {
+ static int report = 10;
+ if (report) {
+ VideoDX9Error("Could not create vertex buffer.", hr);
+ report--;
+ }
+
+ num_verts = 0;
+ vert_size = 0;
+ next_vert = 0;
+ }
+ }
+}
+
+VideoDX9VertexBuffer::~VideoDX9VertexBuffer()
+{
+ RELEASE(vertex_buffer);
+}
+
+// +--------------------------------------------------------------------+
+
+BYTE*
+VideoDX9VertexBuffer::Lock(UINT count)
+{
+ if (vertex_buffer && count <= num_verts) {
+ DWORD flags = 0;
+
+ if (count == 0)
+ count = num_verts;
+
+ if (is_dynamic) {
+ flags = D3DLOCK_NOOVERWRITE;
+
+ if (next_vert + count > num_verts) {
+ next_vert = 0;
+ flags = D3DLOCK_DISCARD;
+ }
+ }
+
+ void* result = 0;
+ HRESULT hr = 0;
+
+ hr = vertex_buffer->Lock(next_vert * vert_size,
+ count * vert_size,
+ &result,
+ flags);
+
+ if (SUCCEEDED(hr)) {
+ num_locked = count;
+ return (BYTE*) result;
+ }
+ }
+
+ return 0;
+}
+
+void
+VideoDX9VertexBuffer::Unlock()
+{
+ if (vertex_buffer && num_locked > 0) {
+ vertex_buffer->Unlock();
+
+ next_vert += num_locked;
+ num_locked = 0;
+ }
+}
+
+bool
+VideoDX9VertexBuffer::Select(int stream)
+{
+ if (video && vertex_buffer) {
+ HRESULT hr = E_FAIL;
+
+ if (num_locked > 0)
+ Unlock();
+
+ hr = video->D3DDevice()->SetStreamSource(stream,
+ vertex_buffer,
+ 0,
+ vert_size);
+
+ if (SUCCEEDED(hr))
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+UINT
+VideoDX9VertexBuffer::GetNumVerts() const
+{
+ return num_verts;
+}
+
+UINT
+VideoDX9VertexBuffer::GetVertSize() const
+{
+ return vert_size;
+}
+
+UINT
+VideoDX9VertexBuffer::GetNextVert() const
+{
+ return next_vert;
+}
+
+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+
+
+VideoDX9IndexBuffer::VideoDX9IndexBuffer(VideoDX9* dx9,
+ UINT nind,
+ DWORD usage)
+ : video(dx9), index_buffer(0),
+ num_indices(nind), num_locked(0), next_index(0),
+ is_dynamic(false)
+{
+ UINT len = num_indices * sizeof(WORD);
+
+ if (video && len) {
+ is_dynamic = (usage & D3DUSAGE_DYNAMIC) ? true : false;
+ D3DPOOL pool = is_dynamic ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED;
+
+ HRESULT hr = video->D3DDevice()->CreateIndexBuffer(len,
+ usage,
+ D3DFMT_INDEX16,
+ pool,
+ &index_buffer,
+ 0);
+
+ if (FAILED(hr)) {
+ static int report = 10;
+ if (report) {
+ VideoDX9Error("Could not create index buffer.", hr);
+ report--;
+ }
+
+ num_indices = 0;
+ next_index = 0;
+ }
+ }
+}
+
+VideoDX9IndexBuffer::~VideoDX9IndexBuffer()
+{
+ RELEASE(index_buffer);
+}
+
+// +--------------------------------------------------------------------+
+
+WORD*
+VideoDX9IndexBuffer::Lock(UINT count)
+{
+ if (index_buffer && count <= num_indices) {
+ DWORD flags = 0;
+
+ if (count == 0)
+ count = num_indices;
+
+ if (is_dynamic) {
+ flags = D3DLOCK_NOOVERWRITE;
+
+ if (next_index + count > num_indices) {
+ next_index = 0;
+ flags = D3DLOCK_DISCARD;
+ }
+ }
+
+ void* result = 0;
+ HRESULT hr = 0;
+
+ hr = index_buffer->Lock(next_index * 2,
+ count * 2,
+ &result,
+ flags);
+
+ if (SUCCEEDED(hr)) {
+ num_locked = count;
+ return (WORD*) result;
+ }
+ }
+
+ return 0;
+}
+
+void
+VideoDX9IndexBuffer::Unlock()
+{
+ if (index_buffer && num_locked > 0) {
+ index_buffer->Unlock();
+
+ next_index += num_locked;
+ num_locked = 0;
+ }
+}
+
+bool
+VideoDX9IndexBuffer::Select()
+{
+ if (video && index_buffer) {
+ if (num_locked > 0)
+ Unlock();
+
+ HRESULT hr = video->D3DDevice()->SetIndices(index_buffer);
+
+ if (SUCCEEDED(hr))
+ return true;
+ }
+
+ return false;
+}
+
+// +--------------------------------------------------------------------+
+
+UINT
+VideoDX9IndexBuffer::GetNumIndices() const
+{
+ return num_indices;
+}
+
+UINT
+VideoDX9IndexBuffer::GetNextIndex() const
+{
+ return next_index;
+}
+
diff --git a/nGenEx/VideoDX9VertexBuffer.h b/nGenEx/VideoDX9VertexBuffer.h
new file mode 100644
index 0000000..815cb86
--- /dev/null
+++ b/nGenEx/VideoDX9VertexBuffer.h
@@ -0,0 +1,79 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: VideoDX9VertexBuffer.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Vertex and Index Buffer classes for DirectX 9
+*/
+
+#ifndef VideoDX9VertexBuffer_h
+#define VideoDX9VertexBuffer_h
+
+#include "VideoDX9.h"
+
+// +--------------------------------------------------------------------+
+
+class VideoDX9VertexBuffer
+{
+public:
+ VideoDX9VertexBuffer(VideoDX9* dx9,
+ UINT num_verts,
+ UINT vert_size,
+ DWORD format,
+ DWORD usage);
+ ~VideoDX9VertexBuffer();
+
+ BYTE* Lock(UINT count);
+ void Unlock();
+ bool Select(int stream=0);
+
+ UINT GetNumVerts() const;
+ UINT GetVertSize() const;
+ UINT GetNextVert() const;
+
+private:
+ VideoDX9* video;
+ IDirect3DVertexBuffer9* vertex_buffer;
+
+ UINT num_verts;
+ UINT num_locked;
+ UINT vert_size;
+ UINT next_vert;
+ bool is_dynamic;
+};
+
+// +--------------------------------------------------------------------+
+
+class VideoDX9IndexBuffer
+{
+public:
+ VideoDX9IndexBuffer(VideoDX9* dx9,
+ UINT num_indices,
+ DWORD usage);
+ ~VideoDX9IndexBuffer();
+
+ WORD* Lock(UINT count);
+ void Unlock();
+ bool Select();
+
+ UINT GetNumIndices() const;
+ UINT GetNextIndex() const;
+
+private:
+ VideoDX9* video;
+ IDirect3DIndexBuffer9* index_buffer;
+
+ UINT num_indices;
+ UINT num_locked;
+ UINT next_index;
+ bool is_dynamic;
+};
+
+#endif VideoDX9VertexBuffer_h
+
diff --git a/nGenEx/VideoFactory.cpp b/nGenEx/VideoFactory.cpp
new file mode 100644
index 0000000..ded9e84
--- /dev/null
+++ b/nGenEx/VideoFactory.cpp
@@ -0,0 +1,71 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: VideoFac.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Video and Polygon Renderer Factory class
+*/
+
+#include "MemDebug.h"
+#include "VideoFactory.h"
+
+#include "VideoDX9.h"
+#include "SoundD3D.h"
+
+// +--------------------------------------------------------------------+
+
+VideoFactory::VideoFactory(HWND h)
+ : hwnd(h), video(0), audio(0)
+{ }
+
+VideoFactory::~VideoFactory()
+{ }
+
+// +--------------------------------------------------------------------+
+
+Video*
+VideoFactory::CreateVideo(VideoSettings* vs)
+{
+ if (!video) {
+ video = (Video*) new(__FILE__,__LINE__) VideoDX9(hwnd, vs);
+
+ if (!video) {
+ delete video;
+ video = 0;
+ }
+ }
+
+ return video;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+VideoFactory::DestroyVideo(Video* v)
+{
+ if (v == video) {
+ delete video;
+ video = 0;
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+SoundCard*
+VideoFactory::CreateSoundCard()
+{
+ if (!audio) {
+ audio = new(__FILE__,__LINE__) SoundCardD3D(hwnd);
+ Sound::UseSoundCard(audio);
+ }
+
+ return audio;
+}
+
+
diff --git a/nGenEx/VideoFactory.h b/nGenEx/VideoFactory.h
new file mode 100644
index 0000000..f30db1b
--- /dev/null
+++ b/nGenEx/VideoFactory.h
@@ -0,0 +1,42 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: VideoFactory.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Video Factory class
+*/
+
+#ifndef VideoFactory_h
+#define VideoFactory_h
+
+#include "Types.h"
+#include "Video.h"
+#include "SoundCard.h"
+
+// +--------------------------------------------------------------------+
+
+class VideoFactory
+{
+public:
+ VideoFactory(HWND h);
+ virtual ~VideoFactory();
+
+ virtual Video* CreateVideo(VideoSettings* vs);
+ virtual void DestroyVideo(Video* video);
+ virtual SoundCard* CreateSoundCard();
+
+private:
+ HWND hwnd;
+
+ Video* video;
+ SoundCard* audio;
+};
+
+#endif VideoFactory_h
+
diff --git a/nGenEx/VideoSettings.cpp b/nGenEx/VideoSettings.cpp
new file mode 100644
index 0000000..cd4c8df
--- /dev/null
+++ b/nGenEx/VideoSettings.cpp
@@ -0,0 +1,268 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: VideoSettings.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Video Settings class implementation
+*/
+
+#include "MemDebug.h"
+#include "VideoSettings.h"
+
+// +--------------------------------------------------------------------+
+
+VideoSettings::VideoSettings()
+{
+ fullscreen_mode.width = 1024;
+ fullscreen_mode.height = 768;
+ fullscreen_mode.refresh = 75;
+ fullscreen_mode.format = VideoMode::FMT_X8R8G8B8;
+
+ windowed_mode.width = 800;
+ windowed_mode.height = 600;
+ windowed_mode.refresh = 0;
+ windowed_mode.format = VideoMode::FMT_NONE;
+
+ window_width = 800;
+ window_height = 600;
+
+ is_windowed = false;
+ use_effects = true;
+ shadows = true;
+ bumpmaps = true;
+ specmaps = true;
+ max_detail = 4;
+ enable_vs = true;
+ enable_ps = true;
+ depth_bias = 0;
+}
+
+VideoSettings::~VideoSettings()
+{ }
+
+// +--------------------------------------------------------------------+
+
+bool
+VideoSettings::IsWindowed() const
+{
+ return is_windowed;
+}
+
+bool
+VideoSettings::UseEffects() const
+{
+ return use_effects;
+}
+
+int
+VideoSettings::GetWidth() const
+{
+ if (is_windowed)
+ return window_width;
+
+ return fullscreen_mode.width;
+}
+
+int
+VideoSettings::GetHeight() const
+{
+ if (is_windowed)
+ return window_height;
+
+ return fullscreen_mode.height;
+}
+
+int
+VideoSettings::GetDepth() const
+{
+ int fmt = 0;
+ int bpp = 0;
+
+ if (is_windowed)
+ fmt = windowed_mode.format;
+ else
+ fmt = fullscreen_mode.format;
+
+ switch (fmt) {
+ default:
+ case VideoMode::FMT_NONE: bpp = 0; break;
+ case VideoMode::FMT_R5G5B5: bpp = 15; break;
+ case VideoMode::FMT_R5G6B5: bpp = 16; break;
+ case VideoMode::FMT_R8G8B8: bpp = 24; break;
+ case VideoMode::FMT_X8R8G8B8: bpp = 32; break;
+ }
+
+ return bpp;
+}
+
+int
+VideoSettings::GetPixSize() const
+{
+ int fmt = 0;
+ int pix = 0;
+
+ if (is_windowed)
+ fmt = windowed_mode.format;
+ else
+ fmt = fullscreen_mode.format;
+
+ switch (fmt) {
+ default:
+ case VideoMode::FMT_NONE: pix = 0; break;
+ case VideoMode::FMT_R5G5B5: pix = 2; break;
+ case VideoMode::FMT_R5G6B5: pix = 2; break;
+ case VideoMode::FMT_R8G8B8: pix = 3; break;
+ case VideoMode::FMT_X8R8G8B8: pix = 4; break;
+ }
+
+ return pix;
+}
+
+int
+VideoSettings::GetRefreshRate() const
+{
+ if (is_windowed)
+ return windowed_mode.refresh;
+
+ return fullscreen_mode.refresh;
+}
+
+// +--------------------------------------------------------------------+
+
+const char*
+VideoSettings::GetModeDescription() const
+{
+ if (is_windowed)
+ return windowed_mode.GetDescription();
+
+ return fullscreen_mode.GetDescription();
+}
+
+// +--------------------------------------------------------------------+
+
+int
+VideoSettings::GetVertexProcessing() const
+{
+ if (is_windowed)
+ return windowed_device.vertex_processing;
+
+ return fullscreen_device.vertex_processing;
+}
+
+int
+VideoSettings::GetDepthBufferBits() const
+{
+ if (is_windowed)
+ return windowed_device.depth_buffer_bits;
+
+ return fullscreen_device.depth_buffer_bits;
+}
+
+int
+VideoSettings::GetAdapterIndex() const
+{
+ if (is_windowed)
+ return windowed_device.adapter_index;
+
+ return fullscreen_device.adapter_index;
+}
+
+int
+VideoSettings::GetDeviceIndex() const
+{
+ if (is_windowed)
+ return windowed_device.device_index;
+
+ return fullscreen_device.device_index;
+}
+
+DWORD
+VideoSettings::GetDeviceType() const
+{
+ if (is_windowed)
+ return windowed_device.device_type;
+
+ return fullscreen_device.device_type;
+}
+
+DWORD
+VideoSettings::GetDepthStencilFormat() const
+{
+ if (is_windowed)
+ return windowed_device.depth_stencil_format;
+
+ return fullscreen_device.depth_stencil_format;
+}
+
+DWORD
+VideoSettings::GetBackBufferFormat() const
+{
+ if (is_windowed)
+ return windowed_device.back_buffer_format;
+
+ return fullscreen_device.back_buffer_format;
+}
+
+const char*
+VideoSettings::GetAdapterDesc() const
+{
+ if (is_windowed)
+ return windowed_device.adapter_desc;
+
+ return fullscreen_device.adapter_desc;
+}
+
+const char*
+VideoSettings::GetDeviceDesc() const
+{
+ if (is_windowed)
+ return windowed_device.device_desc;
+
+ return fullscreen_device.device_desc;
+}
+
+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+
+const char*
+VideoMode::GetDescription() const
+{
+ static char desc[32];
+
+ int bpp = 0;
+ switch (format) {
+ default:
+ case VideoMode::FMT_NONE: bpp = 0; break;
+ case VideoMode::FMT_R5G5B5: bpp = 15; break;
+ case VideoMode::FMT_R5G6B5: bpp = 16; break;
+ case VideoMode::FMT_R8G8B8: bpp = 24; break;
+ case VideoMode::FMT_X8R8G8B8: bpp = 32; break;
+ }
+
+ sprintf(desc, "%d x %d x %d", width, height, bpp);
+ return desc;
+}
+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+// +--------------------------------------------------------------------+
+
+VideoDeviceInfo::VideoDeviceInfo()
+{
+ ZeroMemory(this, sizeof(VideoDeviceInfo));
+
+ vertex_processing = VideoSettings::VTX_HARDWARE;
+ depth_buffer_bits = 32;
+}
+
+VideoDeviceInfo::~VideoDeviceInfo()
+{
+}
diff --git a/nGenEx/VideoSettings.h b/nGenEx/VideoSettings.h
new file mode 100644
index 0000000..e88e645
--- /dev/null
+++ b/nGenEx/VideoSettings.h
@@ -0,0 +1,131 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: VideoSettings.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Video Settings class
+*/
+
+#ifndef VideoSettings_h
+#define VideoSettings_h
+
+#include "Types.h"
+#include "Video.h"
+
+// +--------------------------------------------------------------------+
+
+struct VideoMode
+{
+ enum Format {
+ FMT_NONE = 0,
+ FMT_R5G5B5 = 24,
+ FMT_R5G6B5 = 23,
+ FMT_R8G8B8 = 20,
+ FMT_X8R8G8B8 = 22
+ };
+
+ VideoMode() : width(0), height(0), refresh(0), format(0) { }
+ VideoMode(int w, int h, Format f, int r=0) : width(w), height(h), refresh(r), format(f) { }
+
+ int operator == (const VideoMode& m) const { return m.width == width &&
+ m.height == height &&
+ m.format == format; }
+ int operator != (const VideoMode& m) const { return m.width != width ||
+ m.height != height ||
+ m.format != format; }
+
+ const char* GetDescription() const;
+
+ int width;
+ int height;
+ int refresh;
+ int format;
+};
+
+// +--------------------------------------------------------------------+
+
+struct VideoDeviceInfo
+{
+ VideoDeviceInfo();
+ ~VideoDeviceInfo();
+
+ int vertex_processing;
+ int depth_buffer_bits;
+ int adapter_index;
+ int device_index;
+ DWORD device_type;
+ DWORD depth_stencil_format;
+ DWORD back_buffer_format;
+ DWORD multisample_type;
+ DWORD multisample_qual;
+ char adapter_desc[128];
+ char device_desc[128];
+};
+
+// +--------------------------------------------------------------------+
+
+class VideoSettings
+{
+public:
+ enum VertexProcessing {
+ VTX_SOFTWARE,
+ VTX_MIXED,
+ VTX_HARDWARE,
+ VTX_PURE
+ };
+
+ VideoSettings();
+ ~VideoSettings();
+
+ // accessor methods
+
+ bool IsWindowed() const;
+ bool UseEffects() const;
+ int GetWidth() const;
+ int GetHeight() const;
+ int GetDepth() const;
+ int GetPixSize() const;
+ int GetRefreshRate() const;
+
+ const char* GetModeDescription() const;
+
+ int GetVertexProcessing() const;
+ int GetDepthBufferBits() const;
+ int GetAdapterIndex() const;
+ int GetDeviceIndex() const;
+ DWORD GetDeviceType() const;
+ DWORD GetDepthStencilFormat() const;
+ DWORD GetBackBufferFormat() const;
+ const char* GetAdapterDesc() const;
+ const char* GetDeviceDesc() const;
+
+ // properties
+
+ bool is_windowed;
+ bool use_effects;
+ VideoMode fullscreen_mode;
+ VideoMode windowed_mode;
+ int window_width;
+ int window_height;
+ VideoDeviceInfo fullscreen_device;
+ VideoDeviceInfo windowed_device;
+
+ // feature set
+
+ bool shadows;
+ bool bumpmaps;
+ bool specmaps;
+ int max_detail;
+ DWORD enable_vs;
+ DWORD enable_ps;
+ float depth_bias;
+};
+
+#endif VideoSettings_h
+
diff --git a/nGenEx/View.h b/nGenEx/View.h
new file mode 100644
index 0000000..fd1108c
--- /dev/null
+++ b/nGenEx/View.h
@@ -0,0 +1,52 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: View.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Abstract View class
+*/
+
+#ifndef View_h
+#define View_h
+
+#include "Types.h"
+
+// +--------------------------------------------------------------------+
+
+class Window;
+
+// +--------------------------------------------------------------------+
+
+class View
+{
+ friend class Window;
+
+public:
+ static const char* TYPENAME() { return "View"; }
+
+ View(Window* c) : window(c) { }
+ virtual ~View() { }
+
+ int operator == (const View& that) const { return this == &that; }
+
+ // Operations:
+ virtual void Refresh() { }
+ virtual void OnWindowMove() { }
+ virtual void OnShow() { }
+ virtual void OnHide() { }
+
+ virtual void SetWindow(Window* w) { window = w; OnWindowMove(); }
+ virtual Window* GetWindow() { return window; }
+
+protected:
+ Window* window;
+};
+
+#endif View_h
+
diff --git a/nGenEx/Water.cpp b/nGenEx/Water.cpp
new file mode 100644
index 0000000..41fa2f3
--- /dev/null
+++ b/nGenEx/Water.cpp
@@ -0,0 +1,295 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2006. All Rights Reserved.
+
+ SUBSYSTEM: nGen.lib
+ FILE: Water.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Water surface effect w/ reflection and caustics
+*/
+
+#include "MemDebug.h"
+#include "Water.h"
+#include "Random.h"
+
+// +--------------------------------------------------------------------+
+
+struct WATER_REFRACT
+{
+ // Vrefract = (V + refract * N) * norm
+ float refract;
+ float refractNorm;
+ DWORD diffuse;
+};
+
+struct WATER_SURFACE
+{
+ float height;
+ Vec3 normal;
+};
+
+// +--------------------------------------------------------------------+
+
+#if defined(_X86) && !defined(_WIN64)
+inline int f2i(float flt)
+{
+ volatile int n;
+
+ __asm
+ {
+ fld flt
+ fistp n
+ }
+
+ return n;
+}
+#else
+inline int f2i(float flt)
+{
+ return (int) flt;
+}
+#endif
+
+
+// +--------------------------------------------------------------------+
+
+static WATER_REFRACT RefractionTable[512];
+static bool refractInit = false;
+
+static const int WAVE_SIZE = 256;
+static const DWORD WAVE_MASK = 0xff;
+
+// +--------------------------------------------------------------------+
+
+Water::Water()
+ : size(0), depth(0), scaleTex(1), avgHeight(0),
+ nVertices(0), surface(0), waves(0)
+{
+}
+
+Water::~Water()
+{
+ delete [] surface;
+ delete [] waves;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Water::Init(int n, float s, float d)
+{
+ size = s;
+ depth = d;
+ scaleTex = 1/size;
+
+ // Calculate number of vertices
+ nVertices = n;
+
+ // Create refraction table
+ if (!refractInit) {
+ WATER_REFRACT* refract = &RefractionTable[256];
+
+ for (UINT u = 0; u < 256; u++) {
+ float fCos0 = (float) u / (float) 256.0f;
+ float f0 = acosf(fCos0);
+ float fSin0 = sinf(f0);
+
+ float fSin1 = fSin0 / 1.333f; // water
+ float f1 = asinf(fSin1);
+ float fCos1 = cosf(f1);
+
+ refract[u].refract = fSin0 / fSin1 * fCos1 - fCos0;
+ refract[u].refractNorm = - fSin1 / fSin0;
+ refract[u].diffuse = ((((0xff - u)*(0xff - u)*(0xff - u)) << 8) & 0xff000000);
+
+ RefractionTable[u] = RefractionTable[256];
+ }
+
+ refractInit = true;
+ }
+
+ // Create maps
+ if (surface)
+ delete [] surface;
+
+ surface = new(__FILE__,__LINE__) WATER_SURFACE[n*n];
+ ZeroMemory(surface, n*n * sizeof(WATER_SURFACE));
+
+ if (waves)
+ delete [] waves;
+
+ waves = new(__FILE__,__LINE__) float[WAVE_SIZE*4];
+
+ double f = 1.0 / (double) WAVE_SIZE;
+ for (int i = 0; i < WAVE_SIZE; i++) {
+ double s0 = sin(2*PI*i*f);
+ double s1 = sin(4*PI*i*f);
+ double s2 = sin(6*PI*i*f);
+ double s3 = sin(8*PI*i*f);
+
+ waves[0*WAVE_SIZE + i] = (float) (1.8 * s0*s0 - 0.9);
+ waves[1*WAVE_SIZE + i] = (float) (1.6 * s1*s1 - 0.8);
+ waves[2*WAVE_SIZE + i] = (float) (0.4 * s2);
+ waves[3*WAVE_SIZE + i] = (float) (0.8 * s3*s3 - 0.4);
+ }
+
+ for (i = 0; i < 4; i++) {
+ offsets[i] = (float) Random(0, WAVE_SIZE);
+ }
+
+ offsets[4] = 12.45f;
+ offsets[5] = 14.23f;
+ offsets[6] = 16.72f;
+ offsets[7] = 20.31f;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Water::CalcWaves(double seconds)
+{
+ int i, n[4];
+ UINT SIZE = nVertices;
+ UINT STEP = WAVE_SIZE / (SIZE-1);
+ UINT STEP2 = STEP/2;
+ UINT AREA = SIZE * SIZE;
+ UINT x, y;
+
+ for (i = 0; i < 4; i++) {
+ n[i] = (int) offsets[i];
+ }
+
+ WATER_SURFACE* pSurf = surface;
+
+ // compute heights
+ for (y = 0; y < SIZE; y++) {
+ for (x = 0; x < SIZE; x++) {
+ float h = 0;
+ h += waves[ ((n[0] + x*STEP
+ - y*STEP2) & WAVE_MASK) + 0*WAVE_SIZE ];
+ h += waves[ ((n[1] + x*STEP2
+ + y*STEP) & WAVE_MASK) + 1*WAVE_SIZE ];
+ h += waves[ ((n[2] + x*STEP) & WAVE_MASK) + 2*WAVE_SIZE ];
+ h += waves[ ((n[3] + y*STEP) & WAVE_MASK) + 3*WAVE_SIZE ];
+
+ pSurf->height = h * depth;
+ pSurf++;
+ }
+ }
+
+ // compute normals
+ UINT uXN, uX0, uXP;
+ UINT uYN, uY0, uYP;
+
+ uYP = AREA - SIZE;
+ uY0 = 0;
+ uYN = SIZE;
+
+ for (y = 0; y < SIZE; y++) {
+ uXP = SIZE - 1;
+ uX0 = 0;
+ uXN = 1;
+
+ for (x = 0; x < SIZE; x++) {
+ Vec3 vecN;
+ float f;
+
+ f = surface[uXN + uYN].height - surface[uXP + uYP].height; vecN.x = vecN.z = f;
+ f = surface[uX0 + uYN].height - surface[uX0 + uYP].height; vecN.z += f;
+ f = surface[uXP + uYN].height - surface[uXN + uYP].height; vecN.x -= f; vecN.z += f;
+ f = surface[uXN + uY0].height - surface[uXP + uY0].height; vecN.x += f;
+
+ vecN.y = -15.0f * depth;
+ vecN.Normalize();
+
+ surface[uX0 + uY0].normal = vecN * -1.0f;
+
+ uXP = uX0;
+ uX0 = uXN;
+ uXN = (uXN + 1) % SIZE;
+ }
+
+ uYP = uY0;
+ uY0 = uYN;
+ uYN = (uYN + SIZE) % AREA;
+ }
+
+ // update offsets
+ for (i = 0; i < 4; i++) {
+ offsets[i] += (float) (offsets[i+4] * seconds);
+
+ if (offsets[i] > WAVE_SIZE)
+ offsets[i] -= WAVE_SIZE;
+ }
+
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Water::UpdateSurface(Vec3& eyePos, VertexSet* vset)
+{
+ UINT SIZE = nVertices;
+ UINT AREA = SIZE * SIZE;
+ UINT x, y;
+
+ WATER_SURFACE* pSurf = surface;
+ Vec3* pLoc = vset->loc;
+ Vec3* pNorm = vset->nrm;
+ DWORD* pDiff = vset->diffuse;
+ float* pTu = vset->tu;
+ float* pTv = vset->tv;
+
+ float fInc = 1.0f / (float) (SIZE-1);
+ float fx = 0.0f;
+ float fz = 0.0f;
+
+ for (y = 0; y < SIZE; y++) {
+ for (x = 0; x < SIZE; x++) {
+ // update vertex height and normal
+ pLoc->y += pSurf->height;
+ *pNorm = pSurf->normal;
+
+ /*
+ // Update texture coords and diffuse based upon refraction
+ Vec3 vec = eyePos - *pLoc;
+ vec.Normalize();
+
+ WATER_REFRACT *pRefract;
+ pRefract = RefractionTable + 256 + f2i(vec.dot(*pNorm) * 255.0f);
+
+ *pDiff = pRefract->diffuse;
+
+ // compute apparent displacement
+ Vec3 vecD = (pSurf->normal * pRefract->refract + vec) * pRefract->refractNorm;
+ Vec3 vecP = *pLoc;
+ vecP.y -= depth;
+
+ // perturb texture coords
+ float fB = vecD * vecP * 2.0f;
+ float fD = fB * fB - depth;
+ float fScale = (-fB + sqrtf(fD)) * 0.5f;
+
+ *pTu = vecD.x * fScale + fx;
+ *pTv = vecD.z * fScale + fz;
+ */
+
+ fx += fInc;
+ pSurf++;
+ pLoc++;
+ pNorm++;
+ pDiff++;
+ pTu++;
+ pTv++;
+ }
+
+ fx = 0.0f;
+ fz += fInc;
+ }
+}
+
+
diff --git a/nGenEx/Water.h b/nGenEx/Water.h
new file mode 100644
index 0000000..2f88db0
--- /dev/null
+++ b/nGenEx/Water.h
@@ -0,0 +1,54 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2006. All Rights Reserved.
+
+ SUBSYSTEM: nGen.lib
+ FILE: Water.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Water surface effect w/ reflection and refraction
+*/
+
+#ifndef Water_h
+#define Water_h
+
+#include "Geometry.h"
+#include "Polygon.h"
+#include "Color.h"
+
+// +--------------------------------------------------------------------+
+
+struct WATER_SURFACE;
+
+// +--------------------------------------------------------------------+
+
+class Water
+{
+public:
+ Water();
+ virtual ~Water();
+
+ virtual void Init(int nVerts, float size, float depth);
+ virtual void CalcWaves(double seconds);
+ virtual void UpdateSurface(Vec3& eyePos, VertexSet* vset);
+
+protected:
+ float size;
+ float depth;
+ float scaleTex;
+ float avgHeight;
+
+ DWORD nVertices;
+
+ WATER_SURFACE* surface;
+ float* waves;
+ float offsets[16];
+};
+
+// +--------------------------------------------------------------------+
+
+#endif Water_h
+
diff --git a/nGenEx/Wave.h b/nGenEx/Wave.h
new file mode 100644
index 0000000..9b630ca
--- /dev/null
+++ b/nGenEx/Wave.h
@@ -0,0 +1,52 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Wave.h
+ AUTHOR: John DiCamillo
+
+*/
+
+#ifndef Wave_h
+#define Wave_h
+
+#include "Types.h"
+
+// +--------------------------------------------------------------------+
+
+struct WAVE_HEADER
+{
+ DWORD RIFF;
+ DWORD file_len;
+ DWORD WAVE;
+};
+
+struct WAVE_FMT
+{
+ DWORD FMT;
+ DWORD chunk_size;
+ WORD wFormatTag;
+ WORD nChannels;
+ DWORD nSamplesPerSec;
+ DWORD nAvgBytesPerSec;
+ WORD nBlockAlign;
+ WORD wBitsPerSample;
+};
+
+struct WAVE_FACT
+{
+ DWORD FACT;
+ DWORD chunk_size;
+};
+
+struct WAVE_DATA
+{
+ DWORD DATA;
+ DWORD chunk_size;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif Wave_h
+
diff --git a/nGenEx/WebBrowser.cpp b/nGenEx/WebBrowser.cpp
new file mode 100644
index 0000000..a5f5b94
--- /dev/null
+++ b/nGenEx/WebBrowser.cpp
@@ -0,0 +1,133 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2006. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: WebBrowser.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Helper class to find and launch user's default web browser
+*/
+
+#include "MemDebug.h"
+#include "WebBrowser.h"
+
+// +--------------------------------------------------------------------+
+
+WebBrowser::WebBrowser()
+{
+ FindDefaultBrowser();
+ FindOpenCommand();
+}
+
+WebBrowser::~WebBrowser()
+{
+}
+
+// +--------------------------------------------------------------------+
+
+
+void
+WebBrowser::OpenURL(const char* url)
+{
+ if (url) {
+ char cmdline[256];
+
+ if (command.contains("%1")) {
+ strcpy(cmdline, command.replace("%1", url).data());
+ }
+ else {
+ strcpy(cmdline, Text(command + " " + url).data());
+ }
+
+ STARTUPINFO s;
+ ZeroMemory(&s, sizeof(s));
+ s.cb = sizeof(s);
+
+ PROCESS_INFORMATION pi;
+ ZeroMemory(&pi, sizeof(pi));
+
+ if (CreateProcess(NULL, cmdline, 0, 0, 0, 0, 0, 0, &s, &pi)) {
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ }
+ else {
+ ::Print("Unable to launch web browser for url '%s'\n", url);
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+WebBrowser::FindDefaultBrowser()
+{
+ HKEY hkey;
+ char value[256] = "";
+ DWORD dwSize;
+
+ ZeroMemory(value, 256);
+
+ if (RegOpenKeyEx(HKEY_CLASSES_ROOT,
+ ".html",
+ 0,
+ KEY_READ,
+ &hkey) == ERROR_SUCCESS) {
+
+ dwSize = 256;
+ RegQueryValueEx(hkey,
+ "",
+ NULL,
+ NULL,
+ (LPBYTE) value,
+ &dwSize);
+
+ RegCloseKey(hkey);
+
+ if (dwSize > 0) {
+ ::Print("Default Web Browser: %s\n", value);
+ browser = value;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+WebBrowser::FindOpenCommand()
+{
+ HKEY hkey;
+ char value[256] = "";
+ DWORD dwSize;
+
+ ZeroMemory(value, 256);
+
+ if (RegOpenKeyEx(HKEY_CLASSES_ROOT,
+ browser + "\\shell\\open\\command",
+ 0,
+ KEY_READ,
+ &hkey) == ERROR_SUCCESS) {
+
+ dwSize = 256;
+ RegQueryValueEx(hkey,
+ "",
+ NULL,
+ NULL,
+ (LPBYTE) value,
+ &dwSize);
+
+ RegCloseKey(hkey);
+
+ if (dwSize > 0) {
+ ::Print("Browser Shell Open Command: %s\n", value);
+ command = value;
+ }
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+
diff --git a/nGenEx/WebBrowser.h b/nGenEx/WebBrowser.h
new file mode 100644
index 0000000..b8a27e1
--- /dev/null
+++ b/nGenEx/WebBrowser.h
@@ -0,0 +1,46 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2006. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: WebBrowser.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Helper class to find and launch user's default web browser
+*/
+
+#ifndef WebBrowser_h
+#define WebBrowser_h
+
+#include "Types.h"
+#include "List.h"
+#include "Text.h"
+
+// +--------------------------------------------------------------------+
+
+class WebBrowser
+{
+public:
+ static const char* TYPENAME() { return "WebBrowser"; }
+
+ WebBrowser();
+ virtual ~WebBrowser();
+
+ virtual void OpenURL(const char* url);
+
+protected:
+ void FindDefaultBrowser();
+ void FindOpenCommand();
+
+ Text browser;
+ Text command;
+};
+
+// +--------------------------------------------------------------------+
+
+#endif WebBrowser_h
+
+
diff --git a/nGenEx/Window.cpp b/nGenEx/Window.cpp
new file mode 100644
index 0000000..a69e00d
--- /dev/null
+++ b/nGenEx/Window.cpp
@@ -0,0 +1,936 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Window.cpp
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Window class
+*/
+
+#include "MemDebug.h"
+#include "Window.h"
+#include "Bitmap.h"
+#include "Color.h"
+#include "Fix.h"
+#include "Font.h"
+#include "Polygon.h"
+#include "Screen.h"
+#include "Video.h"
+#include "View.h"
+
+static VertexSet vset4(4);
+
+// +--------------------------------------------------------------------+
+
+Window::Window(Screen* s, int ax, int ay, int aw, int ah)
+ : screen(s), rect(ax, ay, aw, ah), shown(true), font(0)
+{ }
+
+// +--------------------------------------------------------------------+
+
+Window::~Window()
+{
+ view_list.destroy();
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Window::AddView(View* v)
+{
+ if (!v) return false;
+
+ if (!view_list.contains(v))
+ view_list.append(v);
+
+ return true;
+}
+
+bool
+Window::DelView(View* v)
+{
+ if (!v) return false;
+
+ return view_list.remove(v) == v;
+}
+
+void
+Window::MoveTo(const Rect& r)
+{
+ if (rect.x == r.x &&
+ rect.y == r.y &&
+ rect.w == r.w &&
+ rect.h == r.h)
+ return;
+
+ rect = r;
+
+ ListIter<View> v = view_list;
+ while (++v)
+ v->OnWindowMove();
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Window::Paint()
+{
+ ListIter<View> v = view_list;
+ while (++v)
+ v->Refresh();
+}
+
+// +--------------------------------------------------------------------+
+
+static inline void swap(int& a, int& b) { int tmp=a; a=b; b=tmp; }
+static inline void sort(int& a, int& b) { if (a>b) swap(a,b); }
+static inline void swap(double& a, double& b) { double tmp=a; a=b; b=tmp; }
+static inline void sort(double& a, double& b) { if (a>b) swap(a,b); }
+
+Rect
+Window::ClipRect(const Rect& r)
+{
+ Rect clip_rect = r;
+
+ clip_rect.x += rect.x;
+ clip_rect.y += rect.y;
+
+ if (clip_rect.x < rect.x) {
+ clip_rect.w -= rect.x - clip_rect.x;
+ clip_rect.x = rect.x;
+ }
+
+ if (clip_rect.y < rect.y) {
+ clip_rect.h -= rect.y - clip_rect.y;
+ clip_rect.y = rect.y;
+ }
+
+ if (clip_rect.x + clip_rect.w > rect.x + rect.w)
+ clip_rect.w = rect.x + rect.w - clip_rect.x;
+
+ if (clip_rect.y + clip_rect.h > rect.y + rect.h)
+ clip_rect.h = rect.y + rect.h - clip_rect.y;
+
+ return clip_rect;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Window::ClipLine(int& x1, int& y1, int& x2, int& y2)
+{
+ // vertical lines:
+ if (x1==x2) {
+clip_vertical:
+ sort(y1,y2);
+ if (x1 < 0 || x1 >= rect.w) return false;
+ if (y1 < 0) y1 = 0;
+ if (y2 >= rect.h) y2 = rect.h;
+ return true;
+ }
+
+ // horizontal lines:
+ if (y1==y2) {
+clip_horizontal:
+ sort(x1,x2);
+ if (y1 < 0 || y1 >= rect.h) return false;
+ if (x1 < 0) x1 = 0;
+ if (x2 > rect.w) x2 = rect.w;
+ return true;
+ }
+
+ // general lines:
+
+ // sort left to right:
+ if (x1 > x2) {
+ swap(x1,x2);
+ swap(y1,y2);
+ }
+
+ double m = (double)(y2-y1) / (double)(x2-x1);
+ double b = (double) y1 - (m * x1);
+
+ // clip:
+ if (x1 < 0) { x1 = 0; y1 = (int) b; }
+ if (x1 >= rect.w) return false;
+ if (x2 < 0) return false;
+ if (x2 > rect.w-1) { x2 = rect.w-1; y2 = (int) (m * x2 + b); }
+
+ if (y1 < 0 && y2 < 0) return false;
+ if (y1 >= rect.h && y2 >= rect.h) return false;
+
+ if (y1 < 0) { y1 = 0; x1 = (int) (-b/m); }
+ if (y1 >= rect.h) { y1 = rect.h-1; x1 = (int) ((y1-b)/m); }
+ if (y2 < 0) { y2 = 0; x2 = (int) (-b/m); }
+ if (y2 >= rect.h) { y2 = rect.h-1; x2 = (int) ((y2-b)/m); }
+
+ if (x1 == x2)
+ goto clip_vertical;
+
+ if (y1 == y2)
+ goto clip_horizontal;
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+bool
+Window::ClipLine(double& x1, double& y1, double& x2, double& y2)
+{
+ // vertical lines:
+ if (x1==x2) {
+clip_vertical:
+ sort(y1,y2);
+ if (x1 < 0 || x1 >= rect.w) return false;
+ if (y1 < 0) y1 = 0;
+ if (y2 >= rect.h) y2 = rect.h;
+ return true;
+ }
+
+ // horizontal lines:
+ if (y1==y2) {
+clip_horizontal:
+ sort(x1,x2);
+ if (y1 < 0 || y1 >= rect.h) return false;
+ if (x1 < 0) x1 = 0;
+ if (x2 > rect.w) x2 = rect.w;
+ return true;
+ }
+
+ // general lines:
+
+ // sort left to right:
+ if (x1 > x2) {
+ swap(x1,x2);
+ swap(y1,y2);
+ }
+
+ double m = (double)(y2-y1) / (double)(x2-x1);
+ double b = (double) y1 - (m * x1);
+
+ // clip:
+ if (x1 < 0) { x1 = 0; y1 = b; }
+ if (x1 >= rect.w) return false;
+ if (x2 < 0) return false;
+ if (x2 > rect.w-1) { x2 = rect.w-1; y2 = (m * x2 + b); }
+
+ if (y1 < 0 && y2 < 0) return false;
+ if (y1 >= rect.h && y2 >= rect.h) return false;
+
+ if (y1 < 0) { y1 = 0; x1 = (-b/m); }
+ if (y1 >= rect.h) { y1 = rect.h-1; x1 = ((y1-b)/m); }
+ if (y2 < 0) { y2 = 0; x2 = (-b/m); }
+ if (y2 >= rect.h) { y2 = rect.h-1; x2 = ((y2-b)/m); }
+
+ if (x1 == x2)
+ goto clip_vertical;
+
+ if (y1 == y2)
+ goto clip_horizontal;
+
+ return true;
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Window::DrawLine(int x1, int y1, int x2, int y2, Color color, int blend)
+{
+ if (!screen || !screen->GetVideo()) return;
+
+ if (ClipLine(x1,y1,x2,y2)) {
+ float points[4];
+
+ points[0] = (float) (rect.x + x1);
+ points[1] = (float) (rect.y + y1);
+ points[2] = (float) (rect.x + x2);
+ points[3] = (float) (rect.y + y2);
+
+ Video* video = screen->GetVideo();
+ video->DrawScreenLines(1, points, color, blend);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Window::DrawRect(int x1, int y1, int x2, int y2, Color color, int blend)
+{
+ if (!screen || !screen->GetVideo()) return;
+
+ sort(x1,x2);
+ sort(y1,y2);
+
+ if (x1 > rect.w || x2 < 0 || y1 > rect.h || y2 < 0)
+ return;
+
+ float points[16];
+
+ points[ 0] = (float) (rect.x + x1);
+ points[ 1] = (float) (rect.y + y1);
+ points[ 2] = (float) (rect.x + x2);
+ points[ 3] = (float) (rect.y + y1);
+
+ points[ 4] = (float) (rect.x + x2);
+ points[ 5] = (float) (rect.y + y1);
+ points[ 6] = (float) (rect.x + x2);
+ points[ 7] = (float) (rect.y + y2);
+
+ points[ 8] = (float) (rect.x + x2);
+ points[ 9] = (float) (rect.y + y2);
+ points[10] = (float) (rect.x + x1);
+ points[11] = (float) (rect.y + y2);
+
+ points[12] = (float) (rect.x + x1);
+ points[13] = (float) (rect.y + y2);
+ points[14] = (float) (rect.x + x1);
+ points[15] = (float) (rect.y + y1);
+
+ Video* video = screen->GetVideo();
+ video->DrawScreenLines(4, points, color, blend);
+}
+
+void
+Window::DrawRect(const Rect& r, Color color, int blend)
+{
+ DrawRect(r.x, r.y, r.x+r.w, r.y+r.h, color, blend);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Window::FillRect(int x1, int y1, int x2, int y2, Color color, int blend)
+{
+ if (!screen || !screen->GetVideo()) return;
+
+ sort(x1,x2);
+ sort(y1,y2);
+
+ if (x1 > rect.w || x2 < 0 || y1 > rect.h || y2 < 0)
+ return;
+
+ vset4.space = VertexSet::SCREEN_SPACE;
+ for (int i = 0; i < 4; i++) {
+ vset4.diffuse[i] = color.Value();
+ }
+
+ vset4.s_loc[0].x = (float) (rect.x + x1) - 0.5f;
+ vset4.s_loc[0].y = (float) (rect.y + y1) - 0.5f;
+ vset4.s_loc[0].z = 0.0f;
+ vset4.rw[0] = 1.0f;
+ vset4.tu[0] = 0.0f;
+ vset4.tv[0] = 0.0f;
+
+ vset4.s_loc[1].x = (float) (rect.x + x2) - 0.5f;
+ vset4.s_loc[1].y = (float) (rect.y + y1) - 0.5f;
+ vset4.s_loc[1].z = 0.0f;
+ vset4.rw[1] = 1.0f;
+ vset4.tu[1] = 1.0f;
+ vset4.tv[1] = 0.0f;
+
+ vset4.s_loc[2].x = (float) (rect.x + x2) - 0.5f;
+ vset4.s_loc[2].y = (float) (rect.y + y2) - 0.5f;
+ vset4.s_loc[2].z = 0.0f;
+ vset4.rw[2] = 1.0f;
+ vset4.tu[2] = 1.0f;
+ vset4.tv[2] = 1.0f;
+
+ vset4.s_loc[3].x = (float) (rect.x + x1) - 0.5f;
+ vset4.s_loc[3].y = (float) (rect.y + y2) - 0.5f;
+ vset4.s_loc[3].z = 0.0f;
+ vset4.rw[3] = 1.0f;
+ vset4.tu[3] = 0.0f;
+ vset4.tv[3] = 1.0f;
+
+ Poly poly(0);
+ poly.nverts = 4;
+ poly.vertex_set = &vset4;
+ poly.verts[0] = 0;
+ poly.verts[1] = 1;
+ poly.verts[2] = 2;
+ poly.verts[3] = 3;
+
+ Video* video = screen->GetVideo();
+ video->DrawScreenPolys(1, &poly, blend);
+}
+
+void
+Window::FillRect(const Rect& r, Color color, int blend)
+{
+ FillRect(r.x, r.y, r.x+r.w, r.y+r.h, color, blend);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Window::DrawLines(int nPts, POINT* pts, Color color, int blend)
+{
+ if (nPts < 2 || nPts > 16)
+ return;
+
+ if (!screen || !screen->GetVideo())
+ return;
+
+ float f[64];
+ int n = 0;
+
+ for (int i = 0; i < nPts-1; i++) {
+ f[n++] = (float) rect.x + pts[i].x;
+ f[n++] = (float) rect.y + pts[i].y;
+ f[n++] = (float) rect.x + pts[i+1].x;
+ f[n++] = (float) rect.y + pts[i+1].y;
+ }
+
+ Video* video = screen->GetVideo();
+ video->DrawScreenLines(nPts-1, f, color, blend);
+}
+
+void
+Window::DrawPoly(int nPts, POINT* pts, Color color, int blend)
+{
+ if (nPts < 3 || nPts > 8)
+ return;
+
+ if (!screen || !screen->GetVideo())
+ return;
+
+ float f[32];
+ int n = 0;
+
+ for (int i = 0; i < nPts-1; i++) {
+ f[n++] = (float) rect.x + pts[i].x;
+ f[n++] = (float) rect.y + pts[i].y;
+ f[n++] = (float) rect.x + pts[i+1].x;
+ f[n++] = (float) rect.y + pts[i+1].y;
+ }
+
+ f[n++] = (float) rect.x + pts[nPts-1].x;
+ f[n++] = (float) rect.y + pts[nPts-1].y;
+ f[n++] = (float) rect.x + pts[0].x;
+ f[n++] = (float) rect.y + pts[0].y;
+
+ Video* video = screen->GetVideo();
+ video->DrawScreenLines(nPts, f, color, blend);
+}
+
+void
+Window::FillPoly(int nPts, POINT* pts, Color color, int blend)
+{
+ if (nPts < 3 || nPts > 4)
+ return;
+
+ if (!screen || !screen->GetVideo())
+ return;
+
+ vset4.space = VertexSet::SCREEN_SPACE;
+ for (int i = 0; i < nPts; i++) {
+ vset4.diffuse[i] = color.Value();
+ vset4.s_loc[i].x = (float) (rect.x + pts[i].x) - 0.5f;
+ vset4.s_loc[i].y = (float) (rect.y + pts[i].y) - 0.5f;
+ vset4.s_loc[i].z = 0.0f;
+ vset4.rw[i] = 1.0f;
+ vset4.tu[i] = 0.0f;
+ vset4.tv[i] = 0.0f;
+ }
+
+ Poly poly(0);
+ poly.nverts = nPts;
+ poly.vertex_set = &vset4;
+ poly.verts[0] = 0;
+ poly.verts[1] = 1;
+ poly.verts[2] = 2;
+ poly.verts[3] = 3;
+
+ Video* video = screen->GetVideo();
+ video->DrawScreenPolys(1, &poly, blend);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Window::DrawBitmap(int x1, int y1, int x2, int y2, Bitmap* img, int blend)
+{
+ Rect clip_rect;
+ clip_rect.w = rect.w;
+ clip_rect.h = rect.h;
+
+ ClipBitmap(x1,y1,x2,y2,img,Color::White,blend,clip_rect);
+}
+
+void
+Window::FadeBitmap(int x1, int y1, int x2, int y2, Bitmap* img, Color c, int blend)
+{
+ Rect clip_rect;
+ clip_rect.w = rect.w;
+ clip_rect.h = rect.h;
+
+ ClipBitmap(x1,y1,x2,y2,img,c,blend,clip_rect);
+}
+
+void
+Window::ClipBitmap(int x1, int y1, int x2, int y2, Bitmap* img, Color c, int blend, const Rect& clip_rect)
+{
+ if (!screen || !screen->GetVideo() || !img) return;
+
+ Rect clip = clip_rect;
+
+ // clip the clip rect to the window rect:
+ if (clip.x < 0) {
+ clip.w -= clip.x;
+ clip.x = 0;
+ }
+
+ if (clip.x + clip.w > rect.w) {
+ clip.w -= (clip.x + clip.w - rect.w);
+ }
+
+ if (clip.y < 0) {
+ clip.h -= clip.y;
+ clip.y = 0;
+ }
+
+ if (clip.y + clip.h > rect.h) {
+ clip.h -= (clip.y + clip.h - rect.h);
+ }
+
+ // now clip the bitmap to the validated clip rect:
+ sort(x1,x2);
+ sort(y1,y2);
+
+ if (x1 > clip.x + clip.w || x2 < clip.x || y1 > clip.y + clip.h || y2 < clip.y)
+ return;
+
+ vset4.space = VertexSet::SCREEN_SPACE;
+ for (int i = 0; i < 4; i++) {
+ vset4.diffuse[i] = c.Value();
+ }
+
+ float u1 = 0.0f;
+ float u2 = 1.0f;
+ float v1 = 0.0f;
+ float v2 = 1.0f;
+ float iw = (float) (x2-x1);
+ float ih = (float) (y2-y1);
+ int x3 = clip.x + clip.w;
+ int y3 = clip.y + clip.h;
+
+ if (x1 < clip.x) {
+ u1 = (clip.x - x1) / iw;
+ x1 = clip.x;
+ }
+
+ if (x2 > x3) {
+ u2 = 1.0f - (x2 - x3) / iw;
+ x2 = x3;
+ }
+
+ if (y1 < clip.y) {
+ v1 = (clip.y - y1) / ih;
+ y1 = clip.y;
+ }
+
+ if (y2 > y3) {
+ v2 = 1.0f - (y2 - y3) / ih;
+ y2 = y3;
+ }
+
+ vset4.s_loc[0].x = (float) (rect.x + x1) - 0.5f;
+ vset4.s_loc[0].y = (float) (rect.y + y1) - 0.5f;
+ vset4.s_loc[0].z = 0.0f;
+ vset4.rw[0] = 1.0f;
+ vset4.tu[0] = u1;
+ vset4.tv[0] = v1;
+
+ vset4.s_loc[1].x = (float) (rect.x + x2) - 0.5f;
+ vset4.s_loc[1].y = (float) (rect.y + y1) - 0.5f;
+ vset4.s_loc[1].z = 0.0f;
+ vset4.rw[1] = 1.0f;
+ vset4.tu[1] = u2;
+ vset4.tv[1] = v1;
+
+ vset4.s_loc[2].x = (float) (rect.x + x2) - 0.5f;
+ vset4.s_loc[2].y = (float) (rect.y + y2) - 0.5f;
+ vset4.s_loc[2].z = 0.0f;
+ vset4.rw[2] = 1.0f;
+ vset4.tu[2] = u2;
+ vset4.tv[2] = v2;
+
+ vset4.s_loc[3].x = (float) (rect.x + x1) - 0.5f;
+ vset4.s_loc[3].y = (float) (rect.y + y2) - 0.5f;
+ vset4.s_loc[3].z = 0.0f;
+ vset4.rw[3] = 1.0f;
+ vset4.tu[3] = u1;
+ vset4.tv[3] = v2;
+
+ Material mtl;
+ mtl.tex_diffuse = img;
+
+ Poly poly(0);
+ poly.nverts = 4;
+ poly.vertex_set = &vset4;
+ poly.material = &mtl;
+ poly.verts[0] = 0;
+ poly.verts[1] = 1;
+ poly.verts[2] = 2;
+ poly.verts[3] = 3;
+
+ Video* video = screen->GetVideo();
+
+ video->SetRenderState(Video::TEXTURE_WRAP, 0);
+ video->DrawScreenPolys(1, &poly, blend);
+ video->SetRenderState(Video::TEXTURE_WRAP, 1);
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Window::TileBitmap(int x1, int y1, int x2, int y2, Bitmap* img, int blend)
+{
+ if (!screen || !screen->GetVideo()) return;
+ if (!img || !img->Width() || !img->Height()) return;
+
+ vset4.space = VertexSet::SCREEN_SPACE;
+ for (int i = 0; i < 4; i++) {
+ vset4.diffuse[i] = Color::White.Value();
+ }
+
+ float xscale = (float) rect.w / (float) img->Width();
+ float yscale = (float) rect.h / (float) img->Height();
+
+ vset4.s_loc[0].x = (float) (rect.x + x1) - 0.5f;
+ vset4.s_loc[0].y = (float) (rect.y + y1) - 0.5f;
+ vset4.s_loc[0].z = 0.0f;
+ vset4.rw[0] = 1.0f;
+ vset4.tu[0] = 0.0f;
+ vset4.tv[0] = 0.0f;
+
+ vset4.s_loc[1].x = (float) (rect.x + x2) - 0.5f;
+ vset4.s_loc[1].y = (float) (rect.y + y1) - 0.5f;
+ vset4.s_loc[1].z = 0.0f;
+ vset4.rw[1] = 1.0f;
+ vset4.tu[1] = xscale;
+ vset4.tv[1] = 0.0f;
+
+ vset4.s_loc[2].x = (float) (rect.x + x2) - 0.5f;
+ vset4.s_loc[2].y = (float) (rect.y + y2) - 0.5f;
+ vset4.s_loc[2].z = 0.0f;
+ vset4.rw[2] = 1.0f;
+ vset4.tu[2] = xscale;
+ vset4.tv[2] = yscale;
+
+ vset4.s_loc[3].x = (float) (rect.x + x1) - 0.5f;
+ vset4.s_loc[3].y = (float) (rect.y + y2) - 0.5f;
+ vset4.s_loc[3].z = 0.0f;
+ vset4.rw[3] = 1.0f;
+ vset4.tu[3] = 0.0f;
+ vset4.tv[3] = yscale;
+
+ Material mtl;
+ mtl.tex_diffuse = img;
+
+ Poly poly(0);
+ poly.nverts = 4;
+ poly.vertex_set = &vset4;
+ poly.material = &mtl;
+ poly.verts[0] = 0;
+ poly.verts[1] = 1;
+ poly.verts[2] = 2;
+ poly.verts[3] = 3;
+
+ Video* video = screen->GetVideo();
+ video->DrawScreenPolys(1, &poly, blend);
+}
+
+// +--------------------------------------------------------------------+
+
+static float ellipse_pts[256];
+
+void
+Window::DrawEllipse(int x1, int y1, int x2, int y2, Color color, int blend)
+{
+ Video* video = screen->GetVideo();
+
+ if (!video)
+ return;
+
+ sort(x1,x2);
+ sort(y1,y2);
+
+ if (x1 > rect.w || x2 < 0 || y1 > rect.h || y2 < 0)
+ return;
+
+ double w2 = (x2-x1)/2.0;
+ double h2 = (y2-y1)/2.0;
+ double cx = rect.x + x1 + w2;
+ double cy = rect.y + y1 + h2;
+ double r = w2;
+ int ns = 4;
+ int np = 0;
+
+ if (h2 > r)
+ r = h2;
+
+ if (r > 2*ns)
+ ns = (int) (r/2);
+
+ if (ns > 64)
+ ns = 64;
+
+ double theta = 0;
+ double dt = (PI/2) / ns;
+
+ // quadrant 1 (lower right):
+ if (cx < (rect.x+rect.w) && cy < (rect.y + rect.h)) {
+ theta = 0;
+ np = 0;
+
+ for (int i = 0; i < ns; i++) {
+ double ex1 = x1 + w2 + cos(theta) * w2;
+ double ey1 = y1 + h2 + sin(theta) * h2;
+
+ theta += dt;
+
+ double ex2 = x1 + w2 + cos(theta) * w2;
+ double ey2 = y1 + h2 + sin(theta) * h2;
+
+ if (ClipLine(ex1, ey1, ex2, ey2)) {
+ ellipse_pts[np++] = (float) (rect.x + ex1);
+ ellipse_pts[np++] = (float) (rect.y + ey1);
+ ellipse_pts[np++] = (float) (rect.x + ex2);
+ ellipse_pts[np++] = (float) (rect.y + ey2);
+ }
+ }
+
+ video->DrawScreenLines(np/4, ellipse_pts, color, blend);
+ }
+
+ // quadrant 2 (lower left):
+ if (cx > rect.x && cy < (rect.y + rect.h)) {
+ theta = 90*DEGREES;
+ np = 0;
+
+ for (int i = 0; i < ns; i++) {
+ double ex1 = x1 + w2 + cos(theta) * w2;
+ double ey1 = y1 + h2 + sin(theta) * h2;
+
+ theta += dt;
+
+ double ex2 = x1 + w2 + cos(theta) * w2;
+ double ey2 = y1 + h2 + sin(theta) * h2;
+
+ if (ClipLine(ex1, ey1, ex2, ey2)) {
+ ellipse_pts[np++] = (float) (rect.x + ex1);
+ ellipse_pts[np++] = (float) (rect.y + ey1);
+ ellipse_pts[np++] = (float) (rect.x + ex2);
+ ellipse_pts[np++] = (float) (rect.y + ey2);
+ }
+ }
+
+ video->DrawScreenLines(np/4, ellipse_pts, color, blend);
+ }
+
+ // quadrant 3 (upper left):
+ if (cx > rect.x && cy > rect.y) {
+ theta = 180*DEGREES;
+ np = 0;
+
+ for (int i = 0; i < ns; i++) {
+ double ex1 = x1 + w2 + cos(theta) * w2;
+ double ey1 = y1 + h2 + sin(theta) * h2;
+
+ theta += dt;
+
+ double ex2 = x1 + w2 + cos(theta) * w2;
+ double ey2 = y1 + h2 + sin(theta) * h2;
+
+ if (ClipLine(ex1, ey1, ex2, ey2)) {
+ ellipse_pts[np++] = (float) (rect.x + ex1);
+ ellipse_pts[np++] = (float) (rect.y + ey1);
+ ellipse_pts[np++] = (float) (rect.x + ex2);
+ ellipse_pts[np++] = (float) (rect.y + ey2);
+ }
+ }
+
+ video->DrawScreenLines(np/4, ellipse_pts, color, blend);
+ }
+
+ // quadrant 4 (upper right):
+ if (cx < (rect.x+rect.w) && cy > rect.y) {
+ theta = 270*DEGREES;
+ np = 0;
+
+ for (int i = 0; i < ns; i++) {
+ double ex1 = x1 + w2 + cos(theta) * w2;
+ double ey1 = y1 + h2 + sin(theta) * h2;
+
+ theta += dt;
+
+ double ex2 = x1 + w2 + cos(theta) * w2;
+ double ey2 = y1 + h2 + sin(theta) * h2;
+
+ if (ClipLine(ex1, ey1, ex2, ey2)) {
+ ellipse_pts[np++] = (float) (rect.x + ex1);
+ ellipse_pts[np++] = (float) (rect.y + ey1);
+ ellipse_pts[np++] = (float) (rect.x + ex2);
+ ellipse_pts[np++] = (float) (rect.y + ey2);
+ }
+ }
+
+ video->DrawScreenLines(np/4, ellipse_pts, color, blend);
+ }
+
+}
+
+void
+Window::FillEllipse(int x1, int y1, int x2, int y2, Color color, int blend)
+{
+ Video* video = screen->GetVideo();
+
+ if (!video)
+ return;
+
+ sort(x1,x2);
+ sort(y1,y2);
+
+ if (x1 > rect.w || x2 < 0 || y1 > rect.h || y2 < 0)
+ return;
+
+ double w2 = (x2-x1)/2.0;
+ double h2 = (y2-y1)/2.0;
+ double cx = x1 + w2;
+ double cy = y1 + h2;
+ double r = w2;
+ int ns = 4;
+ int np = 0;
+
+ if (h2 > r)
+ r = h2;
+
+ if (r > 2*ns)
+ ns = (int) (r/2);
+
+ if (ns > 64)
+ ns = 64;
+
+ double theta = -PI / 2;
+ double dt = PI / ns;
+
+ for (int i = 0; i < ns; i++) {
+ double ex1 = cos(theta) * w2;
+ double ey1 = sin(theta) * h2;
+
+ theta += dt;
+
+ double ex2 = cos(theta) * w2;
+ double ey2 = sin(theta) * h2;
+
+ POINT pts[4];
+
+ pts[0].x = (int) (cx - ex1);
+ pts[0].y = (int) (cy + ey1);
+
+ pts[1].x = (int) (cx + ex1);
+ pts[1].y = (int) (cy + ey1);
+
+ pts[2].x = (int) (cx + ex2);
+ pts[2].y = (int) (cy + ey2);
+
+ pts[3].x = (int) (cx - ex2);
+ pts[3].y = (int) (cy + ey2);
+
+ if (pts[0].x > rect.w && pts[3].x > rect.w)
+ continue;
+
+ if (pts[1].x < 0 && pts[2].x < 0)
+ continue;
+
+ if (pts[0].y > rect.h)
+ return;
+
+ if (pts[2].y < 0)
+ continue;
+
+ if (pts[0].x < 0) pts[0].x = 0;
+ if (pts[3].x < 0) pts[3].x = 0;
+ if (pts[1].x > rect.w) pts[1].x = rect.w;
+ if (pts[2].x > rect.w) pts[2].x = rect.w;
+
+ if (pts[0].y < 0) pts[0].y = 0;
+ if (pts[1].y < 0) pts[1].y = 0;
+ if (pts[2].y > rect.h) pts[2].y = rect.h;
+ if (pts[3].y > rect.h) pts[3].y = rect.h;
+
+ FillPoly(4, pts, color, blend);
+ }
+}
+
+// +--------------------------------------------------------------------+
+
+void
+Window::Print(int x1, int y1, const char* fmt, ...)
+{
+ if (!font || x1<0 || y1<0 || x1>=rect.w || y1>=rect.h || !fmt)
+ return;
+
+ x1 += rect.x;
+ y1 += rect.y;
+
+ char msgbuf[512];
+ vsprintf(msgbuf, fmt, (char *)(&fmt+1));
+ font->DrawString(msgbuf, strlen(msgbuf), x1, y1, rect);
+}
+
+void
+Window::DrawText(const char* txt, int count, Rect& txt_rect, DWORD flags)
+{
+ if (!font)
+ return;
+
+ if (txt && !count)
+ count = strlen(txt);
+
+ // clip the rect:
+ Rect clip_rect = txt_rect;
+
+ if (clip_rect.x < 0) {
+ int dx = -clip_rect.x;
+ clip_rect.x += dx;
+ clip_rect.w -= dx;
+ }
+
+ if (clip_rect.y < 0) {
+ int dy = -clip_rect.y;
+ clip_rect.y += dy;
+ clip_rect.h -= dy;
+ }
+
+ if (clip_rect.w < 1 || clip_rect.h < 1)
+ return;
+
+ if (clip_rect.x + clip_rect.w > rect.w)
+ clip_rect.w = rect.w - clip_rect.x;
+
+ if (clip_rect.y + clip_rect.h > rect.h)
+ clip_rect.h = rect.h - clip_rect.y;
+
+ clip_rect.x += rect.x;
+ clip_rect.y += rect.y;
+
+ if (font && txt && count) {
+ font->DrawText(txt, count, clip_rect, flags);
+ font->SetAlpha(1);
+ }
+
+ // if calc only, update the rectangle:
+ if (flags & DT_CALCRECT) {
+ txt_rect.h = clip_rect.h;
+ txt_rect.w = clip_rect.w;
+ }
+}
+
diff --git a/nGenEx/Window.h b/nGenEx/Window.h
new file mode 100644
index 0000000..ccafd96
--- /dev/null
+++ b/nGenEx/Window.h
@@ -0,0 +1,104 @@
+/* Project nGenEx
+ Destroyer Studios LLC
+ Copyright © 1997-2004. All Rights Reserved.
+
+ SUBSYSTEM: nGenEx.lib
+ FILE: Window.h
+ AUTHOR: John DiCamillo
+
+
+ OVERVIEW
+ ========
+ Window class (a region of a screen or buffer)
+*/
+
+#ifndef Window_h
+#define Window_h
+
+#include "Types.h"
+#include "Geometry.h"
+#include "List.h"
+
+// +--------------------------------------------------------------------+
+
+class Color;
+class Bitmap;
+class Font;
+class Screen;
+class View;
+
+// +--------------------------------------------------------------------+
+
+class Window
+{
+ friend class Screen;
+
+public:
+ static const char* TYPENAME() { return "Window"; }
+
+ Window(Screen* s, int ax, int ay, int aw, int ah);
+ virtual ~Window();
+
+ int operator == (const Window& that) const { return this == &that; }
+
+ // Screen dimensions:
+ Screen* GetScreen() const { return screen; }
+ const Rect& GetRect() const { return rect; }
+ int X() const { return rect.x; }
+ int Y() const { return rect.y; }
+ int Width() const { return rect.w; }
+ int Height() const { return rect.h; }
+
+ // Operations:
+ virtual void Paint();
+ virtual void Show() { shown = true; }
+ virtual void Hide() { shown = false; }
+ virtual bool IsShown() const { return shown; }
+
+ virtual void MoveTo(const Rect& r);
+
+ virtual bool AddView(View* v);
+ virtual bool DelView(View* v);
+
+ Rect ClipRect(const Rect& r);
+ bool ClipLine(int& x1, int& y1, int& x2, int& y2);
+ bool ClipLine(double& x1, double& y1, double& x2, double& y2);
+
+ void DrawLine(int x1, int y1, int x2, int y2, Color color, int blend=0);
+ void DrawRect(int x1, int y1, int x2, int y2, Color color, int blend=0);
+ void DrawRect(const Rect& r, Color color, int blend=0);
+ void FillRect(int x1, int y1, int x2, int y2, Color color, int blend=0);
+ void FillRect(const Rect& r, Color color, int alpha=0);
+ void DrawBitmap(int x1, int y1, int x2, int y2, Bitmap* img, int blend=0);
+ void FadeBitmap(int x1, int y1, int x2, int y2, Bitmap* img, Color c, int blend);
+ void ClipBitmap(int x1, int y1, int x2, int y2, Bitmap* img, Color c, int blend, const Rect& clip);
+ void TileBitmap(int x1, int y1, int x2, int y2, Bitmap* img, int blend=0);
+ void DrawLines(int nPts, POINT* pts, Color color, int blend=0);
+ void DrawPoly(int nPts, POINT* pts, Color color, int blend=0);
+ void FillPoly(int nPts, POINT* pts, Color color, int blend=0);
+
+ void DrawEllipse(int x1, int y1, int x2, int y2, Color color, int blend=0);
+ void FillEllipse(int x1, int y1, int x2, int y2, Color color, int blend=0);
+
+ // text methods:
+ void SetFont(Font* f) { font = f; }
+ Font* GetFont() const { return font; }
+
+ void Print(int x1, int y1, const char* fmt, ...);
+ void DrawText(const char* txt, int count, Rect& txt_rect, DWORD flags);
+
+protected:
+ // translate screen coords into window relative coords
+ virtual void ScreenToWindow(int& x, int& y) { }
+ virtual void ScreenToWindow(Rect& r) { }
+
+ Rect rect;
+ Screen* screen;
+ bool shown;
+ Font* font;
+
+ List<View> view_list;
+};
+
+#endif Window_h
+
diff --git a/nGenEx/nGenEx.dsp b/nGenEx/nGenEx.dsp
new file mode 100644
index 0000000..5a179f9
--- /dev/null
+++ b/nGenEx/nGenEx.dsp
@@ -0,0 +1,800 @@
+# Microsoft Developer Studio Project File - Name="nGenEx" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Static Library" 0x0104
+
+CFG=nGenEx - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "nGenEx.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "nGenEx.mak" CFG="nGenEx - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "nGenEx - Win32 Release" (based on "Win32 (x86) Static Library")
+!MESSAGE "nGenEx - Win32 Debug" (based on "Win32 (x86) Static Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "nGenEx - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\parser" /I "..\FoundationEx" /I "..\zlib" /I "..\LibPng" /I "..\Opcode\OpcodeLib" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FR /YX /FD /c
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ELSEIF "$(CFG)" == "nGenEx - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "nGenEx___Win32_Debug"
+# PROP BASE Intermediate_Dir "nGenEx___Win32_Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\parser" /I "..\FoundationEx" /I "..\zlib" /I "..\LibPng" /I "..\Opcode\OpcodeLib" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FR /YX /FD /GZ /c
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LIB32=link.exe -lib
+# ADD BASE LIB32 /nologo
+# ADD LIB32 /nologo
+
+!ENDIF
+
+# Begin Target
+
+# Name "nGenEx - Win32 Release"
+# Name "nGenEx - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\ActiveWindow.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Archive.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\FoundationEx\ArrayList.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\AviFile.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Bitmap.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Bmp.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Bolt.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Button.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Camera.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\CameraView.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Color.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\ComboBox.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\ComboList.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\ContentBundle.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\D3DXImage.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\DataLoader.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\EditBox.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Encrypt.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\EventDispatch.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\FadeView.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Fix.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Font.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\FontMgr.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\FormatUtil.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\FormDef.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\FormWindow.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Game.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Geometry.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Graphic.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\ImageBox.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\ImgView.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Joystick.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Keyboard.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Layout.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Light.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\ListBox.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Locale.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\MachineInfo.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\MCIWave.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\FoundationEx\MemDebug.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Menu.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Mouse.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\MouseController.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\MultiController.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\Parser\Parser.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\ParseUtil.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Particles.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\PCX.CPP
+# End Source File
+# Begin Source File
+
+SOURCE=.\Physical.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\PngImage.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Polygon.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Projector.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Random.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\Parser\Reader.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Res.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\RichTextBox.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Scene.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Screen.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\ScrollWindow.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Sha1.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Shadow.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Skin.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Slider.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Solid.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Sound.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\SoundCard.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\SoundD3D.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Sprite.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\Parser\Term.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\TexCubeDX9.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\TexDX9.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\FoundationEx\Text.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\Parser\Token.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Video.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\VideoDX9.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\VideoDX9Enum.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\VideoDX9VertexBuffer.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\VideoFactory.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\VideoSettings.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Water.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\WebBrowser.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\Window.cpp
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\ActiveWindow.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Archive.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\FoundationEx\ArrayList.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\AviFile.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Bitmap.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Bmp.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Bolt.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Button.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Camera.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\CameraView.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Color.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ComboBox.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ComboList.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ContentBundle.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\D3DXImage.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\DataLoader.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\FoundationEx\Dictionary.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\FoundationEx\Dictionary.inl
+# End Source File
+# Begin Source File
+
+SOURCE=.\Director.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\EditBox.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Encrypt.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\EventDispatch.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\EventTarget.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\EventTgt.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Fix.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Font.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\FontMgr.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\FormatUtil.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\FormDef.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\FormWindow.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Game.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Geometry.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Graphic.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\IA3D.H
+# End Source File
+# Begin Source File
+
+SOURCE=.\ImageBox.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ImgView.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Joystick.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Keyboard.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Layout.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Light.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\FoundationEx\List.inl
+# End Source File
+# Begin Source File
+
+SOURCE=.\ListBox.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Locale.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MachineInfo.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MCIWave.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\FoundationEx\MemDebug.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Menu.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MotionController.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Mouse.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MouseController.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\MultiController.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\Parser\Parser.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Particles.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Pcx.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Physical.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Polygon.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Projector.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Random.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\Parser\Reader.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Res.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Resource.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\RichTextBox.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Scene.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Screen.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\ScrollWindow.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Sha1.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Shadow.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Skin.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Slider.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Solid.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Sound.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SoundCard.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\SoundD3D.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Sprite.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\Parser\Term.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\TexCubeDX9.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\TexDX9.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\FoundationEx\Text.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\FoundationEx\ThreadSync.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\TimeSnap.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\Parser\Token.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Types.h
+# End Source File
+# Begin Source File
+
+SOURCE="..\..\Program Files\Microsoft Visual Studio\VC98\Include\SYS\TYPES.H"
+# End Source File
+# Begin Source File
+
+SOURCE=.\Universe.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Video.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\VideoDX9.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\VideoDX9Enum.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\VideoDX9VertexBuffer.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\VideoFactory.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\VideoSettings.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\View.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Water.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Wave.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\WebBrowser.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Window.h
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/nGenEx/nGenEx.dsw b/nGenEx/nGenEx.dsw
new file mode 100644
index 0000000..e0f1773
--- /dev/null
+++ b/nGenEx/nGenEx.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "nGenEx"=.\nGenEx.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/nGenEx/nGenEx.vcxproj b/nGenEx/nGenEx.vcxproj
new file mode 100644
index 0000000..7f5f315
--- /dev/null
+++ b/nGenEx/nGenEx.vcxproj
@@ -0,0 +1,310 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Template|Win32">
+ <Configuration>Template</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <SccProjectName />
+ <SccLocalPath />
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Template|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>StaticLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Template|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.Cpp.UpgradeFromVC60.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.Cpp.UpgradeFromVC60.props" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <OutDir>.\Debug\</OutDir>
+ <IntDir>.\Debug\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <OutDir>.\Release\</OutDir>
+ <IntDir>.\Release\</IntDir>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <InlineFunctionExpansion>Default</InlineFunctionExpansion>
+ <FunctionLevelLinking>false</FunctionLevelLinking>
+ <Optimization>Disabled</Optimization>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <WarningLevel>Level3</WarningLevel>
+ <MinimalRebuild>true</MinimalRebuild>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <AdditionalIncludeDirectories>..\parser;..\FoundationEx;..\zlib;..\LibPng;..\Opcode\OpcodeLib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AssemblerListingLocation>.\Debug\</AssemblerListingLocation>
+ <BrowseInformation>true</BrowseInformation>
+ <PrecompiledHeaderOutputFile>.\Debug\nGenEx.pch</PrecompiledHeaderOutputFile>
+ <ObjectFileName>.\Debug\</ObjectFileName>
+ <ProgramDataBaseFileName>.\Debug\</ProgramDataBaseFileName>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ </ClCompile>
+ <ResourceCompile>
+ <Culture>0x0409</Culture>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>.\Debug\nGenEx.bsc</OutputFile>
+ </Bscmake>
+ <Lib>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>.\Debug\nGenEx.lib</OutputFile>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <StringPooling>true</StringPooling>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <Optimization>MaxSpeed</Optimization>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <WarningLevel>Level3</WarningLevel>
+ <AdditionalIncludeDirectories>..\parser;..\FoundationEx;..\zlib;..\LibPng;..\Opcode\OpcodeLib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AssemblerListingLocation>.\Release\</AssemblerListingLocation>
+ <BrowseInformation>true</BrowseInformation>
+ <PrecompiledHeaderOutputFile>.\Release\nGenEx.pch</PrecompiledHeaderOutputFile>
+ <ObjectFileName>.\Release\</ObjectFileName>
+ <ProgramDataBaseFileName>.\Release\</ProgramDataBaseFileName>
+ </ClCompile>
+ <ResourceCompile>
+ <Culture>0x0409</Culture>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>.\Release\nGenEx.bsc</OutputFile>
+ </Bscmake>
+ <Lib>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <OutputFile>.\Release\nGenEx.lib</OutputFile>
+ </Lib>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="ActiveWindow.cpp" />
+ <ClCompile Include="Archive.cpp" />
+ <ClCompile Include="..\FoundationEx\ArrayList.cpp" />
+ <ClCompile Include="AviFile.cpp" />
+ <ClCompile Include="Bitmap.cpp" />
+ <ClCompile Include="Bmp.cpp" />
+ <ClCompile Include="Bolt.cpp" />
+ <ClCompile Include="Button.cpp" />
+ <ClCompile Include="Camera.cpp" />
+ <ClCompile Include="CameraView.cpp" />
+ <ClCompile Include="Color.cpp" />
+ <ClCompile Include="ComboBox.cpp" />
+ <ClCompile Include="ComboList.cpp" />
+ <ClCompile Include="ContentBundle.cpp" />
+ <ClCompile Include="D3DXImage.cpp" />
+ <ClCompile Include="DataLoader.cpp" />
+ <ClCompile Include="EditBox.cpp" />
+ <ClCompile Include="Encrypt.cpp" />
+ <ClCompile Include="EventDispatch.cpp" />
+ <ClCompile Include="FadeView.cpp" />
+ <ClCompile Include="Fix.cpp" />
+ <ClCompile Include="Font.cpp" />
+ <ClCompile Include="FontMgr.cpp" />
+ <ClCompile Include="FormatUtil.cpp" />
+ <ClCompile Include="FormDef.cpp" />
+ <ClCompile Include="FormWindow.cpp" />
+ <ClCompile Include="Game.cpp" />
+ <ClCompile Include="Geometry.cpp" />
+ <ClCompile Include="Graphic.cpp" />
+ <ClCompile Include="ImageBox.cpp" />
+ <ClCompile Include="ImgView.cpp" />
+ <ClCompile Include="Joystick.cpp" />
+ <ClCompile Include="Keyboard.cpp" />
+ <ClCompile Include="Layout.cpp" />
+ <ClCompile Include="Light.cpp" />
+ <ClCompile Include="ListBox.cpp" />
+ <ClCompile Include="Locale.cpp" />
+ <ClCompile Include="MachineInfo.cpp" />
+ <ClCompile Include="MCIWave.cpp" />
+ <ClCompile Include="..\FoundationEx\MemDebug.cpp" />
+ <ClCompile Include="Menu.cpp" />
+ <ClCompile Include="Mouse.cpp" />
+ <ClCompile Include="MouseController.cpp" />
+ <ClCompile Include="MultiController.cpp" />
+ <ClCompile Include="..\Parser\Parser.cpp" />
+ <ClCompile Include="ParseUtil.cpp" />
+ <ClCompile Include="Particles.cpp" />
+ <ClCompile Include="PCX.CPP" />
+ <ClCompile Include="Physical.cpp" />
+ <ClCompile Include="PngImage.cpp" />
+ <ClCompile Include="Polygon.cpp" />
+ <ClCompile Include="Projector.cpp" />
+ <ClCompile Include="Random.cpp" />
+ <ClCompile Include="..\Parser\Reader.cpp" />
+ <ClCompile Include="Res.cpp" />
+ <ClCompile Include="RichTextBox.cpp" />
+ <ClCompile Include="Scene.cpp" />
+ <ClCompile Include="Screen.cpp" />
+ <ClCompile Include="ScrollWindow.cpp" />
+ <ClCompile Include="Sha1.cpp" />
+ <ClCompile Include="Shadow.cpp" />
+ <ClCompile Include="Skin.cpp" />
+ <ClCompile Include="Slider.cpp" />
+ <ClCompile Include="Solid.cpp" />
+ <ClCompile Include="Sound.cpp" />
+ <ClCompile Include="SoundCard.cpp" />
+ <ClCompile Include="SoundD3D.cpp" />
+ <ClCompile Include="Sprite.cpp" />
+ <ClCompile Include="..\Parser\Term.cpp" />
+ <ClCompile Include="TexCubeDX9.cpp" />
+ <ClCompile Include="TexDX9.cpp" />
+ <ClCompile Include="..\FoundationEx\Text.cpp" />
+ <ClCompile Include="..\Parser\Token.cpp" />
+ <ClCompile Include="Video.cpp" />
+ <ClCompile Include="VideoDX9.cpp" />
+ <ClCompile Include="VideoDX9Enum.cpp" />
+ <ClCompile Include="VideoDX9VertexBuffer.cpp" />
+ <ClCompile Include="VideoFactory.cpp" />
+ <ClCompile Include="VideoSettings.cpp" />
+ <ClCompile Include="Water.cpp" />
+ <ClCompile Include="WebBrowser.cpp" />
+ <ClCompile Include="Window.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="ActiveWindow.h" />
+ <ClInclude Include="Archive.h" />
+ <ClInclude Include="..\FoundationEx\ArrayList.h" />
+ <ClInclude Include="AviFile.h" />
+ <ClInclude Include="Bitmap.h" />
+ <ClInclude Include="Bmp.h" />
+ <ClInclude Include="Bolt.h" />
+ <ClInclude Include="Button.h" />
+ <ClInclude Include="Camera.h" />
+ <ClInclude Include="CameraView.h" />
+ <ClInclude Include="Color.h" />
+ <ClInclude Include="ComboBox.h" />
+ <ClInclude Include="ComboList.h" />
+ <ClInclude Include="ContentBundle.h" />
+ <ClInclude Include="D3DXImage.h" />
+ <ClInclude Include="DataLoader.h" />
+ <ClInclude Include="..\FoundationEx\Dictionary.h" />
+ <ClInclude Include="Director.h" />
+ <ClInclude Include="EditBox.h" />
+ <ClInclude Include="Encrypt.h" />
+ <ClInclude Include="EventDispatch.h" />
+ <ClInclude Include="EventTarget.h" />
+ <ClInclude Include="EventTgt.h" />
+ <ClInclude Include="Fix.h" />
+ <ClInclude Include="Font.h" />
+ <ClInclude Include="FontMgr.h" />
+ <ClInclude Include="FormatUtil.h" />
+ <ClInclude Include="FormDef.h" />
+ <ClInclude Include="FormWindow.h" />
+ <ClInclude Include="Game.h" />
+ <ClInclude Include="Geometry.h" />
+ <ClInclude Include="Graphic.h" />
+ <ClInclude Include="IA3D.H" />
+ <ClInclude Include="ImageBox.h" />
+ <ClInclude Include="ImgView.h" />
+ <ClInclude Include="Joystick.h" />
+ <ClInclude Include="Keyboard.h" />
+ <ClInclude Include="Layout.h" />
+ <ClInclude Include="Light.h" />
+ <ClInclude Include="ListBox.h" />
+ <ClInclude Include="Locale.h" />
+ <ClInclude Include="MachineInfo.h" />
+ <ClInclude Include="MCIWave.h" />
+ <ClInclude Include="..\FoundationEx\MemDebug.h" />
+ <ClInclude Include="Menu.h" />
+ <ClInclude Include="MotionController.h" />
+ <ClInclude Include="Mouse.h" />
+ <ClInclude Include="MouseController.h" />
+ <ClInclude Include="MultiController.h" />
+ <ClInclude Include="..\Parser\Parser.h" />
+ <ClInclude Include="Particles.h" />
+ <ClInclude Include="Pcx.h" />
+ <ClInclude Include="Physical.h" />
+ <ClInclude Include="Polygon.h" />
+ <ClInclude Include="Projector.h" />
+ <ClInclude Include="Random.h" />
+ <ClInclude Include="..\Parser\Reader.h" />
+ <ClInclude Include="Res.h" />
+ <ClInclude Include="Resource.h" />
+ <ClInclude Include="RichTextBox.h" />
+ <ClInclude Include="Scene.h" />
+ <ClInclude Include="Screen.h" />
+ <ClInclude Include="ScrollWindow.h" />
+ <ClInclude Include="Sha1.h" />
+ <ClInclude Include="Shadow.h" />
+ <ClInclude Include="Skin.h" />
+ <ClInclude Include="Slider.h" />
+ <ClInclude Include="Solid.h" />
+ <ClInclude Include="Sound.h" />
+ <ClInclude Include="SoundCard.h" />
+ <ClInclude Include="SoundD3D.h" />
+ <ClInclude Include="Sprite.h" />
+ <ClInclude Include="..\Parser\Term.h" />
+ <ClInclude Include="TexCubeDX9.h" />
+ <ClInclude Include="TexDX9.h" />
+ <ClInclude Include="..\FoundationEx\Text.h" />
+ <ClInclude Include="..\FoundationEx\ThreadSync.h" />
+ <ClInclude Include="TimeSnap.h" />
+ <ClInclude Include="..\Parser\Token.h" />
+ <ClInclude Include="Types.h" />
+ <ClInclude Include="..\..\Program Files\Microsoft Visual Studio\VC98\Include\SYS\TYPES.H" />
+ <ClInclude Include="Universe.h" />
+ <ClInclude Include="Video.h" />
+ <ClInclude Include="VideoDX9.h" />
+ <ClInclude Include="VideoDX9Enum.h" />
+ <ClInclude Include="VideoDX9VertexBuffer.h" />
+ <ClInclude Include="VideoFactory.h" />
+ <ClInclude Include="VideoSettings.h" />
+ <ClInclude Include="View.h" />
+ <ClInclude Include="Water.h" />
+ <ClInclude Include="Wave.h" />
+ <ClInclude Include="WebBrowser.h" />
+ <ClInclude Include="Window.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="..\FoundationEx\Dictionary.inl">
+ <FileType>Document</FileType>
+ </CustomBuild>
+ <CustomBuild Include="..\FoundationEx\List.inl">
+ <FileType>Document</FileType>
+ </CustomBuild>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/nGenEx/nGenEx.vcxproj.filters b/nGenEx/nGenEx.vcxproj.filters
new file mode 100644
index 0000000..b31e5e7
--- /dev/null
+++ b/nGenEx/nGenEx.vcxproj.filters
@@ -0,0 +1,550 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{7036ad9d-3475-4be6-8676-a944961e0ea0}</UniqueIdentifier>
+ <Extensions>cpp;c;cxx;rc;def;r;odl;idl;hpj;bat</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{b22ff676-bf70-4e5a-bab4-348f2a5ed43b}</UniqueIdentifier>
+ <Extensions>h;hpp;hxx;hm;inl</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="ActiveWindow.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Archive.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\FoundationEx\ArrayList.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="AviFile.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Bitmap.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Bmp.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Bolt.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Button.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Camera.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="CameraView.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Color.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ComboBox.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ComboList.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ContentBundle.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="D3DXImage.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="DataLoader.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="EditBox.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Encrypt.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="EventDispatch.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="FadeView.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Fix.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Font.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="FontMgr.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="FormatUtil.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="FormDef.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="FormWindow.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Game.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Geometry.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Graphic.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ImageBox.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ImgView.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Joystick.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Keyboard.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Layout.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Light.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ListBox.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Locale.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="MachineInfo.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="MCIWave.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\FoundationEx\MemDebug.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Menu.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Mouse.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="MouseController.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="MultiController.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\Parser\Parser.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ParseUtil.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Particles.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="PCX.CPP">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Physical.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="PngImage.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Polygon.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Projector.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Random.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\Parser\Reader.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Res.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="RichTextBox.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Scene.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Screen.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ScrollWindow.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Sha1.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Shadow.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Skin.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Slider.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Solid.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Sound.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="SoundCard.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="SoundD3D.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Sprite.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\Parser\Term.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="TexCubeDX9.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="TexDX9.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\FoundationEx\Text.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\Parser\Token.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Video.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="VideoDX9.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="VideoDX9Enum.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="VideoDX9VertexBuffer.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="VideoFactory.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="VideoSettings.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Water.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="WebBrowser.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="Window.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="ActiveWindow.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Archive.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\FoundationEx\ArrayList.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="AviFile.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Bitmap.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Bmp.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Bolt.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Button.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Camera.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="CameraView.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Color.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ComboBox.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ComboList.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ContentBundle.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="D3DXImage.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="DataLoader.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\FoundationEx\Dictionary.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Director.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="EditBox.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Encrypt.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="EventDispatch.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="EventTarget.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="EventTgt.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Fix.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Font.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="FontMgr.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="FormatUtil.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="FormDef.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="FormWindow.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Game.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Geometry.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Graphic.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="IA3D.H">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ImageBox.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ImgView.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Joystick.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Keyboard.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Layout.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Light.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ListBox.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Locale.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="MachineInfo.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="MCIWave.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\FoundationEx\MemDebug.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Menu.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="MotionController.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Mouse.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="MouseController.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="MultiController.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\Parser\Parser.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Particles.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Pcx.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Physical.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Polygon.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Projector.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Random.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\Parser\Reader.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Res.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Resource.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="RichTextBox.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Scene.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Screen.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ScrollWindow.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Sha1.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Shadow.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Skin.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Slider.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Solid.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Sound.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="SoundCard.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="SoundD3D.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Sprite.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\Parser\Term.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="TexCubeDX9.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="TexDX9.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\FoundationEx\Text.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\FoundationEx\ThreadSync.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="TimeSnap.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\Parser\Token.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Types.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\Program Files\Microsoft Visual Studio\VC98\Include\SYS\TYPES.H">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Universe.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Video.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="VideoDX9.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="VideoDX9Enum.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="VideoDX9VertexBuffer.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="VideoFactory.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="VideoSettings.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="View.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Water.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Wave.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="WebBrowser.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Window.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="..\FoundationEx\Dictionary.inl">
+ <Filter>Header Files</Filter>
+ </CustomBuild>
+ <CustomBuild Include="..\FoundationEx\List.inl">
+ <Filter>Header Files</Filter>
+ </CustomBuild>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/nGenEx/nGenEx.vcxproj.user b/nGenEx/nGenEx.vcxproj.user
new file mode 100644
index 0000000..695b5c7
--- /dev/null
+++ b/nGenEx/nGenEx.vcxproj.user
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+</Project> \ No newline at end of file