#include "std.h" #include "bbblitz3d.h" #include "bbgraphics.h" #include "../blitz3d/blitz3d.h" #include "../blitz3d/world.h" #include "../blitz3d/texture.h" #include "../blitz3d/brush.h" #include "../blitz3d/camera.h" #include "../blitz3d/sprite.h" #include "../blitz3d/meshmodel.h" //#include "../blitz3d/loader_x.h" #include "../blitz3d/loader_3ds.h" #include "../blitz3d/loader_b3d.h" #include "../blitz3d/md2model.h" #include "../blitz3d/q3bspmodel.h" #include "../blitz3d/meshutil.h" #include "../blitz3d/pivot.h" #include "../blitz3d/planemodel.h" #include "../blitz3d/terrain.h" #include "../blitz3d/listener.h" #include "../blitz3d/cachedtexture.h" gxScene *gx_scene; extern gxFileSystem *gx_filesys; static int tri_count; static World *world; static set brush_set; static set texture_set; static set entity_set; static Listener *listener; static bool stats_mode; //converts 0...255 color to 0...1 static const float s_colorToFloat = 1.0f / 255.0f; //degrees to radians and back static const float s_degreesToRadians = 0.0174532925199432957692369076848861f; static const float s_radiansToDegrees = 1 / s_degreesToRadians; static Vector projected, tformed; static ObjCollision picked; extern float stats3d[10]; //static Loader_X loader_x; static Loader_3DS loader_3ds; static Loader_B3D loader_b3d; static map loader_mat_map; static inline void debug3d() { #ifdef DEBUG if (debug && !gx_scene) ThrowRuntimeException("3D Graphics mode not set"); #else return; #endif } static inline void debugTexture(Texture *t) { #ifdef DEBUG if (debug && !texture_set.count(t)) ThrowRuntimeException("Texture does not exist"); #else return; #endif } static inline void debugBrush(Brush *b) { #ifdef DEBUG if (debug && !brush_set.count(b)) ThrowRuntimeException("Brush does not exist"); #else return; #endif } static inline void debugEntity(Entity *e) { #ifdef DEBUG if (debug && !entity_set.count(e)) ThrowRuntimeException("Entity does not exist"); #else return; #endif } static inline void debugParent(Entity *e) { #ifdef DEBUG if (debug) { debug3d(); if (e && !entity_set.count(e)) ThrowRuntimeException("Parent entity does not exist"); } #else return; #endif } static inline void debugMesh(MeshModel *m) { #ifdef DEBUG if (debug) { debugEntity(m); if (!m->getMeshModel()) ThrowRuntimeException("Entity is not a mesh"); } #else return; #endif } static inline void debugObject(Object *o) { #ifdef DEBUG if (debug) { debugEntity(o); if (!o->getObject()) ThrowRuntimeException("Entity is not an object"); } #else return; #endif } static inline void debugColl(Object *o, int index) { #ifdef DEBUG if (debug) { debugObject(o); if (index<1 || index>o->getCollisions().size()) ThrowRuntimeException("Collision index out of range"); } #else return; #endif } static inline void debugCamera(Camera *c) { #ifdef DEBUG if (debug) { debugEntity(c); if (!c->getCamera()) ThrowRuntimeException("Entity is not a camera"); } #else return; #endif } static inline void debugLight(Light *l) { #ifdef DEBUG if (debug) { debugEntity(l); if (!l->getLight()) ThrowRuntimeException("Entity is not a light"); } #else return; #endif } static inline void debugModel(Model *m) { #ifdef DEBUG if (debug) { debugEntity(m); if (!m->getModel()) ThrowRuntimeException("Entity is not a model"); } #else return; #endif } static inline void debugSprite(Sprite *s) { #ifdef DEBUG if (debug) { debugModel(s); if (!s->getSprite()) ThrowRuntimeException("Entity is not a sprite"); } #else return; #endif } static inline void debugMD2(MD2Model *m) { #ifdef DEBUG if (debug) { debugModel(m); if (!m->getMD2Model()) ThrowRuntimeException("Entity is not an MD2 Model"); } #else return; #endif } static inline void debugBSP(Q3BSPModel *m) { #ifdef DEBUG if (debug) { debugModel(m); if (!m->getBSPModel()) ThrowRuntimeException("Entity is not a BSP Model"); } #else return; #endif } static inline void debugTerrain(Terrain *t) { #ifdef DEBUG if (debug) { debugModel(t); if (!t->getTerrain()) ThrowRuntimeException("Entity is not a terrain"); } #else return; #endif } static inline void debugSegs(int n) { #ifdef DEBUG if (debug) { debug3d(); if (n < 3 || n>50) ThrowRuntimeException("Illegal number of segments"); } #else return; #endif } static inline void debugVertex(Surface *s, int n) { #ifdef DEBUG if (debug) { debug3d(); if (n < 0 || n >= s->numVertices()) ThrowRuntimeException("Vertex index out of range"); } #else return; #endif } static inline void debugVertex(Surface *s, int n, int t) { #ifdef DEBUG if (debug) { debug3d(); if (n < 0 || n >= s->numVertices()) ThrowRuntimeException("Vertex index out of range"); if (t < 0 || t>1) ThrowRuntimeException("Texture coordinate set out of range"); } #else return; #endif } static Entity *loadEntity(string t, int hint) { t = tolower(t); int n = t.rfind("."); if (n == string::npos) return 0; string ext = t.substr(n + 1); MeshLoader *l; if (ext == "x") { //l = &Loader_X; } else if (ext == "3ds") { l = &loader_3ds; } else if (ext == "b3d") { l = &loader_b3d; } else { return 0; } const Transform &conv = loader_mat_map[ext]; CachedTexture::setPath(filenamepath(t)); Entity *e = l->load(t, conv, hint); CachedTexture::setPath(""); return e; } static void collapseMesh(MeshModel *mesh, Entity *e) { while (e->GetChildren()) { collapseMesh(mesh, e->GetChildren()); } if (Model *p = e->getModel()) { if (MeshModel *t = p->getMeshModel()) { t->transform(e->GetWorldTransform()); mesh->add(*t); } } delete e; } static void insert(Entity *e) { if (debug) entity_set.insert(e); e->SetVisible(true); e->SetEnabled(true); e->getObject()->reset(); for (Entity *p = e->GetChildren(); p; p = p->GetSuccessor()) { insert(p); } } static Entity *insertEntity(Entity *e, Entity *p) { e->SetParent(p); insert(e); return e; } static void erase(Entity *e) { for (Entity *p = e->GetChildren(); p; p = p->GetSuccessor()) { erase(p); } if (e->getListener()) listener = 0; if (debug) entity_set.erase(e); } static Entity *findChild(Entity *e, const string &t) { if (e->getName() == t) return e; for (Entity *p = e->GetChildren(); p; p = p->GetSuccessor()) { if (Entity *q = findChild(p, t)) return q; } return 0; } /////////////////////////// // GLOBAL WORLD COMMANDS // /////////////////////////// void bbLoaderMatrix(BBStr *ext, float xx, float xy, float xz, float yx, float yy, float yz, float zx, float zy, float zz) { loader_mat_map.erase(*ext); loader_mat_map[*ext] = Transform(Matrix(Vector(xx, xy, xz), Vector(yx, yy, yz), Vector(zx, zy, zz))); delete ext; } int bbHWTexUnits() { debug3d(); return gx_scene->hwTexUnits(); } int bbGfxDriverCaps3D() { debug3d(); return gx_scene->gfxDriverCaps3D(); } void bbHWMultiTex(int enable) { debug3d(); gx_scene->setHWMultiTex(!!enable); } void bbWBuffer(int enable) { debug3d(); gx_scene->setWBuffer(!!enable); } void bbDither(int enable) { debug3d(); gx_scene->setDither(!!enable); } void bbAntiAlias(int enable) { debug3d(); gx_scene->setAntialias(!!enable); } void bbWireFrame(int enable) { debug3d(); gx_scene->setWireframe(!!enable); } void bbAmbientLight(float r, float g, float b) { debug3d(); Vector t(r*s_colorToFloat, g*s_colorToFloat, b*s_colorToFloat); gx_scene->setAmbient(&(t.x)); } void bbClearCollisions() { debug3d(); world->clearCollisions(); } void bbCollisions(int src_type, int dest_type, int method, int response) { debug3d(); world->addCollision(src_type, dest_type, method, response); } static int update_ms; void bbUpdateWorld(float elapsed) { debug3d(); #ifndef BETA world->update(elapsed); return; #endif update_ms = gx_runtime->getMilliSecs(); world->update(elapsed); update_ms = gx_runtime->getMilliSecs() - update_ms; } void bbCaptureWorld() { debug3d(); world->capture(); } void bbRenderWorld(float tween) { debug3d(); #ifndef BETA tri_count = gx_scene->getTrianglesDrawn(); world->render(tween); tri_count = gx_scene->getTrianglesDrawn() - tri_count; return; #else int tris = gx_scene->getTrianglesDrawn(); int render_ms = gx_runtime->getMilliSecs(); world->render(tween); render_ms = gx_runtime->getMilliSecs() - render_ms; extern int bbKeyHit(int); extern void bbDelay(int); bbDelay(0); if (bbKeyHit(0x57)) { stats_mode = !stats_mode; } if (bbKeyHit(0x58)) { static int n; string t = "screenshot" + itoa(++n) + ".bmp"; bbSaveBuffer(bbBackBuffer(), new BBStr(t)); } if (!stats_mode) return; tris = gx_scene->getTrianglesDrawn() - tris; static int time; int frame_ms = gx_runtime->getMilliSecs() - time; time += frame_ms; int fps = frame_ms ? 1000 / frame_ms : 1000; int ups = update_ms ? 1000 / update_ms : 1000; int rps = render_ms ? 1000 / render_ms : 1000; string t_fps = "000" + itoa(fps); t_fps = t_fps.substr(t_fps.size() - 4); string t_ups = "000" + itoa(ups); t_ups = t_ups.substr(t_ups.size() - 4); string t_rps = "000" + itoa(rps); t_rps = t_rps.substr(t_rps.size() - 4); string t_tris = "00000" + itoa(tris); t_tris = t_tris.substr(t_tris.size() - 6); string t = "FPS:" + t_fps + " UPS:" + t_ups + " RPS:" + t_rps + " TRIS:" + t_tris; bbText(0, bbGraphicsHeight() - bbFontHeight(), new BBStr(t), 0, 0); #endif } int bbTrisRendered() { return tri_count; } float bbStats3D(int n) { return stats3d[n]; } ////////////////////// // TEXTURE COMMANDS // ////////////////////// //Note: modify canvas->backup() to NOT release backup image! // Texture * bbLoadTexture(BBStr *file, int flags) { debug3d(); Texture *t = new Texture(*file, flags); delete file; if (!t->getCanvas(0)) { delete t; return 0; } texture_set.insert(t); return t; } Texture * bbLoadAnimTexture(BBStr *file, int flags, int w, int h, int first, int cnt) { debug3d(); Texture *t = new Texture(*file, flags, w, h, first, cnt); delete file; if (!t->getCanvas(0)) { delete t; return 0; } texture_set.insert(t); return t; } Texture * bbCreateTexture(int w, int h, int flags, int frames) { if (debug) { debug3d(); if (frames <= 0) { ThrowRuntimeException("Illegal number of texture frames"); } } Texture *t = new Texture(w, h, flags, frames); texture_set.insert(t); return t; } void bbFreeTexture(Texture *t) { if (!t) return; debugTexture(t); if (texture_set.erase(t)) delete t; } void bbTextureBlend(Texture *t, int blend) { debugTexture(t); t->setBlend(blend); } void bbTextureCoords(Texture *t, int flags) { debugTexture(t); t->setFlags(flags); } void bbScaleTexture(Texture *t, float u_scale, float v_scale) { debugTexture(t); t->setScale(1 / u_scale, 1 / v_scale); } void bbRotateTexture(Texture *t, float angle) { debugTexture(t); t->setRotation(-angle*s_degreesToRadians); } void bbPositionTexture(Texture *t, float u_pos, float v_pos) { debugTexture(t); t->setPosition(-u_pos, -v_pos); } int bbTextureWidth(Texture *t) { debugTexture(t); return t->getCanvas(0)->getWidth(); } int bbTextureHeight(Texture *t) { debugTexture(t); return t->getCanvas(0)->getHeight(); } BBStr *bbTextureName(Texture *t) { debugTexture(t); CachedTexture *c = t->getCachedTexture(); return c ? new BBStr(c->getName().c_str()) : new BBStr(""); } void bbSetCubeFace(Texture *t, int face) { debugTexture(t); if (gxCanvas *c = t->getCanvas(0)) { c->setCubeFace(face); } } void bbSetCubeMode(Texture *t, int mode) { debugTexture(t); if (gxCanvas *c = t->getCanvas(0)) { c->setCubeMode(mode); } } gxCanvas *bbTextureBuffer(Texture *t, int frame) { //v1.04 debugTexture(t); if (gxCanvas *c = t->getCanvas(frame)) { if (c->getDepth()) return c; } return 0; /* //v1.03 crashes if t->getCanvas returns null! debugTexture(t); gxCanvas *c=t->getCanvas( frame ); if( c->getDepth() ) return c; return 0; */ } void bbClearTextureFilters() { debug3d(); Texture::clearFilters(); } void bbTextureFilter(BBStr *t, int flags) { debug3d(); Texture::addFilter(*t, flags); delete t; } //////////////////// // BRUSH COMMANDS // //////////////////// Brush * bbCreateBrush(float r, float g, float b) { debug3d(); Brush *br = new Brush(); br->setColor(Vector(r*s_colorToFloat, g*s_colorToFloat, b*s_colorToFloat)); brush_set.insert(br); return br; } Brush * bbLoadBrush(BBStr *file, int flags, float u_scale, float v_scale) { debug3d(); Texture t(*file, flags); delete file; if (!t.getCanvas(0)) return 0; if (u_scale != 1 || v_scale != 1) t.setScale(1 / u_scale, 1 / v_scale); Brush *br = bbCreateBrush(255, 255, 255); br->setTexture(0, t, 0); delete file; return br; } void bbFreeBrush(Brush *b) { if (!b) return; debugBrush(b); if (brush_set.erase(b)) delete b; } void bbBrushColor(Brush *br, float r, float g, float b) { debugBrush(br); br->setColor(Vector(r*s_colorToFloat, g*s_colorToFloat, b*s_colorToFloat)); } void bbBrushAlpha(Brush *b, float alpha) { debugBrush(b); b->setAlpha(alpha); } void bbBrushShininess(Brush *b, float n) { debugBrush(b); b->setShininess(n); } void bbBrushTexture(Brush *b, Texture *t, int frame, int index) { debugBrush(b); debugTexture(t); b->setTexture(index, *t, frame); } Texture *bbGetBrushTexture(Brush *b, int index) { debugBrush(b); Texture *tex = new Texture(b->getTexture(index)); texture_set.insert(tex); return tex; } void bbBrushBlend(Brush *b, int blend) { debugBrush(b); b->setBlend(blend); } void bbBrushFX(Brush *b, int fx) { debugBrush(b); b->setFX(fx); } /////////////////// // MESH COMMANDS // /////////////////// Entity * bbCreateMesh(Entity *p) { debugParent(p); MeshModel *m = new MeshModel(); return insertEntity(m, p); } Entity * bbLoadMesh(BBStr *f, Entity *p) { debugParent(p); Entity *e = loadEntity(f->c_str(), MeshLoader::HINT_COLLAPSE); delete f; if (!e) return 0; MeshModel *m = new MeshModel(); collapseMesh(m, e); return insertEntity(m, p); } Entity * bbLoadAnimMesh(BBStr *f, Entity *p) { debugParent(p); Entity *e = loadEntity(f->c_str(), 0); delete f; if (!e) return 0; if (Animator *anim = e->getObject()->getAnimator()) { anim->animate(1, 0, 0, 0); } return insertEntity(e, p); } Entity * bbCreateCube(Entity *p) { debugParent(p); Entity *e = MeshUtil::createCube(Brush()); return insertEntity(e, p); } Entity * bbCreateSphere(int segs, Entity *p) { if (debug) { debugParent(p); if (segs < 2 || segs>100) ThrowRuntimeException("Illegal number of segments"); } Entity *e = MeshUtil::createSphere(Brush(), segs); return insertEntity(e, p); } Entity * bbCreateCylinder(int segs, int solid, Entity *p) { if (debug) { debugParent(p); if (segs < 3 || segs>100) ThrowRuntimeException("Illegal number of segments"); } Entity *e = MeshUtil::createCylinder(Brush(), segs, !!solid); return insertEntity(e, p); } Entity * bbCreateCone(int segs, int solid, Entity *p) { if (debug) { debugParent(p); if (segs < 3 || segs>100) ThrowRuntimeException("Illegal number of segments"); } Entity *e = MeshUtil::createCone(Brush(), segs, !!solid); return insertEntity(e, p); } Entity * bbCopyMesh(MeshModel *m, Entity *p) { debugMesh(m); debugParent(p); MeshModel *t = new MeshModel(); t->add(*m); return insertEntity(t, p); } void bbScaleMesh(MeshModel *m, float x, float y, float z) { debugMesh(m); m->transform(scaleMatrix(x, y, z)); } void bbRotateMesh(MeshModel *m, float x, float y, float z) { debugMesh(m); m->transform(rotationMatrix(x*s_degreesToRadians, y*s_degreesToRadians, z*s_degreesToRadians)); } void bbPositionMesh(MeshModel *m, float x, float y, float z) { debugMesh(m); m->transform(Vector(x, y, z)); } void bbFitMesh(MeshModel *m, float x, float y, float z, float w, float h, float d, int uniform) { debugMesh(m); Box box(Vector(x, y, z)); box.update(Vector(x + w, y + h, z + d)); const Box &curr_box = m->getBox(); float x_scale = box.width() / curr_box.width(); float y_scale = box.height() / curr_box.height(); float z_scale = box.depth() / curr_box.depth(); Transform t; if (uniform) { if (x_scale < y_scale && x_scale < z_scale) { y_scale = z_scale = x_scale; } else if (y_scale < x_scale && y_scale < z_scale) { x_scale = z_scale = y_scale; } else { x_scale = y_scale = z_scale; } } t.m.i.x = x_scale; t.m.j.y = y_scale; t.m.k.z = z_scale; t.v = box.centre() - t.m * curr_box.centre(); m->transform(t); } void bbFlipMesh(MeshModel *m) { debugMesh(m); m->flipTriangles(); } void bbPaintMesh(MeshModel *m, Brush *b) { if (debug) { debugMesh(m); debugBrush(b); } m->paint(*b); } void bbAddMesh(MeshModel *src, MeshModel *dest) { if (debug) { debugMesh(src); debugMesh(dest); if (src == dest) ThrowRuntimeException("A mesh cannot be added to itself"); } dest->add(*src); } void bbUpdateNormals(MeshModel *m) { debugMesh(m); m->updateNormals(); } void bbLightMesh(MeshModel *m, float r, float g, float b, float range, float x, float y, float z) { debugMesh(m); MeshUtil::lightMesh(m, Vector(x, y, z), Vector(r*s_colorToFloat, g*s_colorToFloat, b*s_colorToFloat), range); } float bbMeshWidth(MeshModel *m) { debugMesh(m); return m->getBox().width(); } float bbMeshHeight(MeshModel *m) { debugMesh(m); return m->getBox().height(); } float bbMeshDepth(MeshModel *m) { debugMesh(m); return m->getBox().depth(); } int bbMeshesIntersect(MeshModel *a, MeshModel *b) { if (debug) { debugMesh(a); debugMesh(b); } return a->intersects(*b); } int bbCountSurfaces(MeshModel *m) { debugMesh(m); return m->getSurfaces().size(); } Surface * bbGetSurface(MeshModel *m, int index) { #ifdef DEBUG if (debug) { debugMesh(m); if ((size_t)index<1 || index>m->getSurfaces().size()) { ThrowRuntimeException("Surface Index out of range"); } } #endif return m->getSurfaces()[index - 1]; } void bbMeshCullBox(MeshModel *m, float x, float y, float z, float width, float height, float depth) { debugMesh(m); m->setCullBox(Box(Vector(x, y, z), Vector(x + width, y + height, z + depth))); } ////////////////////// // SURFACE COMMANDS // ////////////////////// Surface * bbFindSurface(MeshModel *m, Brush *b) { if (debug) { debugMesh(m); debugBrush(b); } return m->findSurface(*b); } Surface * bbCreateSurface(MeshModel *m, Brush *b) { if (debug) { debugMesh(m); if (b) debugBrush(b); } Surface *s = b ? m->createSurface(*b) : m->createSurface(Brush()); return s; } Brush *bbGetSurfaceBrush(Surface *s) { Brush *br = new Brush(s->getBrush()); brush_set.insert(br); return br; } Brush *bbGetEntityBrush(Model *m) { debugModel(m); Brush *br = new Brush(m->getBrush()); brush_set.insert(br); return br; } void bbClearSurface(Surface *s, int verts, int tris) { s->clear(!!verts, !!tris); } void bbPaintSurface(Surface *s, Brush *b) { debugBrush(b); s->setBrush(*b); } int bbAddVertex(Surface *s, float x, float y, float z, float tu, float tv, float tw) { Surface::Vertex v; v.coords = Vector(x, y, z); v.color = 0xffffffff; v.tex_coords[0][0] = v.tex_coords[1][0] = tu; v.tex_coords[0][1] = v.tex_coords[1][1] = tv; s->addVertex(v); return s->numVertices() - 1; } int bbAddTriangle(Surface *s, int v0, int v1, int v2) { Surface::Triangle t; t.verts[0] = v0; t.verts[1] = v1; t.verts[2] = v2; s->addTriangle(t); return s->numTriangles() - 1; } void bbVertexCoords(Surface *s, int n, float x, float y, float z) { s->setCoords(n, Vector(x, y, z)); } void bbVertexNormal(Surface *s, int n, float x, float y, float z) { s->setNormal(n, Vector(x, y, z)); } void bbVertexColor(Surface *s, int n, float r, float g, float b, float a) { if (r < 0)r = 0; else if (r > 255)r = 255; if (g < 0)g = 0; else if (g > 255)g = 255; if (b < 0)b = 0; else if (b > 255)b = 255; a *= 255; if (a < 0)a = 0; else if (a > 255)a = 255; s->setColor(n, (int(a) << 24) | (int(r) << 16) | (int(g) << 8) | int(b)); } void bbVertexTexCoords(Surface *s, int n, float u, float v, float w, int set) { s->setTexCoords(n, Vector(u, v, w), set); } int bbCountVertices(Surface *s) { return s->numVertices(); } int bbCountTriangles(Surface *s) { return s->numTriangles(); } float bbVertexX(Surface *s, int n) { debugVertex(s, n); return s->getVertex(n).coords.x; } float bbVertexY(Surface *s, int n) { debugVertex(s, n); return s->getVertex(n).coords.y; } float bbVertexZ(Surface *s, int n) { debugVertex(s, n); return s->getVertex(n).coords.z; } float bbVertexNX(Surface *s, int n) { debugVertex(s, n); return s->getVertex(n).normal.x; } float bbVertexNY(Surface *s, int n) { debugVertex(s, n); return s->getVertex(n).normal.y; } float bbVertexNZ(Surface *s, int n) { debugVertex(s, n); return s->getVertex(n).normal.z; } float bbVertexRed(Surface *s, int n) { debugVertex(s, n); return (float)((s->getVertex(n).color & 0xff0000) >> 16); } float bbVertexGreen(Surface *s, int n) { debugVertex(s, n); return (float)((s->getVertex(n).color & 0xff00) >> 8); } float bbVertexBlue(Surface *s, int n) { debugVertex(s, n); return (float)(s->getVertex(n).color & 0xff); } float bbVertexAlpha(Surface *s, int n) { debugVertex(s, n); return (float)(((s->getVertex(n).color & 0xff000000) >> 24) / 255.0f); } float bbVertexU(Surface *s, int n, int t) { debugVertex(s, n, t); return s->getVertex(n).tex_coords[t][0]; } float bbVertexV(Surface *s, int n, int t) { debugVertex(s, n, t); return s->getVertex(n).tex_coords[t][1]; } float bbVertexW(Surface *s, int n, int t) { debugVertex(s, n, t); return 1; } int bbTriangleVertex(Surface *s, int n, int v) { return s->getTriangle(n).verts[v]; } ///////////////////// // CAMERA COMMANDS // ///////////////////// Entity * bbCreateCamera(Entity *p) { debugParent(p); int x, y, w, h; gx_canvas->getViewport(&x, &y, &w, &h); Camera *c = new Camera(); c->setViewport(x, y, w, h); return insertEntity(c, p); } void bbCameraZoom(Camera *c, float zoom) { debugCamera(c); c->setZoom(zoom); } void bbCameraRange(Camera *c, float nr, float fr) { debugCamera(c); c->setRange(nr, fr); } void bbCameraClsColor(Camera *c, float r, float g, float b) { debugCamera(c); c->setClsColor(Vector(r*s_colorToFloat, g*s_colorToFloat, b*s_colorToFloat)); } void bbCameraClsMode(Camera *c, int cls_color, int cls_zbuffer) { debugCamera(c); c->setClsMode(cls_color ? true : false, cls_zbuffer ? true : false); } void bbCameraProjMode(Camera *c, int mode) { debugCamera(c); c->setProjMode(mode); } void bbCameraViewport(Camera *c, int x, int y, int w, int h) { debugCamera(c); c->setViewport(x, y, w, h); } void bbCameraFogRange(Camera *c, float nr, float fr) { debugCamera(c); c->setFogRange(nr, fr); } void bbCameraFogColor(Camera *c, float r, float g, float b) { debugCamera(c); c->setFogColor(Vector(r*s_colorToFloat, g*s_colorToFloat, b*s_colorToFloat)); } void bbCameraFogMode(Camera *c, int mode) { debugCamera(c); c->setFogMode(mode); } int bbCameraProject(Camera *c, float x, float y, float z) { debugCamera(c); Vector v = -c->GetWorldTransform()*Vector(x, y, z); const Frustum &f = c->getFrustum(); if (c->getProjMode() == Camera::PROJ_ORTHO) { int vp_x, vp_y, vp_w, vp_h; c->getViewport(&vp_x, &vp_y, &vp_w, &vp_h); float nr = c->getFrustumNear(); float fr = c->getFrustumFar(); float nr_w = c->getFrustumWidth(); float nr_h = c->getFrustumHeight(); projected = Vector((v.x / nr_w + .5f)*vp_w, (.5f - v.y / nr_h)*vp_h, nr); return 1; } if (v.z > 0) { float fr = +f.getPlane(Frustum::PLANE_FAR).d; if (v.z <= fr) { int vp_x, vp_y, vp_w, vp_h; c->getViewport(&vp_x, &vp_y, &vp_w, &vp_h); float nr = c->getFrustumNear(); float fr = c->getFrustumFar(); float nr_w = c->getFrustumWidth(); float nr_h = c->getFrustumHeight(); projected = Vector( (v.x*nr / v.z / nr_w + .5f)*vp_w, (.5f - v.y*nr / v.z / nr_h)*vp_h, nr); return 1; } } projected = Vector(); return 0; } float bbProjectedX() { return projected.x; } float bbProjectedY() { return projected.y; } float bbProjectedZ() { return projected.z; } static Object *doPick(const Line &l, float radius) { picked.collision.time = 1; return world->traceRay(l, radius, &picked); } Entity * bbCameraPick(Camera *c, float x, float y) { debugCamera(c); int vp_x, vp_y, vp_w, vp_h; c->getViewport(&vp_x, &vp_y, &vp_w, &vp_h); float nr = c->getFrustumNear(); float fr = c->getFrustumFar(); float nr_w = c->getFrustumWidth(); float nr_h = c->getFrustumHeight(); x = ((x / vp_w) - .5f)*nr_w; y = (.5f - (y / vp_h))*nr_h; Line l; if (c->getProjMode() == Camera::PROJ_ORTHO) { l = c->GetWorldTransform() * Line(Vector(x, y, 0), Vector(0, 0, fr)); //x,y,fr) ); } else { x /= nr; y /= nr; l = c->GetWorldTransform() * Line(Vector(), Vector(x*fr, y*fr, fr)); } return doPick(l, 0); } Entity * bbLinePick(float x, float y, float z, float dx, float dy, float dz, float radius) { debug3d(); Line l(Vector(x, y, z), Vector(dx, dy, dz)); return doPick(l, radius); } Entity * bbEntityPick(Object *src, float range) { debugEntity(src); Line l(src->GetWorldPosition(), src->GetWorldTransform().m.k * range); return doPick(l, 0); } int bbEntityVisible(Object *src, Object *dest) { if (debug) { debugObject(src); debugObject(dest); } return world->CheckLineOfSight(src, dest) ? 1 : 0; } int bbEntityInView(Entity *e, Camera *c) { if (debug) { debugEntity(e); debugCamera(c); } if (Model *p = e->getModel()) { if (MeshModel *m = p->getMeshModel()) { const Box &b = m->getBox(); Transform t = -c->GetWorldTransform() * e->GetWorldTransform(); Vector p[] = { t*b.corner(0),t*b.corner(1),t*b.corner(2),t*b.corner(3), t*b.corner(4),t*b.corner(5),t*b.corner(6),t*b.corner(7) }; return c->getFrustum().cull(p, 8); } } Vector p[] = { -c->GetWorldTransform() * e->GetWorldPosition() }; return c->getFrustum().cull(p, 1); } float bbPickedX() { return picked.coords.x; } float bbPickedY() { return picked.coords.y; } float bbPickedZ() { return picked.coords.z; } float bbPickedNX() { return picked.collision.normal.x; } float bbPickedNY() { return picked.collision.normal.y; } float bbPickedNZ() { return picked.collision.normal.z; } float bbPickedTime() { return picked.collision.time; } Object * bbPickedEntity() { return picked.with; } void * bbPickedSurface() { return picked.collision.surface; } int bbPickedTriangle() { return picked.collision.index; } //////////////////// // LIGHT COMMANDS // //////////////////// Entity * bbCreateLight(int type, Entity *p) { debugParent(p); Light *t = new Light(type); return insertEntity(t, p); } void bbLightColor(Light *light, float r, float g, float b) { debugLight(light); light->setColor(Vector(r*s_colorToFloat, g*s_colorToFloat, b*s_colorToFloat)); } void bbLightRange(Light *light, float range) { debugLight(light); light->setRange(range); } void bbLightConeAngles(Light *light, float inner, float outer) { debugLight(light); inner *= s_degreesToRadians; outer *= s_degreesToRadians; if (inner < 0) inner = 0; else if (inner > PI) inner = PI; if (outer < inner) outer = inner; else if (outer > PI) outer = PI; light->setConeAngles(inner, outer); } //////////////////// // PIVOT COMMANDS // //////////////////// Entity * bbCreatePivot(Entity *p) { debugParent(p); Pivot *t = new Pivot(); return insertEntity(t, p); } ///////////////////// // SPRITE COMMANDS // ///////////////////// Entity * bbCreateSprite(Entity *p) { debugParent(p); Sprite *s = new Sprite(); s->setFX(gxScene::FX_FULLBRIGHT); return insertEntity(s, p); } Entity * bbLoadSprite(BBStr *file, int flags, Entity *p) { debugParent(p); Texture t(*file, flags); delete file; if (!t.getCanvas(0)) return 0; Sprite *s = new Sprite(); s->setTexture(0, t, 0); s->setFX(gxScene::FX_FULLBRIGHT); if (flags & gxCanvas::CANVAS_TEX_MASK) s->setBlend(gxScene::BLEND_REPLACE); else if (flags & gxCanvas::CANVAS_TEX_ALPHA) s->setBlend(gxScene::BLEND_ALPHA); else s->setBlend(gxScene::BLEND_ADD); return insertEntity(s, p); } void bbRotateSprite(Sprite *s, float angle) { debugSprite(s); s->setRotation(angle*s_degreesToRadians); } void bbScaleSprite(Sprite *s, float x, float y) { debugSprite(s); s->setScale(x, y); } void bbHandleSprite(Sprite *s, float x, float y) { debugSprite(s); s->setHandle(x, y); } void bbSpriteViewMode(Sprite *s, int mode) { debugSprite(s); s->setViewmode(mode); } ///////////////////// // MIRROR COMMANDS // ///////////////////// Entity * bbCreateMirror(Entity *p) { debugParent(p); Mirror *t = new Mirror(); return insertEntity(t, p); } //////////////////// // PLANE COMMANDS // //////////////////// Entity * bbCreatePlane(int segs, Entity *p) { if (debug) { debugParent(p); if (segs < 1 || segs>20) ThrowRuntimeException("Illegal number of segments"); } PlaneModel *t = new PlaneModel(segs); return insertEntity(t, p); } ////////////////// // MD2 COMMANDS // ////////////////// Entity * bbLoadMD2(BBStr *file, Entity *p) { debugParent(p); MD2Model *t = new MD2Model(*file); delete file; if (!t->getValid()) { delete t; return 0; } return insertEntity(t, p); } void bbAnimateMD2(MD2Model *m, int mode, float speed, int first, int last, float trans) { debugMD2(m); m->startMD2Anim(first, last, mode, speed, trans); } float bbMD2AnimTime(MD2Model *m) { debugMD2(m); return m->getMD2AnimTime(); } int bbMD2AnimLength(MD2Model *m) { debugMD2(m); return m->getMD2AnimLength(); } int bbMD2Animating(MD2Model *m) { debugMD2(m); return m->getMD2Animating(); } ////////////////// // BSP Commands // ////////////////// Entity * bbLoadBSP(BBStr *file, float gam, Entity *p) { debugParent(p); CachedTexture::setPath(filenamepath(*file)); Q3BSPModel *t = new Q3BSPModel(*file, gam); delete file; CachedTexture::setPath(""); if (!t->isValid()) { delete t; return 0; } return insertEntity(t, p); } void bbBSPAmbientLight(Q3BSPModel *t, float r, float g, float b) { debugBSP(t); t->setAmbient(Vector(r*s_colorToFloat, g*s_colorToFloat, b*s_colorToFloat)); } void bbBSPLighting(Q3BSPModel *t, int lmap) { debugBSP(t); t->setLighting(!!lmap); } ////////////////////// // TERRAIN COMMANDS // ////////////////////// static float terrainHeight(Terrain *t, float x, float z) { int ix = (int)floor(x); int iz = (int)floor(z); float tx = x - ix, tz = z - iz; float h0 = t->getHeight(ix, iz); float h1 = t->getHeight(ix + 1, iz); float h2 = t->getHeight(ix, iz + 1); float h3 = t->getHeight(ix + 1, iz + 1); float ha = (h1 - h0)*tx + h0, hb = (h3 - h2)*tx + h2; float h = (hb - ha)*tz + ha; return h; } static Vector terrainVector(Terrain *t, float x, float y, float z) { Vector v = -t->GetWorldTransform() * Vector(x, y, z); return t->GetWorldTransform() * Vector(v.x, terrainHeight(t, v.x, v.z), v.z); } Entity * bbCreateTerrain(int n, Entity *p) { debugParent(p); int shift = 0; while ((1 << shift) < n) ++shift; if ((1 << shift) != n) ThrowRuntimeException("Illegal terrain size"); Terrain *t = new Terrain(shift); return insertEntity(t, p); } Entity * bbLoadTerrain(BBStr *file, Entity *p) { debugParent(p); gxCanvas *c = gx_graphics->loadCanvas(*file, gxCanvas::CANVAS_HIGHCOLOR); if (!c) ThrowRuntimeException("Unable to load heightmap image"); int w = c->getWidth(), h = c->getHeight(); if (w != h) ThrowRuntimeException("Terrain must be square"); int shift = 0; while ((1 << shift) < w) ++shift; if ((1 << shift) != w) ThrowRuntimeException("Illegal terrain size"); Terrain *t = new Terrain(shift); c->lock(); for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { int rgb = c->getPixelFast(x, y); int r = (rgb >> 16) & 0xff, g = (rgb >> 8) & 0xff, b = rgb & 0xff; float p = (r > g ? (r > b ? r : b) : (g > b ? g : b)) / 255.0f; t->setHeight(x, h - 1 - y, p, false); } } c->unlock(); gx_graphics->freeCanvas(c); return insertEntity(t, p); } void bbTerrainDetail(Terrain *t, int n, int m) { debugTerrain(t); t->setDetail(n, !!m); } void bbTerrainShading(Terrain *t, int enable) { debugTerrain(t); t->setShading(!!enable); } float bbTerrainX(Terrain *t, float x, float y, float z) { debugTerrain(t); return terrainVector(t, x, y, z).x; } float bbTerrainY(Terrain *t, float x, float y, float z) { debugTerrain(t); return terrainVector(t, x, y, z).y; } float bbTerrainZ(Terrain *t, float x, float y, float z) { debugTerrain(t); return terrainVector(t, x, y, z).z; } int bbTerrainSize(Terrain *t) { debugTerrain(t); return t->getSize(); } float bbTerrainHeight(Terrain *t, int x, int z) { debugTerrain(t); return t->getHeight(x, z); } void bbModifyTerrain(Terrain *t, int x, int z, float h, int realtime) { debugTerrain(t); t->setHeight(x, z, h, !!realtime); } //////////////////// // AUDIO COMMANDS // //////////////////// Entity * bbCreateListener(Entity *p, float roll, float dopp, float dist) { if (debug) { debugParent(p); if (listener) ThrowRuntimeException("Listener already created"); } listener = new Listener(roll, dopp, dist); return insertEntity(listener, p); } gxChannel * bbEmitSound(gxSound *sound, Object *o) { if (debug) { debugObject(o); if (!listener) ThrowRuntimeException("No Listener created"); } return o->emitSound(sound); } ///////////////////// // ENTITY COMMANDS // ///////////////////// Entity * bbCopyEntity(Entity *e, Entity *p) { if (debug) { debugEntity(e); debugParent(p); } Entity *t = e->getObject()->copy(); if (!t) return 0; return insertEntity(t, p); } void bbFreeEntity(Entity *e) { if (!e) return; if (debug) { debugEntity(e); erase(e); } delete e; } void bbHideEntity(Entity *e) { debugEntity(e); e->SetEnabled(false); e->SetVisible(false); } void bbShowEntity(Entity *e) { debugEntity(e); e->SetVisible(true); e->SetEnabled(true); e->getObject()->reset(); } void bbEntityParent(Entity *e, Entity *p, int global) { if (debug) { debugEntity(e); debugParent(p); Entity *t = p; while (t) { if (t == e) { ThrowRuntimeException("Entity cannot be parented to itself!"); } t = t->getParent(); } } if (e->getParent() == p) return; if (global) { Transform t = e->GetWorldTransform(); e->SetParent(p); e->SetWorldTransform(t); } else { e->SetParent(p); e->getObject()->reset(); } } int bbCountChildren(Entity *e) { debugEntity(e); int n = 0; for (Entity *p = e->GetChildren(); p; p = p->GetSuccessor()) ++n; return n; } Entity * bbGetChild(Entity *e, int index) { debugEntity(e); Entity *p = e->GetChildren(); while (--index && p) p = p->GetSuccessor(); return p; } Entity * bbFindChild(Entity *e, BBStr *t) { debugEntity(e); e = findChild(e, *t); delete t; return e; } //////////////////////// // ANIMATION COMMANDS // //////////////////////// int bbLoadAnimSeq(Object *o, BBStr *f) { debugObject(o); if (Animator *anim = o->getAnimator()) { Entity *t = loadEntity(f->c_str(), MeshLoader::HINT_ANIMONLY); delete f; if (t) { if (Animator *p = t->getObject()->getAnimator()) { anim->addSeqs(p); } delete t; } return anim->numSeqs() - 1; } else { delete f; } return -1; } void bbSetAnimTime(Object *o, float time, int seq) { debugObject(o); if (Animator *anim = o->getAnimator()) { anim->setAnimTime(time, seq); } else { ThrowRuntimeException("Entity has not animation"); } } void bbAnimate(Object *o, int mode, float speed, int seq, float trans) { debugObject(o); if (Animator *anim = o->getAnimator()) { anim->animate(mode, speed, seq, trans); } else { ThrowRuntimeException("Entity has no animation"); } } void bbSetAnimKey(Object *o, int frame, int pos_key, int rot_key, int scl_key) { debugObject(o); Animation anim = o->getAnimation(); if (pos_key) anim.setPositionKey(frame, o->GetLocalPosition()); if (rot_key) anim.setRotationKey(frame, o->GetLocalRotation()); if (scl_key) anim.setScaleKey(frame, o->GetLocalScale()); o->setAnimation(anim); } int bbExtractAnimSeq(Object *o, int first, int last, int seq) { debugObject(o); if (Animator *anim = o->getAnimator()) { anim->extractSeq(first, last, seq); return anim->numSeqs() - 1; } return -1; } int bbAddAnimSeq(Object *o, int length) { debugObject(o); Animator *anim = o->getAnimator(); if (anim) { anim->addSeq(length); } else { anim = new Animator(o, length); o->setAnimator(anim); } return anim->numSeqs() - 1; } int bbAnimSeq(Object *o) { debugObject(o); if (Animator *anim = o->getAnimator()) return anim->animSeq(); return -1; } float bbAnimTime(Object *o) { debugObject(o); if (Animator *anim = o->getAnimator()) return anim->animTime(); return -1; } int bbAnimLength(Object *o) { debugObject(o); if (Animator *anim = o->getAnimator()) return anim->animLen(); return -1; } int bbAnimating(Object *o) { debugObject(o); if (Animator *anim = o->getAnimator()) return anim->animating(); return 0; } //////////////////////////////// // ENTITY SPECIAL FX COMMANDS // //////////////////////////////// void bbPaintEntity(Model *m, Brush *b) { if (debug) { debugModel(m); debugBrush(b); } m->setBrush(*b); } void bbEntityColor(Model *m, float r, float g, float b) { debugModel(m); m->setColor(Vector(r*s_colorToFloat, g*s_colorToFloat, b*s_colorToFloat)); } void bbEntityAlpha(Model *m, float alpha) { debugModel(m); m->setAlpha(alpha); } void bbEntityShininess(Model *m, float shininess) { debugModel(m); m->setShininess(shininess); } void bbEntityTexture(Model *m, Texture *t, int frame, int index) { debugModel(m); debugTexture(t); m->setTexture(index, *t, frame); } void bbEntityBlend(Model *m, int blend) { debugModel(m); m->setBlend(blend); } void bbEntityFX(Model *m, int fx) { debugModel(m); m->setFX(fx); } void bbEntityAutoFade(Model *m, float nr, float fr) { debugModel(m); m->setAutoFade(nr, fr); } void bbEntityOrder(Object *o, int n) { if (debug) { debugEntity(o); if (!o->getModel() && !o->getCamera()) { ThrowRuntimeException("Entity is not a model or camera"); } } o->setOrder(n); } ////////////////////////////// // ENTITY PROPERTY COMMANDS // ////////////////////////////// float bbEntityX(Entity *e, int global) { debugEntity(e); return global ? e->GetWorldPosition().x : e->GetLocalPosition().x; } float bbEntityY(Entity *e, int global) { debugEntity(e); return global ? e->GetWorldPosition().y : e->GetLocalPosition().y; } float bbEntityZ(Entity *e, int global) { debugEntity(e); return global ? e->GetWorldPosition().z : e->GetLocalPosition().z; } float bbEntityPitch(Entity *e, int global) { debugEntity(e); return quatPitch(global ? e->GetWorldRotation() : e->GetLocalRotation()) * s_radiansToDegrees; } float bbEntityYaw(Entity *e, int global) { debugEntity(e); return quatYaw(global ? e->GetWorldRotation() : e->GetLocalRotation()) * s_radiansToDegrees; } float bbEntityRoll(Entity *e, int global) { debugEntity(e); return quatRoll(global ? e->GetWorldRotation() : e->GetLocalRotation()) * s_radiansToDegrees; } float bbGetMatElement(Entity *e, int row, int col) { debugEntity(e); return row < 3 ? e->GetWorldTransform().m[row][col] : e->GetWorldTransform().v[col]; } void bbTFormPoint(float x, float y, float z, Entity *src, Entity *dest) { if (debug) { if (src) debugEntity(src); if (dest) debugEntity(dest); } tformed = Vector(x, y, z); if (src) tformed = src->GetWorldTransform() * tformed; if (dest) tformed = -dest->GetWorldTransform() * tformed; } void bbTFormVector(float x, float y, float z, Entity *src, Entity *dest) { if (debug) { if (src) debugEntity(src); if (dest) debugEntity(dest); } tformed = Vector(x, y, z); if (src) tformed = src->GetWorldTransform().m * tformed; if (dest) tformed = -dest->GetWorldTransform().m * tformed; } void bbTFormNormal(float x, float y, float z, Entity *src, Entity *dest) { if (debug) { if (src) debugEntity(src); if (dest) debugEntity(dest); } tformed = Vector(x, y, z); if (src) tformed = (src->GetWorldTransform().m).cofactor() * tformed; if (dest) tformed = (-dest->GetWorldTransform().m).cofactor() * tformed; tformed.normalize(); } float bbTFormedX() { return tformed.x; } float bbTFormedY() { return tformed.y; } float bbTFormedZ() { return tformed.z; } float bbVectorYaw(float x, float y, float z) { return Vector(x, y, z).yaw() * s_radiansToDegrees; } float bbVectorPitch(float x, float y, float z) { return Vector(x, y, z).pitch() * s_radiansToDegrees; } float bbDeltaYaw(Entity *src, Entity *dest) { float x = src->GetWorldTransform().m.k.yaw(); float y = (dest->GetWorldTransform().v - src->GetWorldTransform().v).yaw(); float d = y - x; if (d < -PI) d += TWOPI; else if (d >= PI) d -= TWOPI; return d*s_radiansToDegrees; } float bbDeltaPitch(Entity *src, Entity *dest) { float x = src->GetWorldTransform().m.k.pitch(); float y = (dest->GetWorldTransform().v - src->GetWorldTransform().v).pitch(); float d = y - x; if (d < -PI) d += TWOPI; else if (d >= PI) d -= TWOPI; return d*s_radiansToDegrees; } /////////////////////////////// // ENTITY COLLISION COMMANDS // /////////////////////////////// void bbResetEntity(Object *o) { debugObject(o); o->reset(); } static void entityType(Entity *e, int type) { e->getObject()->setCollisionType(type); e->getObject()->reset(); for (Entity *p = e->GetChildren(); p; p = p->GetSuccessor()) { entityType(p, type); } } void bbEntityType(Object *o, int type, int recurs) { if (debug) { debugObject(o); if (type < 0 || type>999) ThrowRuntimeException("EntityType ID must be in the range 0...999"); } if (recurs) entityType(o, type); else { o->setCollisionType(type); o->reset(); } } void bbEntityPickMode(Object *o, int mode, int obs) { debugObject(o); o->setPickGeometry(mode); o->setObscurer(!!obs); } Entity * bbGetParent(Entity *e) { debugEntity(e); return e->getParent(); } int bbGetEntityType(Object *o) { debugObject(o); return o->getCollisionType(); } void bbEntityRadius(Object *o, float x_radius, float y_radius) { debugObject(o); Vector radii(x_radius, y_radius ? y_radius : x_radius, x_radius); o->setCollisionRadii(radii); } void bbEntityBox(Object *o, float x, float y, float z, float w, float h, float d) { debugObject(o); Box b(Vector(x, y, z)); b.update(Vector(x + w, y + h, z + d)); o->setCollisionBox(b); } Object * bbEntityCollided(Object *o, int type) { debugObject(o); Object::Collisions::const_iterator it; const Object::Collisions &c = o->getCollisions(); for (it = c.begin(); it != c.end(); ++it) { const ObjCollision *c = *it; if (c->with->getCollisionType() == type) return c->with; } return 0; } int bbCountCollisions(Object *o) { debugObject(o); return o->getCollisions().size(); } float bbCollisionX(Object *o, int index) { debugColl(o, index); return o->getCollisions()[index - 1]->coords.x; } float bbCollisionY(Object *o, int index) { debugColl(o, index); return o->getCollisions()[index - 1]->coords.y; } float bbCollisionZ(Object *o, int index) { debugColl(o, index); return o->getCollisions()[index - 1]->coords.z; } float bbCollisionNX(Object *o, int index) { debugColl(o, index); return o->getCollisions()[index - 1]->collision.normal.x; } float bbCollisionNY(Object *o, int index) { debugColl(o, index); return o->getCollisions()[index - 1]->collision.normal.y; } float bbCollisionNZ(Object *o, int index) { debugColl(o, index); return o->getCollisions()[index - 1]->collision.normal.z; } float bbCollisionTime(Object *o, int index) { debugColl(o, index); return o->getCollisions()[index - 1]->collision.time; } Object * bbCollisionEntity(Object *o, int index) { debugColl(o, index); return o->getCollisions()[index - 1]->with; } void * bbCollisionSurface(Object *o, int index) { debugColl(o, index); return o->getCollisions()[index - 1]->collision.surface; } int bbCollisionTriangle(Object *o, int index) { debugColl(o, index); return o->getCollisions()[index - 1]->collision.index; } float bbEntityDistance(Entity *src, Entity *dest) { debugEntity(src); debugEntity(dest); return src->GetWorldPosition().distance(dest->GetWorldPosition()); } //////////////////////////////////// // ENTITY TRANSFORMATION COMMANDS // //////////////////////////////////// void bbMoveEntity(Entity *e, float x, float y, float z) { debugEntity(e); e->SetLocalPosition(e->GetLocalPosition() + e->GetLocalRotation()*Vector(x, y, z)); } void bbTurnEntity(Entity *e, float p, float y, float r, int global) { debugEntity(e); global ? e->SetWorldRotation(rotationQuat(p*s_degreesToRadians, y*s_degreesToRadians, r*s_degreesToRadians)*e->GetWorldRotation()) : e->SetLocalRotation(e->GetLocalRotation()*rotationQuat(p*s_degreesToRadians, y*s_degreesToRadians, r*s_degreesToRadians)); } void bbTranslateEntity(Entity *e, float x, float y, float z, int global) { debugEntity(e); global ? e->SetWorldPosition(e->GetWorldPosition() + Vector(x, y, z)) : e->SetLocalPosition(e->GetLocalPosition() + Vector(x, y, z)); } void bbPositionEntity(Entity *e, float x, float y, float z, int global) { debugEntity(e); global ? e->SetWorldPosition(Vector(x, y, z)) : e->SetLocalPosition(Vector(x, y, z)); } void bbScaleEntity(Entity *e, float x, float y, float z, int global) { debugEntity(e); global ? e->SetWorldScale(Vector(x, y, z)) : e->SetLocalScale(Vector(x, y, z)); } void bbRotateEntity(Entity *e, float p, float y, float r, int global) { debugEntity(e); global ? e->SetWorldRotation(rotationQuat(p*s_degreesToRadians, y*s_degreesToRadians, r*s_degreesToRadians)) : e->SetLocalRotation(rotationQuat(p*s_degreesToRadians, y*s_degreesToRadians, r*s_degreesToRadians)); } void bbPointEntity(Entity *e, Entity *t, float roll) { if (debug) { debugEntity(e); debugEntity(t); } Vector v = t->GetWorldTransform().v - e->GetWorldTransform().v; e->SetWorldRotation(rotationQuat(v.pitch(), v.yaw(), roll*s_degreesToRadians)); } void bbAlignToVector(Entity *e, float nx, float ny, float nz, int axis, float rate) { Vector ax(nx, ny, nz); float l = ax.length(); if (l <= FLT_EPSILON) return; ax /= l; Quat q = e->GetWorldRotation(); Vector tv = (axis == 1) ? q.i() : (axis == 2 ? q.j() : q.k()); float dp = ax.dot(tv); if (dp >= 1 - FLT_EPSILON) return; if (dp <= -1 + FLT_EPSILON) { float an = PI*rate / 2; Vector cp = (axis == 1) ? q.j() : (axis == 2 ? q.k() : q.i()); e->SetWorldRotation(Quat(cosf(an), cp*sinf(an)) * q); return; } float an = acosf(dp)*rate / 2; Vector cp = ax.cross(tv).normalized(); e->SetWorldRotation(Quat(cosf(an), cp*sinf(an)) * q); } ////////////////////////// // ENTITY MISC COMMANDS // ////////////////////////// void bbNameEntity(Entity *e, BBStr *t) { debugEntity(e); e->SetName(*t); delete t; } BBStr * bbEntityName(Entity *e) { debugEntity(e); return new BBStr(e->getName()); } BBStr *bbEntityClass(Entity *e) { debugEntity(e); const char *p = "Pivot"; if (e->getLight()) p = "Light"; else if (e->getCamera()) p = "Camera"; else if (e->getMirror()) p = "Mirror"; else if (e->getListener()) p = "Listener"; else if (Model *t = e->getModel()) { if (t->getSprite()) p = "Sprite"; else if (t->getTerrain()) p = "Terrain"; else if (t->getPlaneModel()) p = "Plane"; else if (t->getMeshModel()) p = "Mesh"; else if (t->getMD2Model()) p = "MD2"; else if (t->getBSPModel()) p = "BSP"; } return new BBStr(p); } void bbClearWorld(int e, int b, int t) { if (e) { while (Entity::GetEntityOrphans()) bbFreeEntity(Entity::GetEntityOrphans()); } if (b) { while (brush_set.size()) bbFreeBrush(*brush_set.begin()); } if (t) { while (texture_set.size()) bbFreeTexture(*texture_set.begin()); } } extern int active_texs; int bbActiveTextures() { return active_texs; } void blitz3d_open() { gx_scene = gx_graphics->createScene(0); if (!gx_scene) ThrowRuntimeException("Unable to create 3D Scene"); world = new World(); projected = Vector(); picked.collision = Collision(); picked.with = 0; picked.coords = Vector(); Texture::clearFilters(); Texture::addFilter("", gxCanvas::CANVAS_TEX_RGB | gxCanvas::CANVAS_TEX_MIPMAP); loader_mat_map.clear(); loader_mat_map["x"] = Transform(); loader_mat_map["3ds"] = Transform(Matrix(Vector(1, 0, 0), Vector(0, 0, 1), Vector(0, 1, 0))); listener = 0; stats_mode = false; } void blitz3d_close() { if (!gx_scene) return; bbClearWorld(1, 1, 1); Texture::clearFilters(); loader_mat_map.clear(); delete world; gx_graphics->freeScene(gx_scene); gx_scene = 0; } bool blitz3d_create() { tri_count = 0; gx_scene = 0; world = 0; return true; } bool blitz3d_destroy() { blitz3d_close(); return true; } void blitz3d_link(void(*rtSym)(const char *sym, void *pc)) { rtSym("LoaderMatrix$file_ext#xx#xy#xz#yx#yy#yz#zx#zy#zz", bbLoaderMatrix); rtSym("HWMultiTex%enable", bbHWMultiTex); rtSym("%HWTexUnits", bbHWTexUnits); rtSym("%GfxDriverCaps3D", bbGfxDriverCaps3D); rtSym("WBuffer%enable", bbWBuffer); rtSym("Dither%enable", bbDither); rtSym("AntiAlias%enable", bbAntiAlias); rtSym("WireFrame%enable", bbWireFrame); rtSym("AmbientLight#red#green#blue", bbAmbientLight); rtSym("ClearCollisions", bbClearCollisions); rtSym("Collisions%source_type%destination_type%method%response", bbCollisions); rtSym("UpdateWorld#elapsed_time=1", bbUpdateWorld); rtSym("CaptureWorld", bbCaptureWorld); rtSym("RenderWorld#tween=1", bbRenderWorld); rtSym("ClearWorld%entities=1%brushes=1%textures=1", bbClearWorld); rtSym("%ActiveTextures", bbActiveTextures); rtSym("%TrisRendered", bbTrisRendered); rtSym("#Stats3D%type", bbStats3D); rtSym("%CreateTexture%width%height%flags=0%frames=1", bbCreateTexture); rtSym("%LoadTexture$file%flags=1", bbLoadTexture); rtSym("%LoadAnimTexture$file%flags%width%height%first%count", bbLoadAnimTexture); rtSym("FreeTexture%texture", bbFreeTexture); rtSym("TextureBlend%texture%blend", bbTextureBlend); rtSym("TextureCoords%texture%coords", bbTextureCoords); rtSym("ScaleTexture%texture#u_scale#v_scale", bbScaleTexture); rtSym("RotateTexture%texture#angle", bbRotateTexture); rtSym("PositionTexture%texture#u_offset#v_offset", bbPositionTexture); rtSym("%TextureWidth%texture", bbTextureWidth); rtSym("%TextureHeight%texture", bbTextureHeight); rtSym("$TextureName%texture", bbTextureName); rtSym("SetCubeFace%texture%face", bbSetCubeFace); rtSym("SetCubeMode%texture%mode", bbSetCubeMode); rtSym("%TextureBuffer%texture%frame=0", bbTextureBuffer); rtSym("ClearTextureFilters", bbClearTextureFilters); rtSym("TextureFilter$match_text%texture_flags=0", bbTextureFilter); rtSym("%CreateBrush#red=255#green=255#blue=255", bbCreateBrush); rtSym("%LoadBrush$file%texture_flags=1#u_scale=1#v_scale=1", bbLoadBrush); rtSym("FreeBrush%brush", bbFreeBrush); rtSym("BrushColor%brush#red#green#blue", bbBrushColor); rtSym("BrushAlpha%brush#alpha", bbBrushAlpha); rtSym("BrushShininess%brush#shininess", bbBrushShininess); rtSym("BrushTexture%brush%texture%frame=0%index=0", bbBrushTexture); rtSym("%GetBrushTexture%brush%index=0", bbGetBrushTexture); rtSym("BrushBlend%brush%blend", bbBrushBlend); rtSym("BrushFX%brush%fx", bbBrushFX); rtSym("%LoadMesh$file%parent=0", bbLoadMesh); rtSym("%LoadAnimMesh$file%parent=0", bbLoadAnimMesh); rtSym("%LoadAnimSeq%entity$file", bbLoadAnimSeq); rtSym("%CreateMesh%parent=0", bbCreateMesh); rtSym("%CreateCube%parent=0", bbCreateCube); rtSym("%CreateSphere%segments=8%parent=0", bbCreateSphere); rtSym("%CreateCylinder%segments=8%solid=1%parent=0", bbCreateCylinder); rtSym("%CreateCone%segments=8%solid=1%parent=0", bbCreateCone); rtSym("%CopyMesh%mesh%parent=0", bbCopyMesh); rtSym("ScaleMesh%mesh#x_scale#y_scale#z_scale", bbScaleMesh); rtSym("RotateMesh%mesh#pitch#yaw#roll", bbRotateMesh); rtSym("PositionMesh%mesh#x#y#z", bbPositionMesh); rtSym("FitMesh%mesh#x#y#z#width#height#depth%uniform=0", bbFitMesh); rtSym("FlipMesh%mesh", bbFlipMesh); rtSym("PaintMesh%mesh%brush", bbPaintMesh); rtSym("AddMesh%source_mesh%dest_mesh", bbAddMesh); rtSym("UpdateNormals%mesh", bbUpdateNormals); rtSym("LightMesh%mesh#red#green#blue#range=0#x=0#y=0#z=0", bbLightMesh); rtSym("#MeshWidth%mesh", bbMeshWidth); rtSym("#MeshHeight%mesh", bbMeshHeight); rtSym("#MeshDepth%mesh", bbMeshDepth); rtSym("%MeshesIntersect%mesh_a%mesh_b", bbMeshesIntersect); rtSym("%CountSurfaces%mesh", bbCountSurfaces); rtSym("%GetSurface%mesh%surface_index", bbGetSurface); rtSym("MeshCullBox%mesh#x#y#z#width#height#depth", bbMeshCullBox); rtSym("%CreateSurface%mesh%brush=0", bbCreateSurface); rtSym("%GetSurfaceBrush%surface", bbGetSurfaceBrush); rtSym("%GetEntityBrush%entity", bbGetEntityBrush); rtSym("%FindSurface%mesh%brush", bbFindSurface); rtSym("ClearSurface%surface%clear_vertices=1%clear_triangles=1", bbClearSurface); rtSym("PaintSurface%surface%brush", bbPaintSurface); rtSym("%AddVertex%surface#x#y#z#u=0#v=0#w=1", bbAddVertex); rtSym("%AddTriangle%surface%v0%v1%v2", bbAddTriangle); rtSym("VertexCoords%surface%index#x#y#z", bbVertexCoords); rtSym("VertexNormal%surface%index#nx#ny#nz", bbVertexNormal); rtSym("VertexColor%surface%index#red#green#blue#alpha=1", bbVertexColor); rtSym("VertexTexCoords%surface%index#u#v#w=1%coord_set=0", bbVertexTexCoords); rtSym("%CountVertices%surface", bbCountVertices); rtSym("%CountTriangles%surface", bbCountTriangles); rtSym("#VertexX%surface%index", bbVertexX); rtSym("#VertexY%surface%index", bbVertexY); rtSym("#VertexZ%surface%index", bbVertexZ); rtSym("#VertexNX%surface%index", bbVertexNX); rtSym("#VertexNY%surface%index", bbVertexNY); rtSym("#VertexNZ%surface%index", bbVertexNZ); rtSym("#VertexRed%surface%index", bbVertexRed); rtSym("#VertexGreen%surface%index", bbVertexGreen); rtSym("#VertexBlue%surface%index", bbVertexBlue); rtSym("#VertexAlpha%surface%index", bbVertexAlpha); rtSym("#VertexU%surface%index%coord_set=0", bbVertexU); rtSym("#VertexV%surface%index%coord_set=0", bbVertexV); rtSym("#VertexW%surface%index%coord_set=0", bbVertexW); rtSym("%TriangleVertex%surface%index%vertex", bbTriangleVertex); rtSym("%CreateCamera%parent=0", bbCreateCamera); rtSym("CameraZoom%camera#zoom", bbCameraZoom); rtSym("CameraRange%camera#near#far", bbCameraRange); rtSym("CameraClsColor%camera#red#green#blue", bbCameraClsColor); rtSym("CameraClsMode%camera%cls_color%cls_zbuffer", bbCameraClsMode); rtSym("CameraProjMode%camera%mode", bbCameraProjMode); rtSym("CameraViewport%camera%x%y%width%height", bbCameraViewport); rtSym("CameraFogColor%camera#red#green#blue", bbCameraFogColor); rtSym("CameraFogRange%camera#near#far", bbCameraFogRange); rtSym("CameraFogMode%camera%mode", bbCameraFogMode); rtSym("CameraProject%camera#x#y#z", bbCameraProject); rtSym("#ProjectedX", bbProjectedX); rtSym("#ProjectedY", bbProjectedY); rtSym("#ProjectedZ", bbProjectedZ); rtSym("%EntityInView%entity%camera", bbEntityInView); rtSym("%EntityVisible%src_entity%dest_entity", bbEntityVisible); rtSym("%EntityPick%entity#range", bbEntityPick); rtSym("%LinePick#x#y#z#dx#dy#dz#radius=0", bbLinePick); rtSym("%CameraPick%camera#viewport_x#viewport_y", bbCameraPick); rtSym("#PickedX", bbPickedX); rtSym("#PickedY", bbPickedY); rtSym("#PickedZ", bbPickedZ); rtSym("#PickedNX", bbPickedNX); rtSym("#PickedNY", bbPickedNY); rtSym("#PickedNZ", bbPickedNZ); rtSym("#PickedTime", bbPickedTime); rtSym("%PickedEntity", bbPickedEntity); rtSym("%PickedSurface", bbPickedSurface); rtSym("%PickedTriangle", bbPickedTriangle); rtSym("%CreateLight%type=1%parent=0", bbCreateLight); rtSym("LightColor%light#red#green#blue", bbLightColor); rtSym("LightRange%light#range", bbLightRange); rtSym("LightConeAngles%light#inner_angle#outer_angle", bbLightConeAngles); rtSym("%CreatePivot%parent=0", bbCreatePivot); rtSym("%CreateSprite%parent=0", bbCreateSprite); rtSym("%LoadSprite$file%texture_flags=1%parent=0", bbLoadSprite); rtSym("RotateSprite%sprite#angle", bbRotateSprite); rtSym("ScaleSprite%sprite#x_scale#y_scale", bbScaleSprite); rtSym("HandleSprite%sprite#x_handle#y_handle", bbHandleSprite); rtSym("SpriteViewMode%sprite%view_mode", bbSpriteViewMode); rtSym("%LoadMD2$file%parent=0", bbLoadMD2); rtSym("AnimateMD2%md2%mode=1#speed=1%first_frame=0%last_frame=9999#transition=0", bbAnimateMD2); rtSym("#MD2AnimTime%md2", bbMD2AnimTime); rtSym("%MD2AnimLength%md2", bbMD2AnimLength); rtSym("%MD2Animating%md2", bbMD2Animating); rtSym("%LoadBSP$file#gamma_adj=0%parent=0", bbLoadBSP); rtSym("BSPLighting%bsp%use_lightmaps", bbBSPLighting); rtSym("BSPAmbientLight%bsp#red#green#blue", bbBSPAmbientLight); rtSym("%CreateMirror%parent=0", bbCreateMirror); rtSym("%CreatePlane%segments=1%parent=0", bbCreatePlane); rtSym("%CreateTerrain%grid_size%parent=0", bbCreateTerrain); rtSym("%LoadTerrain$heightmap_file%parent=0", bbLoadTerrain); rtSym("TerrainDetail%terrain%detail_level%morph=0", bbTerrainDetail); rtSym("TerrainShading%terrain%enable", bbTerrainShading); rtSym("#TerrainX%terrain#world_x#world_y#world_z", bbTerrainX); rtSym("#TerrainY%terrain#world_x#world_y#world_z", bbTerrainY); rtSym("#TerrainZ%terrain#world_x#world_y#world_z", bbTerrainZ); rtSym("%TerrainSize%terrain", bbTerrainSize); rtSym("#TerrainHeight%terrain%terrain_x%terrain_z", bbTerrainHeight); rtSym("ModifyTerrain%terrain%terrain_x%terrain_z#height%realtime=0", bbModifyTerrain); rtSym("%CreateListener%parent#rolloff_factor=1#doppler_scale=1#distance_scale=1", bbCreateListener); rtSym("%EmitSound%sound%entity", bbEmitSound); rtSym("%CopyEntity%entity%parent=0", bbCopyEntity); rtSym("#EntityX%entity%global=0", bbEntityX); rtSym("#EntityY%entity%global=0", bbEntityY); rtSym("#EntityZ%entity%global=0", bbEntityZ); rtSym("#EntityPitch%entity%global=0", bbEntityPitch); rtSym("#EntityYaw%entity%global=0", bbEntityYaw); rtSym("#EntityRoll%entity%global=0", bbEntityRoll); rtSym("#GetMatElement%entity%row%column", bbGetMatElement); rtSym("TFormPoint#x#y#z%source_entity%dest_entity", bbTFormPoint); rtSym("TFormVector#x#y#z%source_entity%dest_entity", bbTFormVector); rtSym("TFormNormal#x#y#z%source_entity%dest_entity", bbTFormNormal); rtSym("#TFormedX", bbTFormedX); rtSym("#TFormedY", bbTFormedY); rtSym("#TFormedZ", bbTFormedZ); rtSym("#VectorYaw#x#y#z", bbVectorYaw); rtSym("#VectorPitch#x#y#z", bbVectorPitch); rtSym("#DeltaPitch%src_entity%dest_entity", bbDeltaPitch); rtSym("#DeltaYaw%src_entity%dest_entity", bbDeltaYaw); rtSym("ResetEntity%entity", bbResetEntity); rtSym("EntityType%entity%collision_type%recursive=0", bbEntityType); rtSym("EntityPickMode%entity%pick_geometry%obscurer=1", bbEntityPickMode); rtSym("%GetParent%entity", bbGetParent); rtSym("%GetEntityType%entity", bbGetEntityType); rtSym("EntityRadius%entity#x_radius#y_radius=0", bbEntityRadius); rtSym("EntityBox%entity#x#y#z#width#height#depth", bbEntityBox); rtSym("#EntityDistance%source_entity%destination_entity", bbEntityDistance); rtSym("%EntityCollided%entity%type", bbEntityCollided); rtSym("%CountCollisions%entity", bbCountCollisions); rtSym("#CollisionX%entity%collision_index", bbCollisionX); rtSym("#CollisionY%entity%collision_index", bbCollisionY); rtSym("#CollisionZ%entity%collision_index", bbCollisionZ); rtSym("#CollisionNX%entity%collision_index", bbCollisionNX); rtSym("#CollisionNY%entity%collision_index", bbCollisionNY); rtSym("#CollisionNZ%entity%collision_index", bbCollisionNZ); rtSym("#CollisionTime%entity%collision_index", bbCollisionTime); rtSym("%CollisionEntity%entity%collision_index", bbCollisionEntity); rtSym("%CollisionSurface%entity%collision_index", bbCollisionSurface); rtSym("%CollisionTriangle%entity%collision_index", bbCollisionTriangle); rtSym("MoveEntity%entity#x#y#z", bbMoveEntity); rtSym("TurnEntity%entity#pitch#yaw#roll%global=0", bbTurnEntity); rtSym("TranslateEntity%entity#x#y#z%global=0", bbTranslateEntity); rtSym("PositionEntity%entity#x#y#z%global=0", bbPositionEntity); rtSym("ScaleEntity%entity#x_scale#y_scale#z_scale%global=0", bbScaleEntity); rtSym("RotateEntity%entity#pitch#yaw#roll%global=0", bbRotateEntity); rtSym("PointEntity%entity%target#roll=0", bbPointEntity); rtSym("AlignToVector%entity#vector_x#vector_y#vector_z%axis#rate=1", bbAlignToVector); rtSym("SetAnimTime%entity#time%anim_seq=0", bbSetAnimTime); rtSym("Animate%entity%mode=1#speed=1%sequence=0#transition=0", bbAnimate); rtSym("SetAnimKey%entity%frame%pos_key=1%rot_key=1%scale_key=1", bbSetAnimKey); rtSym("%AddAnimSeq%entity%length", bbAddAnimSeq); rtSym("%ExtractAnimSeq%entity%first_frame%last_frame%anim_seq=0", bbExtractAnimSeq); rtSym("%AnimSeq%entity", bbAnimSeq); rtSym("#AnimTime%entity", bbAnimTime); rtSym("%AnimLength%entity", bbAnimLength); rtSym("%Animating%entity", bbAnimating); rtSym("EntityParent%entity%parent%global=1", bbEntityParent); rtSym("%CountChildren%entity", bbCountChildren); rtSym("%GetChild%entity%index", bbGetChild); rtSym("%FindChild%entity$name", bbFindChild); rtSym("PaintEntity%entity%brush", bbPaintEntity); rtSym("EntityColor%entity#red#green#blue", bbEntityColor); rtSym("EntityAlpha%entity#alpha", bbEntityAlpha); rtSym("EntityShininess%entity#shininess", bbEntityShininess); rtSym("EntityTexture%entity%texture%frame=0%index=0", bbEntityTexture); rtSym("EntityBlend%entity%blend", bbEntityBlend); rtSym("EntityFX%entity%fx", bbEntityFX); rtSym("EntityAutoFade%entity#near#far", bbEntityAutoFade); rtSym("EntityOrder%entity%order", bbEntityOrder); rtSym("HideEntity%entity", bbHideEntity); rtSym("ShowEntity%entity", bbShowEntity); rtSym("FreeEntity%entity", bbFreeEntity); rtSym("NameEntity%entity$name", bbNameEntity); rtSym("$EntityName%entity", bbEntityName); rtSym("$EntityClass%entity", bbEntityClass); }