From 6ada405af0ce8b648e62b23e0ae84ad21f888059 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 25 Jan 2012 16:08:55 +0100 Subject: [PATCH] removing last bits of old D code --- old_d_version/.gitignore | 66 -- old_d_version/bullet/bindings.d | 85 -- old_d_version/bullet/bullet.d | 34 - old_d_version/bullet/cpp_bullet.cpp | 502 ---------- old_d_version/bullet/cpp_player.cpp | 381 -------- old_d_version/bullet/cpp_scale.cpp | 98 -- old_d_version/core/config.d | 437 --------- old_d_version/core/filefinder.d | 229 ----- old_d_version/esm/defs.d | 187 ---- old_d_version/esm/esmmain.d | 167 ---- old_d_version/esm/filereader.d | 783 --------------- old_d_version/esm/imports.d | 47 - old_d_version/esm/listkeeper.d | 330 ------- old_d_version/esm/loadcell.d | 189 ---- old_d_version/esm/loaddial.d | 289 ------ old_d_version/esm/records.d | 219 ----- old_d_version/esmtool.d | 377 -------- old_d_version/input/events.d | 336 ------- old_d_version/input/ois.d | 428 --------- old_d_version/ogre/cpp_framelistener.cpp | 41 - old_d_version/ogre/cpp_interface.cpp | 135 --- old_d_version/ogre/meshloader.d | 391 -------- old_d_version/openmw.d | 471 --------- old_d_version/terrain/archive.d | 461 --------- old_d_version/terrain/bindings.d | 27 - old_d_version/terrain/cachewriter.d | 337 ------- old_d_version/terrain/cpp_baseland.cpp | 101 -- old_d_version/terrain/cpp_mesh.cpp | 286 ------ old_d_version/terrain/cpp_terrain.cpp | 413 -------- old_d_version/terrain/esmland.d | 158 --- old_d_version/terrain/generator.d | 1114 ---------------------- old_d_version/terrain/myfile.d | 71 -- old_d_version/terrain/outbuffer.d | 94 -- old_d_version/terrain/quad.d | 286 ------ old_d_version/terrain/terrain.d | 103 -- 35 files changed, 9673 deletions(-) delete mode 100644 old_d_version/.gitignore delete mode 100644 old_d_version/bullet/bindings.d delete mode 100644 old_d_version/bullet/bullet.d delete mode 100644 old_d_version/bullet/cpp_bullet.cpp delete mode 100644 old_d_version/bullet/cpp_player.cpp delete mode 100644 old_d_version/bullet/cpp_scale.cpp delete mode 100644 old_d_version/core/config.d delete mode 100644 old_d_version/core/filefinder.d delete mode 100644 old_d_version/esm/defs.d delete mode 100644 old_d_version/esm/esmmain.d delete mode 100644 old_d_version/esm/filereader.d delete mode 100644 old_d_version/esm/imports.d delete mode 100644 old_d_version/esm/listkeeper.d delete mode 100644 old_d_version/esm/loadcell.d delete mode 100644 old_d_version/esm/loaddial.d delete mode 100644 old_d_version/esm/records.d delete mode 100644 old_d_version/esmtool.d delete mode 100644 old_d_version/input/events.d delete mode 100644 old_d_version/input/ois.d delete mode 100644 old_d_version/ogre/cpp_framelistener.cpp delete mode 100644 old_d_version/ogre/cpp_interface.cpp delete mode 100644 old_d_version/ogre/meshloader.d delete mode 100644 old_d_version/openmw.d delete mode 100644 old_d_version/terrain/archive.d delete mode 100644 old_d_version/terrain/bindings.d delete mode 100644 old_d_version/terrain/cachewriter.d delete mode 100644 old_d_version/terrain/cpp_baseland.cpp delete mode 100644 old_d_version/terrain/cpp_mesh.cpp delete mode 100644 old_d_version/terrain/cpp_terrain.cpp delete mode 100644 old_d_version/terrain/esmland.d delete mode 100644 old_d_version/terrain/generator.d delete mode 100644 old_d_version/terrain/myfile.d delete mode 100644 old_d_version/terrain/outbuffer.d delete mode 100644 old_d_version/terrain/quad.d delete mode 100644 old_d_version/terrain/terrain.d diff --git a/old_d_version/.gitignore b/old_d_version/.gitignore deleted file mode 100644 index 23e6b6fd2..000000000 --- a/old_d_version/.gitignore +++ /dev/null @@ -1,66 +0,0 @@ - -# / -/cache -/later -/openmw.ini.* -/rr.sh -/fontdump -/MyGUI.log -/upm.sh -/raw.txt -/vids -/include -/includes -/.thumbnails -/*.jpg -/*.dll -/*.exe -/*.def -/*.a -/*.map -/*.rsp -/ogre.cfg -/openmw -/bored -/bsatool -/niftool -/esmtool -/bored.highscores -/Ogre.log -/openmw.ini -/openmw.ini.old -/dsss_* -/dsss.last -/objs -/nifobjs - -# /bullet/ -/bullet/OgreOpcode* -/bullet/demo -/bullet/*.a - -# /media_mygui/ -/media_mygui/core.skin.orig -/media_mygui/.thumbnails - -# /monster/ -/monster/*openmw_last - -# /mscripts/ -/mscripts/draft - -# /nif/ -/nif/bumpmap -/nif/*.nif - -# /ogre/ -/ogre/*.nif -/ogre/cs - -# /util/ -/util/iconv - -*.o -*.patch -*.diff -.directory diff --git a/old_d_version/bullet/bindings.d b/old_d_version/bullet/bindings.d deleted file mode 100644 index 9dbd76197..000000000 --- a/old_d_version/bullet/bindings.d +++ /dev/null @@ -1,85 +0,0 @@ -/* - OpenMW - The completely unofficial reimplementation of Morrowind - Copyright (C) 2008 Nicolay Korslund - Email: < korslund@gmail.com > - WWW: http://openmw.snaptoad.com/ - - This file (bindings.d) is part of the OpenMW package. - - OpenMW is distributed as free software: you can redistribute it - and/or modify it under the terms of the GNU General Public License - version 3, as published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - version 3 along with this program. If not, see - http://www.gnu.org/licenses/ . - - */ - -module bullet.bindings; - -/* - * This module is the interface between D and the C++ code that - * handles Bullet. - */ - -typedef void* BulletShape; - -extern(C): - -// Initialize the dynamic world. Returns non-zero if an error occurs. -int bullet_init(); - -// Set physics modes -void bullet_nextMode(); -void bullet_walk(); -void bullet_fly(); -void bullet_ghost(); - -// Warp the player to a specific location. -void bullet_movePlayer(float x, float y, float z); - -// Request that the player moves in this direction -void bullet_setPlayerDir(float x, float y, float z); - -// Get the current player position, after physics and collision have -// been applied. -void bullet_getPlayerPos(float *x, float *y, float *z); - -// Create a box shape. Used for bounding boxes. The box is a trimesh -// and is hollow (you can walk inside it.) -void bullet_createBoxShape(float minX, float minY, float minZ, - float maxX, float maxY, float maxZ, - float *trans,float *matrix); - -// Create a triangle shape. This is cumulative, all meshes created -// with this function are added to the same shape. Since the various -// parts of a mesh can be differently transformed and we are putting -// them all in one shape, we must transform the vertices manually. -void bullet_createTriShape(int numFaces, - void *triArray, - int numVerts, - void *vertArray, - float *trans,float *matrix); - -// "Flushes" the meshes created with createTriShape, returning the -// pointer to the final shape object. -BulletShape bullet_getFinalShape(); - -// Insert a static mesh with the given translation, quaternion -// rotation and scale. The quaternion is assumed to be in Ogre format, -// ie. with the W first. -void bullet_insertStatic(BulletShape shp, float *pos, - float *quat, float scale); - -// Move the physics simulation 'delta' seconds forward in time -void bullet_timeStep(float delta); - -// Deallocate objects -void bullet_cleanup(); - diff --git a/old_d_version/bullet/bullet.d b/old_d_version/bullet/bullet.d deleted file mode 100644 index d33a22b7c..000000000 --- a/old_d_version/bullet/bullet.d +++ /dev/null @@ -1,34 +0,0 @@ -/* - OpenMW - The completely unofficial reimplementation of Morrowind - Copyright (C) 2008 Nicolay Korslund - Email: < korslund@gmail.com > - WWW: http://openmw.snaptoad.com/ - - This file (bullet.d) is part of the OpenMW package. - - OpenMW is distributed as free software: you can redistribute it - and/or modify it under the terms of the GNU General Public License - version 3, as published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - version 3 along with this program. If not, see - http://www.gnu.org/licenses/ . - - */ - -module bullet.bullet; - -import bullet.bindings; - -void initBullet() -{ - if(bullet_init()) - throw new Exception("Bullet setup failed"); -} - -void cleanupBullet() { bullet_cleanup(); } diff --git a/old_d_version/bullet/cpp_bullet.cpp b/old_d_version/bullet/cpp_bullet.cpp deleted file mode 100644 index 50af01b57..000000000 --- a/old_d_version/bullet/cpp_bullet.cpp +++ /dev/null @@ -1,502 +0,0 @@ -/* - OpenMW - The completely unofficial reimplementation of Morrowind - Copyright (C) 2008 Nicolay Korslund - Email: < korslund@gmail.com > - WWW: http://openmw.snaptoad.com/ - - This file (cpp_bullet.cpp) is part of the OpenMW package. - - OpenMW is distributed as free software: you can redistribute it - and/or modify it under the terms of the GNU General Public License - version 3, as published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - version 3 along with this program. If not, see - http://www.gnu.org/licenses/ . - - */ - -#include "btBulletDynamicsCommon.h" - -#include - -#include "../util/dbg.h" - -using namespace std; - -class CustomOverlappingPairCallback; - -enum - { - MASK_PLAYER = 1, - MASK_STATIC = 2 - }; - -// System variables -btDefaultCollisionConfiguration* g_collisionConfiguration; -btCollisionDispatcher *g_dispatcher; -//btBroadphaseInterface *g_broadphase; -btAxisSweep3 *g_broadphase; -btSequentialImpulseConstraintSolver* g_solver; -btDynamicsWorld *g_dynamicsWorld; - -// Player variables -btCollisionObject* g_playerObject; -btConvexShape *g_playerShape; - -// Player position. This is updated automatically by the physics -// system based on g_walkDirection and collisions. It is read by D -// code through bullet_getPlayerPos(). -btVector3 g_playerPosition; - -// Walking vector - defines direction and speed that the player -// intends to move right now. This is updated from D code each frame -// through bullet_setPlayerDir(), based on player input (and later, AI -// decisions.) The units of the vector are points per second. -btVector3 g_walkDirection; - -// The current trimesh shape being built. All new inserted meshes are -// added into this, until bullet_getFinalShape() is called. -btTriangleIndexVertexArray *g_currentMesh; - -// These variables and the class below are used in player collision -// detection. The callback is injected into the broadphase and keeps a -// continuously updated list of what objects are colliding with the -// player (in g_pairCache). This list is used in the function called -// recoverFromPenetration(). -btHashedOverlappingPairCache* g_pairCache; -CustomOverlappingPairCallback *g_customPairCallback; - -// Three physics modes: walking (with gravity and collision), flying -// (collision but no gravity) and ghost mode (fly through walls) -enum - { - PHYS_WALK, - PHYS_FLY, - PHYS_GHOST - }; -int g_physMode; - -// Include the player physics -#include "cpp_player.cpp" - -// Include the uniform shape scaler -#include "cpp_scale.cpp" - -class CustomOverlappingPairCallback : public btOverlappingPairCallback -{ -public: - virtual btBroadphasePair* addOverlappingPair(btBroadphaseProxy* proxy0, - btBroadphaseProxy* proxy1) - { - if (proxy0->m_clientObject==g_playerObject || - proxy1->m_clientObject==g_playerObject) - return g_pairCache->addOverlappingPair(proxy0,proxy1); - return 0; - } - - virtual void* removeOverlappingPair(btBroadphaseProxy* proxy0, - btBroadphaseProxy* proxy1, - btDispatcher* dispatcher) - { - if (proxy0->m_clientObject==g_playerObject || - proxy1->m_clientObject==g_playerObject) - return g_pairCache->removeOverlappingPair(proxy0,proxy1,dispatcher); - - return 0; - } - - void removeOverlappingPairsContainingProxy(btBroadphaseProxy* proxy0, - btDispatcher* dispatcher) - { if (proxy0->m_clientObject==g_playerObject) - g_pairCache->removeOverlappingPairsContainingProxy(proxy0,dispatcher); - } -}; - -extern "C" int32_t bullet_init() -{ - // ------- SET UP THE WORLD ------- - - // Set up basic objects - g_collisionConfiguration = new btDefaultCollisionConfiguration(); - g_dispatcher = new btCollisionDispatcher(g_collisionConfiguration); - //g_broadphase = new btDbvtBroadphase(); - g_solver = new btSequentialImpulseConstraintSolver; - - // TODO: Figure out what to do with this. We need the user callback - // function used below (I think), but this is only offered by this - // broadphase implementation (as far as I can see.) Maybe we can - // scan through the cell first and find good values that covers all - // the objects before we set up the dynamic world. Another option is - // to create a custom broadphase designed for our purpose. (We - // should probably use different ones for interior and exterior - // cells in any case.) - btVector3 worldMin(-20000,-20000,-20000); - btVector3 worldMax(20000,20000,20000); - g_broadphase = new btAxisSweep3(worldMin,worldMax); - - g_dynamicsWorld = - new btDiscreteDynamicsWorld(g_dispatcher, - g_broadphase, - g_solver, - g_collisionConfiguration); - - //g_dynamicsWorld->setGravity(btVector3(0,-10,0)); - - - // ------- SET UP THE PLAYER ------- - - // Create the player collision shape. - float width = 30; - - /* - float height = 50; - btVector3 spherePositions[2]; - btScalar sphereRadii[2]; - sphereRadii[0] = width; - sphereRadii[1] = width; - spherePositions[0] = btVector3 (0,0,0); - spherePositions[1] = btVector3 (0,0,-height); - - // One possible shape is the convex hull around two spheres - g_playerShape = new btMultiSphereShape(btVector3(width/2.0, height/2.0, - width/2.0), &spherePositions[0], &sphereRadii[0], 2); - */ - - // Other posibilities - most are too slow, except the sphere - //g_playerShape = new btCylinderShapeZ(btVector3(width, width, height)); - g_playerShape = new btSphereShape(width); - //g_playerShape = new btCapsuleShapeZ(width, height); - - // Create the collision object - g_playerObject = new btCollisionObject (); - g_playerObject->setCollisionShape (g_playerShape); - g_playerObject->setCollisionFlags (btCollisionObject::CF_NO_CONTACT_RESPONSE); - - - // ------- OTHER STUFF ------- - - // Create a custom callback to pick out all the objects colliding - // with the player. We use this in the collision recovery phase. - g_pairCache = new btHashedOverlappingPairCache(); - g_customPairCallback = new CustomOverlappingPairCallback(); - g_broadphase->setOverlappingPairUserCallback(g_customPairCallback); - - // Set up the callback that moves the player at the end of each - // simulation step. - g_dynamicsWorld->setInternalTickCallback(playerStepCallback); - - // Add the character collision object to the world. - g_dynamicsWorld->addCollisionObject(g_playerObject, - MASK_PLAYER, - MASK_STATIC); - - // Make sure these is zero at startup - g_currentMesh = NULL; - - // Start out walking - g_physMode = PHYS_WALK; - - // Success! - return 0; -} - -// Set physics modes -extern "C" void bullet_walk() -{ - g_physMode = PHYS_WALK; - cout << "Walk mode\n"; -} - -extern "C" void bullet_fly() -{ - g_physMode = PHYS_FLY; - cout << "Fly mode\n"; -} - -extern "C" void bullet_ghost() -{ - g_physMode = PHYS_GHOST; - cout << "Ghost mode\n"; -} - -// Switch to the next physics mode -extern "C" void bullet_nextMode() -{ - switch(g_physMode) - { - case PHYS_WALK: - bullet_fly(); - break; - case PHYS_FLY: - bullet_ghost(); - break; - case PHYS_GHOST: - bullet_walk(); - break; - } -} - -// Warp the player to a specific location. We do not bother setting -// rotation, since it's completely irrelevant for collision detection, -// and doubly so since the collision mesh is a sphere. -extern "C" void bullet_movePlayer(float x, float y, float z) -{ - btTransform tr; - tr.setIdentity(); - tr.setOrigin(btVector3(x,y,z)); - g_playerObject->setWorldTransform(tr); -} - -// Request that the player moves in this direction -extern "C" void bullet_setPlayerDir(float x, float y, float z) -{ g_walkDirection.setValue(x,y,z); } - -// Get the current player position, after physics and collision have -// been applied. -extern "C" void bullet_getPlayerPos(float *x, float *y, float *z) -{ - *x = g_playerPosition.getX(); - *y = g_playerPosition.getY(); - *z = g_playerPosition.getZ(); -} - -void* copyBuffer(const void *buf, int elemSize, int len) -{ - int size = elemSize * len; - void *res = malloc(size); - memcpy(res, buf, size); - - return res; -} - -// Internal version that does not copy buffers -void createTriShape(int32_t numFaces, const void *triArray, - int32_t numVerts, const void *vertArray, - const float *trans, const float *matrix) -{ - // This struct holds the index and vertex buffers of a single - // trimesh. - btIndexedMesh im; - - // Set up the triangles - int numTriangles = numFaces / 3; - im.m_numTriangles = numTriangles; - im.m_triangleIndexStride = 6; // 3 indices * 2 bytes per short - im.m_triangleIndexBase = (unsigned char*)triArray; - - // Set up the vertices - im.m_numVertices = numVerts; - im.m_vertexStride = 12; // 4 bytes per float * 3 floats per vertex - im.m_vertexBase = (unsigned char*)vertArray; - - // Transform vertex values in vb according to 'trans' and 'matrix' - float *vb = (float*)im.m_vertexBase; - for(int i=0; iaddIndexedMesh(im, PHY_SHORT); -} - -// Define a cube with coordinates 0,0,0 - 1,1,1. -const float cube_verts[] = - { - 0,0,0, 1,0,0, 0,1,0, - 1,1,0, 0,0,1, 1,0,1, - 0,1,1, 1,1,1 - }; - -// Triangles of the cube. The orientation of each triange doesn't -// matter. -const short cube_tris[] = - { - // bottom side - 0, 1, 2, - 1, 2, 3, - // top side - 4, 5, 6, - 5, 6, 7, - // front side - 0, 4, 5, - 0, 1, 5, - // back side - 2, 3, 7, - 2, 6, 7, - // left side - 0, 2, 4, - 2, 4, 6, - // right side - 1, 3, 5, - 3, 5, 7 - }; - -const int cube_num_verts = 8; -const int cube_num_tris = 12; - -// Create a (trimesh) box with the given dimensions. Used for bounding -// boxes. TODO: I guess we should use the NIF-specified bounding box -// for this, not our automatically calculated one. -extern "C" void bullet_createBoxShape(float xmin, float ymin, float zmin, - float xmax, float ymax, float zmax, - float *trans, float *matrix) -{ - // Make a copy of the vertex buffer, since we need to change it - float *vbuffer = (float*)copyBuffer(cube_verts, 12, cube_num_verts); - - // Calculate the widths - float xwidth = xmax-xmin; - float ywidth = ymax-ymin; - float zwidth = zmax-zmin; - - // Transform the cube to (xmin,xmax) etc - float *vb = vbuffer; - for(int i=0; isetCollisionShape(shape); - obj->setWorldTransform(trafo); - g_dynamicsWorld->addCollisionObject(obj, MASK_STATIC, MASK_PLAYER); -} - -// Move the physics simulation 'delta' seconds forward in time -extern "C" void bullet_timeStep(float delta) -{ - TRACE("bullet_timeStep"); - // TODO: We might experiment with the number of time steps. Remember - // that the function also returns the number of steps performed. - g_dynamicsWorld->stepSimulation(delta,2); -} - -// Cleanup in the reverse order of creation/initialization -extern "C" void bullet_cleanup() -{ - // Remove the rigidbodies from the dynamics world and delete them - for (int i=g_dynamicsWorld->getNumCollisionObjects()-1; i>=0 ;i--) - { - btCollisionObject* obj = g_dynamicsWorld->getCollisionObjectArray()[i]; - btRigidBody* body = btRigidBody::upcast(obj); - - if (body && body->getMotionState()) - delete body->getMotionState(); - - g_dynamicsWorld->removeCollisionObject( obj ); - delete obj; - } - - delete g_dynamicsWorld; - delete g_solver; - delete g_broadphase; - delete g_dispatcher; - delete g_collisionConfiguration; -} diff --git a/old_d_version/bullet/cpp_player.cpp b/old_d_version/bullet/cpp_player.cpp deleted file mode 100644 index 14b04bf84..000000000 --- a/old_d_version/bullet/cpp_player.cpp +++ /dev/null @@ -1,381 +0,0 @@ -/* - OpenMW - The completely unofficial reimplementation of Morrowind - Copyright (C) 2008 Nicolay Korslund - Email: < korslund@gmail.com > - WWW: http://openmw.snaptoad.com/ - (see additional copyrights for this file below) - - This file (cpp_player.cpp) is part of the OpenMW package. - - OpenMW is distributed as free software: you can redistribute it - and/or modify it under the terms of the GNU General Public License - version 3, as published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - version 3 along with this program. If not, see - http://www.gnu.org/licenses/ . - - ---- - - Parts of this file is based on the kinematic character controller - demo included with the Bullet library. The copyright statement for - these parts follow: - - Bullet Continuous Collision Detection and Physics Library - Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any - damages arising from the use of this software. Permission is - granted to anyone to use this software for any purpose, including - commercial applications, and to alter it and redistribute it freely, - subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must - not claim that you wrote the original software. If you use this - software in a product, an acknowledgment in the product - documentation would be appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must - not be misrepresented as being the original software. - 3. This notice may not be removed or altered from any source - distribution. -*/ - - -// This file handles player-specific physics and collision detection - -// TODO: Later we might handle various physics modes, eg. dynamic -// (full physics), player_walk, player_fall, player_swim, -// player_float, player_levitate, player_ghost. These would be -// applicable to any object (through Monster script), allowing the -// physics code to be shared between NPCs, creatures and the player. - -// Variables used internally in this file. Once we make per-object -// player collision, these will be member variables. -bool g_touchingContact; -btVector3 g_touchingNormal; -btScalar g_currentStepOffset; -float g_stepHeight = 5; - -// Returns the reflection direction of a ray going 'direction' hitting -// a surface with normal 'normal' -btVector3 reflect (const btVector3& direction, const btVector3& normal) -{ return direction - (btScalar(2.0) * direction.dot(normal)) * normal; } - -// Returns the portion of 'direction' that is perpendicular to -// 'normal' -btVector3 perpComponent (const btVector3& direction, const btVector3& normal) -{ return direction - normal * direction.dot(normal); } - -btManifoldArray manifoldArray; - -// Callback used for collision detection sweep tests. It prevents self -// collision and is used in calls to convexSweepTest(). TODO: It might -// be enough to just set the filters on this. If we set the group and -// mask so that we only collide with static objects, self collision -// would never happen. The sweep test function should have had a -// version where you only specify the filters - I might add that -// myself. -class ClosestNotMeConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback -{ -public: - ClosestNotMeConvexResultCallback() - : btCollisionWorld::ClosestConvexResultCallback - (btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)) - { - m_collisionFilterGroup = g_playerObject-> - getBroadphaseHandle()->m_collisionFilterGroup; - - m_collisionFilterMask = g_playerObject-> - getBroadphaseHandle()->m_collisionFilterMask; - } - - btScalar addSingleResult(btCollisionWorld::LocalConvexResult& - convexResult, bool normalInWorldSpace) - { - if (convexResult.m_hitCollisionObject == g_playerObject) return 1.0; - - return ClosestConvexResultCallback::addSingleResult - (convexResult, normalInWorldSpace); - } -}; - -// Used to step up small steps and slopes. -void stepUp() -{ - // phase 1: up - btVector3 targetPosition = g_playerPosition + - btVector3(0.0, 0.0, g_stepHeight); - btTransform start, end; - - start.setIdentity (); - end.setIdentity (); - - // FIXME: Handle penetration properly - start.setOrigin (g_playerPosition + btVector3(0.0, 0.1, 0.0)); - end.setOrigin (targetPosition); - - ClosestNotMeConvexResultCallback callback; - g_dynamicsWorld->convexSweepTest (g_playerShape, start, end, callback); - - if (callback.hasHit()) - { - // we moved up only a fraction of the step height - g_currentStepOffset = g_stepHeight * callback.m_closestHitFraction; - g_playerPosition.setInterpolate3(g_playerPosition, targetPosition, - callback.m_closestHitFraction); - } - else - { - g_currentStepOffset = g_stepHeight; - g_playerPosition = targetPosition; - } -} - -void updateTargetPositionBasedOnCollision (const btVector3& hitNormal, - btVector3 &targetPosition) -{ - btVector3 movementDirection = targetPosition - g_playerPosition; - btScalar movementLength = movementDirection.length(); - - if (movementLength <= SIMD_EPSILON) - return; - - // Is this needed? - movementDirection.normalize(); - - btVector3 reflectDir = reflect(movementDirection, hitNormal); - reflectDir.normalize(); - - btVector3 perpendicularDir = perpComponent (reflectDir, hitNormal); - - targetPosition = g_playerPosition; - targetPosition += perpendicularDir * movementLength; -} - -// This covers all normal forward movement and collision, including -// walking sideways when hitting a wall at an angle. It does NOT -// handle walking up slopes and steps, or falling/gravity. -void stepForward(btVector3& walkMove) -{ - btVector3 originalDir = walkMove.normalized(); - - // If no walking direction is given, we still run the function. This - // allows moving forces to push the player around even if she is - // standing still. - if (walkMove.length() < SIMD_EPSILON) - originalDir.setValue(0.f,0.f,0.f); - - btTransform start, end; - btVector3 targetPosition = g_playerPosition + walkMove; - start.setIdentity (); - end.setIdentity (); - - btScalar fraction = 1.0; - btScalar distance2 = (g_playerPosition-targetPosition).length2(); - - if (g_touchingContact) - if (originalDir.dot(g_touchingNormal) > btScalar(0.0)) - updateTargetPositionBasedOnCollision (g_touchingNormal, targetPosition); - - int maxIter = 10; - - while (fraction > btScalar(0.01) && maxIter-- > 0) - { - start.setOrigin (g_playerPosition); - end.setOrigin (targetPosition); - - ClosestNotMeConvexResultCallback callback; - g_dynamicsWorld->convexSweepTest (g_playerShape, start, end, callback); - - fraction -= callback.m_closestHitFraction; - - if (callback.hasHit()) - { - // We moved only a fraction - btScalar hitDistance = (callback.m_hitPointWorld - g_playerPosition).length(); - // If the distance is further than the collision margin, - // move - if (hitDistance > 0.05) - g_playerPosition.setInterpolate3(g_playerPosition, targetPosition, - callback.m_closestHitFraction); - - updateTargetPositionBasedOnCollision(callback.m_hitNormalWorld, - targetPosition); - btVector3 currentDir = targetPosition - g_playerPosition; - distance2 = currentDir.length2(); - - if (distance2 <= SIMD_EPSILON) - break; - - currentDir.normalize(); - - if (currentDir.dot(originalDir) <= btScalar(0.0)) - break; - } - else - // we moved the whole way - g_playerPosition = targetPosition; - } -} - -void stepDown (btScalar dt) -{ - btTransform start, end; - - // phase 3: down - btVector3 step_drop = btVector3(0,0,g_currentStepOffset); - btVector3 gravity_drop = btVector3(0,0,g_stepHeight); - - btVector3 targetPosition = g_playerPosition - step_drop - gravity_drop; - - start.setIdentity (); - end.setIdentity (); - - start.setOrigin (g_playerPosition); - end.setOrigin (targetPosition); - - ClosestNotMeConvexResultCallback callback; - g_dynamicsWorld->convexSweepTest(g_playerShape, start, end, callback); - - if (callback.hasHit()) - // we dropped a fraction of the height -> hit floor - g_playerPosition.setInterpolate3(g_playerPosition, targetPosition, - callback.m_closestHitFraction); - else - // we dropped the full height - g_playerPosition = targetPosition; -} - -// Check if the player currently collides with anything, and adjust -// its position accordingly. Returns true if collisions were found. -bool recoverFromPenetration() -{ - bool penetration = false; - - // Update the collision pair cache - g_dispatcher->dispatchAllCollisionPairs(g_pairCache, - g_dynamicsWorld->getDispatchInfo(), - g_dispatcher); - - btScalar maxPen = 0.0; - for (int i = 0; i < g_pairCache->getNumOverlappingPairs(); i++) - { - manifoldArray.resize(0); - - btBroadphasePair* collisionPair = &g_pairCache->getOverlappingPairArray()[i]; - // Get the contact points - if (collisionPair->m_algorithm) - collisionPair->m_algorithm->getAllContactManifolds(manifoldArray); - - // And handle them - for (int j=0;jgetBody0() == - g_playerObject ? btScalar(-1.0) : btScalar(1.0); - - for (int p=0;pgetNumContacts();p++) - { - const btManifoldPoint &pt = manifold->getContactPoint(p); - - if (pt.getDistance() < 0.0) - { - // Pick out the maximum penetration normal and store - // it - if (pt.getDistance() < maxPen) - { - maxPen = pt.getDistance(); - g_touchingNormal = pt.m_normalWorldOnB * directionSign;//?? - - } - g_playerPosition += pt.m_normalWorldOnB * directionSign * - pt.getDistance() * btScalar(0.2); - - penetration = true; - } - } - } - } - - btTransform newTrans = g_playerObject->getWorldTransform(); - newTrans.setOrigin(g_playerPosition); - g_playerObject->setWorldTransform(newTrans); - - return penetration; -} - -// Callback called at the end of each simulation cycle. This is the -// main function is responsible for player movement. -void playerStepCallback(btDynamicsWorld* dynamicsWorld, btScalar timeStep) -{ - // The walking direction is set from D code each frame, and the - // final player position is read back from D code after the - // simulation. - btVector3 walkStep = g_walkDirection * timeStep; - - float len = walkStep.length(); - - // In walk mode, it shouldn't matter whether or not we look up or - // down. Rotate the vector back to the horizontal plane. - if(g_physMode == PHYS_WALK) - { - walkStep.setZ(0); - float len2 = walkStep.length(); - if(len2 > 0) - walkStep *= len/len2; - } - - // Get the player position - g_playerPosition = g_playerObject->getWorldTransform().getOrigin(); - - if(g_physMode == PHYS_GHOST) - { - // Ghost mode - just move, no collision - g_playerPosition += walkStep; - } - else - { - // Collision detection is active - - // Before moving, recover from current penetrations - int numPenetrationLoops = 0; - g_touchingContact = false; - while (recoverFromPenetration()) - { - numPenetrationLoops++; - g_touchingContact = true; - - // Make sure we don't stay here indefinitely - if (numPenetrationLoops > 4) - break; - } - - // recoverFromPenetration updates g_playerPosition and the - // collision mesh, so they are still in sync at this point - - // Next, do the walk. The following functions only updates - // g_playerPosition, they do not move the collision object. - - if(g_physMode == PHYS_WALK) - { - stepUp(); - stepForward(walkStep); - stepDown(timeStep); - } - else if(g_physMode == PHYS_FLY) - stepForward(walkStep); - else - cout << "WARNING: Unknown physics mode " << g_physMode << "!\n"; - } - - // Move the player collision mesh - btTransform xform = g_playerObject->getWorldTransform (); - xform.setOrigin (g_playerPosition); - g_playerObject->setWorldTransform (xform); -} diff --git a/old_d_version/bullet/cpp_scale.cpp b/old_d_version/bullet/cpp_scale.cpp deleted file mode 100644 index 3dfd34a30..000000000 --- a/old_d_version/bullet/cpp_scale.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/* - OpenMW - The completely unofficial reimplementation of Morrowind - Copyright (C) 2008 Nicolay Korslund - Email: < korslund@gmail.com > - WWW: http://openmw.snaptoad.com/ - - This file (cpp_scale.cpp) is part of the OpenMW package. - - OpenMW is distributed as free software: you can redistribute it - and/or modify it under the terms of the GNU General Public License - version 3, as published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - version 3 along with this program. If not, see - http://www.gnu.org/licenses/ . - -*/ - -// WARNING: This file does NOT work, and it is not used yet. - -class ScaleCallback : public btTriangleCallback -{ - btTriangleCallback *call; - float factor; - -public: - ScaleCallback(btTriangleCallback *c, float f) - { call = c; factor = f; } - - void processTriangle(btVector3 *tri, int partid, int triindex) - { - btVector3 vecs[3]; - vecs[0] = tri[0]*factor; - vecs[1] = tri[1]*factor; - vecs[2] = tri[2]*factor; - - call->processTriangle(vecs, partid, triindex); - } -}; - -// This class is used to uniformly scale a triangle mesh by a -// factor. It wraps around an existing shape and does not copy the -// data. -class ScaleShape : public btConcaveShape -{ - btConcaveShape* child; - float factor, fact3, facthalf; - -public: - - ScaleShape(btConcaveShape* ch, float ft) - { - child = ch; - factor = ft; - fact3 = factor*factor*factor; - facthalf = factor*0.5; - } - - void calculateLocalInertia(btScalar mass,btVector3& inertia) const - { - btVector3 tmpInertia; - child->calculateLocalInertia(mass,tmpInertia); - inertia = tmpInertia * fact3; - } - - const char* getName()const { return "ScaleShape"; } - - void getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const - { - child->getAabb(t,aabbMin,aabbMax); - btVector3 aabbCenter = (aabbMax+aabbMin)*0.5; - btVector3 scaledAabbHalfExtends = (aabbMax-aabbMin)*facthalf; - - aabbMin = aabbCenter - scaledAabbHalfExtends; - aabbMax = aabbCenter + scaledAabbHalfExtends; - } - - void processAllTriangles(btTriangleCallback *callback,const btVector3& aabbMin,const btVector3& aabbMax) const - { - ScaleCallback scb(callback, factor); - - child->processAllTriangles(&scb, aabbMin, aabbMax); - } - - void setLocalScaling(const btVector3& scaling) - { child->setLocalScaling(scaling); } - - const btVector3& getLocalScaling() const - { return child->getLocalScaling(); } - - int getShapeType() const - { return TRIANGLE_MESH_SHAPE_PROXYTYPE; } -}; diff --git a/old_d_version/core/config.d b/old_d_version/core/config.d deleted file mode 100644 index 55daa2be1..000000000 --- a/old_d_version/core/config.d +++ /dev/null @@ -1,437 +0,0 @@ -/* - OpenMW - The completely unofficial reimplementation of Morrowind - Copyright (C) 2008 Nicolay Korslund - Email: < korslund@gmail.com > - WWW: http://openmw.snaptoad.com/ - - This file (config.d) is part of the OpenMW package. - - OpenMW is distributed as free software: you can redistribute it - and/or modify it under the terms of the GNU General Public License - version 3, as published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - version 3 along with this program. If not, see - http://www.gnu.org/licenses/ . - - */ - -module core.config; - -import std.string; -import std.file; -import std.path; -import std.stdio; - -import monster.monster; -import monster.util.string; - -import core.inifile; -import core.filefinder; - -import sound.audio; - -import input.keys; -import input.ois; - -import ogre.ogre; - -ConfigManager config; - -/* - * Structure that handles all user adjustable configuration options, - * including things like file paths, plugins, graphics resolution, - * game settings, window positions, etc. It is also responsible for - * reading and writing configuration files, for importing settings - * from Morrowind.ini and for configuring OGRE. It doesn't currently - * DO all of this, but it is supposed to in the future. - */ - -struct ConfigManager -{ - MonsterObject *mo; - - IniWriter iniWriter; - - // Mouse sensitivity - float *mouseSensX; - float *mouseSensY; - bool *flipMouseY; - - // Ogre configuration - bool showOgreConfig; // The configuration setting - // The actual result, overridable by a command line switch, and also - // set to true if firstRun is true. - bool finalOgreConfig; - - // Other settings - bool firstRun; - - // Set to true if sound is completely disabled - bool noSound = false; - - // Number of current screen shot. Saved upon exit, so that shots - // from separate sessions don't overwrite each other. - int screenShotNum; - - // Game files to load (max 255) - char[][] gameFiles; - - // Directories - char[] dataDir; - char[] esmDir; - char[] bsaDir; - char[] sndDir; - char[] fontDir; - char[] musDir; // Explore music - char[] musDir2; // Battle music - - // Configuration file - char[] confFile = "openmw.ini"; - - // Cell to load at startup - char[] defaultCell; - - // These set the volume to a new value and updates all sounds to - // take notice. - void setMusicVolume(float vol) - { - stack.pushFloat(vol); - mo.call("setMusicVolume"); - } - float getMusicVolume() - { return mo.getFloat("musicVolume"); } - - void setSfxVolume(float vol) - { - stack.pushFloat(vol); - mo.call("setSfxVolume"); - } - float getSfxVolume() - { return mo.getFloat("sfxVolume"); } - - void setMainVolume(float vol) - { - stack.pushFloat(vol); - mo.call("setMainVolume"); - } - float getMainVolume() - { return mo.getFloat("mainVolume"); } - - // Initialize the config manager. Send a 'true' parameter to reset - // all keybindings to the default. A lot of this stuff will be moved - // to script code at some point. In general, all input mechanics and - // distribution of key events should happen in native code, while - // all setup and control should be handled in script code. - void initialize(bool reset = false) - { - // Initialize variables from Monster. - assert(mo !is null); - mouseSensX = mo.getFloatPtr("mouseSensX"); - mouseSensY = mo.getFloatPtr("mouseSensY"); - flipMouseY = mo.getBoolPtr("flipMouseY"); - - // Initialize the key binding manager - keyBindings.initKeys(); - - /* Disable this at the moment. It's a good idea to put - configuration in a central location, but it's useless as long - as Ogre expects to find it's files in the current working - directory. The best permanent solution would be to let the - locations of ogre.cfg and plugins.cfg be determined by - openmw.ini - I will fix that later. - - version(Posix) - { - if(!exists(confFile)) - confFile = expandTilde("~/.openmw/openmw.ini"); - } - */ - - readIni(reset); - } - - // Read config from morro.ini, if it exists. The reset parameter is - // set to true if we should use default key bindings instead of the - // ones from the config file. - void readIni(bool reset) - { - // Read configuration file, if it exists. - IniReader ini; - - ini.readFile(confFile); - - screenShotNum = ini.getInt("General", "Screenshots", 0); - float mainVolume = saneVol(ini.getFloat("Sound", "Main Volume", 0.7)); - float musicVolume = saneVol(ini.getFloat("Sound", "Music Volume", 0.5)); - float sfxVolume = saneVol(ini.getFloat("Sound", "SFX Volume", 0.5)); - bool useMusic = ini.getBool("Sound", "Enable Music", true); - - - lightConst = ini.getInt("LightAttenuation", "UseConstant", 0); - lightConstValue = ini.getFloat("LightAttenuation", "ConstantValue", 0.0); - - lightLinear = ini.getInt("LightAttenuation", "UseLinear", 1); - lightLinearMethod = ini.getInt("LightAttenuation", "LinearMethod", 1); - lightLinearValue = ini.getFloat("LightAttenuation", "LinearValue", 3.0); - lightLinearRadiusMult = ini.getFloat("LightAttenuation", "LinearRadiusMult", 1.0); - - lightQuadratic = ini.getInt("LightAttenuation", "UseQuadratic", 0); - lightQuadraticMethod = ini.getInt("LightAttenuation", "QuadraticMethod", 2); - lightQuadraticValue = ini.getFloat("LightAttenuation", "QuadraticValue", 16.0); - lightQuadraticRadiusMult = ini.getFloat("LightAttenuation", "QuadraticRadiusMult", 1.0); - - lightOutQuadInLin = ini.getInt("LightAttenuation", "OutQuadInLin", 0); - - - *mouseSensX = ini.getFloat("Controls", "Mouse Sensitivity X", 0.2); - *mouseSensY = ini.getFloat("Controls", "Mouse Sensitivity Y", 0.2); - *flipMouseY = ini.getBool("Controls", "Flip Mouse Y Axis", false); - - mo.setFloat("mainVolume", mainVolume); - mo.setFloat("musicVolume", musicVolume); - mo.setFloat("sfxVolume", sfxVolume); - mo.setBool("useMusic", useMusic); - - defaultCell = ini.getString("General", "Default Cell", "Assu"); - - firstRun = ini.getBool("General", "First Run", true); - showOgreConfig = ini.getBool("General", "Show Ogre Config", false); - - // This flag determines whether we will actually show the Ogre - // config dialogue. The EITHER of the following are true, the - // config box will be shown: - // - The program is being run for the first time - // - The "Show Ogre Config" option in openmw.ini is set. - // - The -oc option is specified on the command line - // - The file ogre.cfg is missing - - finalOgreConfig = showOgreConfig || firstRun || - !exists("ogre.cfg"); - - // Set default key bindings first. - with(keyBindings) - { - // Bind some default keys - bind(Keys.MoveLeft, KC.A, KC.LEFT); - bind(Keys.MoveRight, KC.D, KC.RIGHT); - bind(Keys.MoveForward, KC.W, KC.UP); - bind(Keys.MoveBackward, KC.S, KC.DOWN); - bind(Keys.MoveUp, KC.LSHIFT); - bind(Keys.MoveDown, KC.LCONTROL); - - bind(Keys.MainVolUp, KC.ADD); - bind(Keys.MainVolDown, KC.SUBTRACT); - bind(Keys.MusVolDown, KC.N1); - bind(Keys.MusVolUp, KC.N2); - bind(Keys.SfxVolDown, KC.N3); - bind(Keys.SfxVolUp, KC.N4); - bind(Keys.Mute, KC.M); - - bind(Keys.Fullscreen, KC.F); - - bind(Keys.ToggleBattleMusic, KC.SPACE); - bind(Keys.PhysMode, KC.T); - bind(Keys.Nighteye, KC.N); - bind(Keys.ToggleGui, KC.Mouse1); - bind(Keys.Console, KC.F1, KC.GRAVE); - bind(Keys.Debug, KC.G); - - bind(Keys.Pause, KC.PAUSE, KC.P); - bind(Keys.ScreenShot, KC.SYSRQ); - bind(Keys.Exit, KC.Q, KC.ESCAPE); - } - - // Unless the ini file was missing or we were asked to reset all - // keybindings to default, replace all present bindings with the - // values from the ini. - if(!reset && ini.wasRead) - { - // Read key bindings - for(int i; i - WWW: http://openmw.snaptoad.com/ - - This file (filefinder.d) is part of the OpenMW package. - - OpenMW is distributed as free software: you can redistribute it - and/or modify it under the terms of the GNU General Public License - version 3, as published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - version 3 along with this program. If not, see - http://www.gnu.org/licenses/ . - - */ - -module core.filefinder; - -import std.file; -import std.string; - -import monster.util.string; -import monster.util.aa; - -import core.memory; - -import std.stdio; - -class FileFinderException : Exception -{ - this(char[] msg, char[] ext, char[] dir) - { - if(ext.length) super(format("FileFinder for %s files in %s: %s", ext, dir, msg)); - else super(format("FileFinder for %s: %s", dir, msg)); - } -} - -// Do we traverse directories recursively? Default is yes. -enum Recurse { Yes, No } - -// The file finder is used to list all files in a directory so we can -// look up files without searching the filesystem each time. It is -// case insensitive on all platforms, and transparently converts to -// the right directory separator character (\ or /). We might extend -// it later with code from other projects. -class FileFinder -{ - private: - char[][] files; // Use GC for this, it's not too big and we don't - // have to manage roots pointing to the filenames. - HashTable!(char[], int, ESMRegionAlloc, FilenameHasher) lookup; - - char[] dir; // Base directory to search - char[] ext; // Extensions to pick out - - void fail(char[] err) - { - throw new FileFinderException(err, ext, dir); - } - - // Removes the part of a path that is stored in 'dir' - char[] removeDir(char[] path) - { - //TODO: Should this be case insensitive? - assert(path[0..dir.length] == dir); - - return path[dir.length..$]; - } - - void insert(char[] filename) - { - // Only keep the part of the filename not given in 'dir'. - char[] name = removeDir(filename); - - if(!name.iEnds(ext)) return; - - // We start counting from 1 - uint newVal = files.length+1; - - // Insert it, or get the old value if it already exists - uint oldVal = lookup[name, newVal]; - if(oldVal != newVal) - fail("Already have " ~ name ~ "\nPreviously inserted as " ~ files[oldVal-1]); - - // Store it - files ~= filename; - } - - public: - - static char[] addSlash(char[] dir) - { - // Add a trailing slash - version(Windows) if(!dir.ends("\\")) dir ~= '\\'; - version(Posix) if(!dir.ends("/")) dir ~= '/'; - return dir; - } - - int length() { return lookup.length; } - - this(char[] dir, char[] ext = null, Recurse r = Recurse.Yes) - in - { - if(!dir.length) fail("'dir' can not be empty"); - } - out - { - assert(files.length == lookup.length); - } - body - { - // Add a trailing slash - dir = addSlash(dir); - - this.dir = dir; - - if(ext.length && ext[0] != '.') ext = "." ~ ext; - this.ext = ext; - - bool callback(DirEntry* de) - { - if (de.isdir) - { - if(r == Recurse.Yes) - listdir(de.name, &callback); - } - else - insert(de.name); - return true; - } - - try listdir(dir, &callback); - catch(FileException e) - fail(e.toString); - } - - char[] opIndex(int i) { return files[i-1]; } - - int opIndex(char[] file) - { - int i; - - // Get value if it exists - if(lookup.inList(file, i)) - return i; - return 0; - } - - bool has(char[] file) - { - return lookup.inList(file); - } - - int opApply(int delegate(ref char[]) del) - { - int res = 0; - - foreach(char[] s; files) - { - char[] tmp = removeDir(s); - res = del(tmp); - if(res) break; - } - return res; - } - - char[] toString() - { - char[] result; - foreach(char[] s; this) - result ~= s ~ "\n"; - return result; - } -} - -// Hash functions that does not differentiate between linux and -// windows file names. This means that it is case insensitive, and -// treats '\' and '/' as the same character. Only needed in linux, in -// windows just use CITextHasher. -version(Posix) -struct FilenameHasher -{ - static const char conv = 'a'-'A'; - - static int isEqual(char[] aa, char[] bb) - { - if(aa.length != bb.length) return 0; - - foreach(int i, char a; aa) - { - char b = bb[i]; - - if(a == b) - continue; - - // Convert both to lowercase and "/ case" - if(a <= 'Z' && a >= 'A') a += conv; - else if(a == '\\') a = '/'; - if(b <= 'Z' && b >= 'A') b += conv; - else if(b == '\\') b = '/'; - - if(a != b) return 0; - } - - // No differences were found - return 1; - } - - static uint hash(char[] s) - { - uint hash; - foreach (char c; s) - { - if(c <= 'Z' && c >= 'A') c += conv; - else if(c == '\\') c = '/'; - hash = (hash * 37) + c; - } - return hash; - } -} - -version(Windows) alias CITextHash FilenameHasher; diff --git a/old_d_version/esm/defs.d b/old_d_version/esm/defs.d deleted file mode 100644 index 337db02fc..000000000 --- a/old_d_version/esm/defs.d +++ /dev/null @@ -1,187 +0,0 @@ -/* - OpenMW - The completely unofficial reimplementation of Morrowind - Copyright (C) 2008 Nicolay Korslund - Email: < korslund@gmail.com > - WWW: http://openmw.snaptoad.com/ - - This file (defs.d) is part of the OpenMW package. - - OpenMW is distributed as free software: you can redistribute it - and/or modify it under the terms of the GNU General Public License - version 3, as published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - version 3 along with this program. If not, see - http://www.gnu.org/licenses/ . - - */ - -module esm.defs; - -public import std.string; -public import monster.util.string; -import monster.monster; - -/* - * Types and definitions related to parsing esm and esp files - */ - -alias char[4] NAME; -alias char[32] NAME32; -alias char[256] NAME256; - -union Color -{ - align(1) struct - { - ubyte red, green, blue, alpha; - } - - ubyte[4] array; - uint value; - - char[] toString() { return format("RGBA:%s", array); } -} -static assert(Color.sizeof==4); - -// State of a record struct -enum LoadState - { - Unloaded, // This record is not loaded, it has just been - // referenced. - Loaded, // This record has been loaded by the current file - Previous // The record has been loaded by a previous file - - // Finalized - might be the case for some record types, but I - // don't know if this actual state value would be used for - // anything. - } - -enum VarType { Unknown, None, Short, Int, Long, Float, String, Ignored } - -enum SpellSchool : int - { - Alteration = 0, - Conjuration = 1, - Destruction = 2, - Illusion = 3, - Mysticism = 4, - Restoration = 5, - Length - } - -enum Attribute : int - { - Strength = 0, - Intelligence = 1, - Willpower = 2, - Agility = 3, - Speed = 4, - Endurance = 5, - Personality = 6, - Luck = 7, - Length - } - -enum SkillEnum : int - { - Block = 0, - Armorer = 1, - MediumArmor = 2, - HeavyArmor = 3, - BluntWeapon = 4, - LongBlade = 5, - Axe = 6, - Spear = 7, - Athletics = 8, - Enchant = 9, - Destruction = 10, - Alteration = 11, - Illusion = 12, - Conjuration = 13, - Mysticism = 14, - Restoration = 15, - Alchemy = 16, - Unarmored = 17, - Security = 18, - Sneak = 19, - Acrobatics = 20, - LightArmor = 21, - ShortBlade = 22, - Marksman = 23, - Mercantile = 24, - Speechcraft = 25, - HandToHand = 26, - Length - } - -// Shared between SPEL (Spells), ALCH (Potions) and ENCH (Item -// enchantments) records -align(1) struct ENAMstruct -{ - // Magical effect - short effectID; // ID of magic effect - - // Which skills/attributes are affected (for restore/drain spells etc.) - byte skill, attribute; // -1 if N/A - - // Other spell parameters - int range; // 0 - self, 1 - touch, 2 - target - int area, duration, magnMin, magnMax; - - static assert(ENAMstruct.sizeof==24); -} - -// Common stuff for all the load* structs -template LoadTT(T) -{ - LoadState state; - char[] name, id; - - MonsterObject *proto; - static MonsterClass mc; - - void makeProto(char[] clsName = null) - { - // Set up a prototype object - if(mc is null) - { - // Use the template type name as the Monster class name if - // none is specified. - if(clsName == "") - { - clsName = typeid(T).toString; - - // Remove the module name - int i = clsName.rfind('.'); - if(i != -1) - clsName = clsName[i+1..$]; - } - - // All the game objects are in the 'game' package - clsName = "game." ~ clsName; - mc = vm.load(clsName); - } - - proto = mc.createObject(); - - proto.setString8("id", id); - proto.setString8("name", name); - - static if(is(typeof(data.weight) == float)) - { - proto.setFloat("weight", data.weight); - proto.setInt("value", data.value); - } - - static if(is(typeof(data.enchant)==int)) - proto.setInt("enchant", data.enchant); - } -} - -template LoadT() { mixin LoadTT!(typeof(*this)); } diff --git a/old_d_version/esm/esmmain.d b/old_d_version/esm/esmmain.d deleted file mode 100644 index 2340ea59e..000000000 --- a/old_d_version/esm/esmmain.d +++ /dev/null @@ -1,167 +0,0 @@ -/* - OpenMW - The completely unofficial reimplementation of Morrowind - Copyright (C) 2008 Nicolay Korslund - Email: < korslund@gmail.com > - WWW: http://openmw.snaptoad.com/ - - This file (esmmain.d) is part of the OpenMW package. - - OpenMW is distributed as free software: you can redistribute it - and/or modify it under the terms of the GNU General Public License - version 3, as published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - version 3 along with this program. If not, see - http://www.gnu.org/licenses/ . - - */ - -module esm.esmmain; - -public import esm.records; - -import ogre.ogre; - -/* This file is the main module for loading from ESM, ESP and ESS - files. It stores all the data in the appropriate data structures - for later referal. TODO: Put this in a class or whatever? Nah, we - definately only need one structure like this at any one - time. However, we have to deal with unloading and reloading it, - even though that should be the exceptional case (change of plugins, - etc), not the rule (loading a savegame should not alter the base - data set, I think, but it's to early do decide.)*/ - -// Load a set of esm and esp files. For now, we just traverse in the -// order given. Later, we should sort these into 'masters' and -// 'plugins', because esms are always supposed to be loaded -// first. TODO: I'm not sure if I should load all these in one -// function. Do we need to be able to respond to errors in each file? -// Nah, if anything fails, give a general error message, remove the -// file from the list and try again. We have to be able to get a list -// of which files depend upon which, though... this can be done before -// this function is called. -void loadTESFiles(char[][] files) -{ - // Set up all the lists to hold our data - initializeLists(); - - foreach(char[] filename; files) - { - esFile.open(filename, esmRegion); - while(esFile.hasMoreRecs()) - { - uint flags; - - // Read record header - char[] recName = esFile.getRecName(); - esFile.getRecHeader(flags); - - if(flags & RecordFlags.Unknown) - esFile.fail(format("UNKNOWN record flags: %xh", flags)); - - loadRecord(recName); - } - - // We have to loop through the lists and check for broken - // references at this point, and if all forward references were - // loaded. There might be other end-of-file things to do also. - endFiles(); - } - - esFile.close(); - - // Put all inventory items into one list - items.addList(appas, ItemType.Apparatus); - items.addList(lockpicks, ItemType.Pick); - items.addList(probes, ItemType.Probe); - items.addList(repairs, ItemType.Repair); - items.addList(lights, ItemType.Light); - items.addList(ingreds, ItemType.Ingredient); - items.addList(potions, ItemType.Potion); - items.addList(armors, ItemType.Armor); - items.addList(weapons, ItemType.Weapon); - items.addList(books, ItemType.Book); - items.addList(clothes, ItemType.Clothing); - items.addList(miscItems, ItemType.Misc); - items.addList(itemLists, ItemType.ItemList); // Leveled item lists - - // Same with all actors - actors.addList(creatures, ItemType.Creature); - actors.addList(creatureLists, ItemType.CreatureList); - actors.addList(npcs, ItemType.NPC); - - // Finally, add everything that might be looked up in a cell into - // one list - cellRefs.addList(items); - cellRefs.addList(actors); - cellRefs.addList(doors, ItemType.Door); - cellRefs.addList(activators, ItemType.Activator); - cellRefs.addList(statics, ItemType.Static); - cellRefs.addList(containers, ItemType.Container); - - // Check that all references are resolved - items.endMerge(); - actors.endMerge(); - cellRefs.endMerge(); - - // Put all NPC dialogues into the hyperlink list - foreach(char[] id, ref Dialogue dl; dialogues.names) - hyperlinks.add(id, &dl); - - // Finally, sort the hyperlink lists - hyperlinks.sort(); -} - -// Contains the small bits of information that we currently extract -// from savegames. -struct PlayerSaveInfo -{ - char[] cellName; - char[] playerName; - Placement pos; -} - -// Load a TES savegame file (.ess). Currently VERY limited, reads the -// player's cell name and position -PlayerSaveInfo importSavegame(char[] file) -{ - PlayerSaveInfo pi; - - esFile.open(file, esmRegion); - scope(exit) esFile.close(); - - if(esFile.getFileType != FileType.Ess) - throw new TES3FileException(file ~ " is not a savegame"); - - with(esFile.saveData) - { - pi.cellName = stripz(cell); - pi.playerName = stripz(player); - } - - with(esFile) - { - while(hasMoreRecs()) - { - if(isNextHRec("REFR")) - { - while(hasMoreSubs()) - { - getSubName(); - if(retSubName() == "DATA") - readHExact(&pi.pos, pi.pos.sizeof); - else - skipHSub(); - } - } - else - skipHRecord(); - } - } - return pi; -} diff --git a/old_d_version/esm/filereader.d b/old_d_version/esm/filereader.d deleted file mode 100644 index 4d1dfb2d9..000000000 --- a/old_d_version/esm/filereader.d +++ /dev/null @@ -1,783 +0,0 @@ -/* - OpenMW - The completely unofficial reimplementation of Morrowind - Copyright (C) 2008 Nicolay Korslund - Email: < korslund@gmail.com > - WWW: http://openmw.snaptoad.com/ - - This file (filereader.d) is part of the OpenMW package. - - OpenMW is distributed as free software: you can redistribute it - and/or modify it under the terms of the GNU General Public License - version 3, as published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - version 3 along with this program. If not, see - http://www.gnu.org/licenses/ . - - */ - -module esm.filereader; - -private: -import std.stdio; -import std.stream; -import std.string; - -import util.regions; -import util.utfconvert; -import monster.util.string; -import core.resource; - -import esm.listkeeper; -import esm.defs; - -public: - -/* - * Exception class for TES3File - */ - -class TES3FileException: Exception -{ - this(char[] msg) {super("Error reading TES3 file: " ~ msg);} - this() {this("Unknown error");} -} - -// Some flags are in use that we don't know. But we don't really know -// any of them. -enum RecordFlags : uint - { - Flag6 = 0x20, // Eg. adventurers_v2.0.esp (only once per file?) - Persistent = 0x400, - Flag13 = 0x1000, // Eg. Astarsis_BR.esm (several times per file?) - Blocked = 0x2000, - - Unknown = 0xffffffff - 0x3420 - } - -enum FileType - { - Unknown, - Esp, Plugin = Esp, - Esm, Master = Esm, - Ess, Savegame = Ess - } - -// Special files -enum SpecialFile - { - Other, - Morrowind, - Tribunal, - Bloodmoon - } - -enum Version { Unknown, v12, v13 } - -// This struct should contain enough data to put a TES3File object -// back into a specific file position and state. We use it to save the -// "position" of objects in a file (eg. a cell), so we can return -// there later and continue where we stopped (eg. when we want to load -// that specific cell.) -struct TES3FileContext -{ - char[] filename; - uint leftRec, leftSub; - ulong leftFile; - NAME recName, subName; - FileType type; - Version ver; - - ulong filepos; -} - -/** - * Instance used to read TES3 files. Since we will only be reading one - * file at a time, we might as well make one global instance. - */ -TES3File esFile; - -/** - * This struct reads an Elder Scrolls 3 file (esp, esm or ess) - * - * Makes heavy use of private variables to represent current - * state. - * - * Relevant exceptions are - * TES3FileException - error interpreting file - * StreamFileException - file IO error - */ -struct TES3File -{ - private: - BufferedFile file;// Input file - - // These are only used by getRecHeader and getSubHeader for - // asserting the file's integrity. - ulong leftFile; // Number of unread bytes in file - uint leftRec; // Number of unread bytes in record - - // This is used by sub-record readers for integrity checking. - uint leftSub; // Number of bytes in subrecord - - // Name of current record and current sub-record. - NAME recName, subName; - - char[] filename; // Filename - FileType type; // File type - Version ver; // File format version - char[] author; // File author (max 32 bytes (with null?)) - char[] desc; // Description (max 256 bytes (ditto?)) - uint records; // Number of records in the file (doesn't seem to be right?) - SpecialFile spf; // Is this a file we have to treat in a special way? - - struct _mast - { - char[] name; // File name of an esm master for this file - ulong size; // The master file's size in bytes (used for - // version control) - } - - // List of esm masters for this file. For savegames this list also - // contains all plugins. - _mast masters[]; - - - // TES3.HEDR, file header struct - align(1) struct HEDRstruct - { - union - { - float ver; // File format version, 1.2 and 1.3 supported. - uint verHex; // 1.2 = 0x3f99999a, 1.3 = 0x3fa66666 - } - int type; // 0=esp, 1=esm, 32=ess - NAME32 author; // Author's name - NAME256 desc; // File description blurb - uint records; // Number of records in file (?) - } - - static assert(HEDRstruct.sizeof == 300); - - // Which memory region to use for allocations. - RegionManager region; - - public: - - // A struct found in the headers of savegame files. Contains quick - // information to get us going, like the cell name and the player - // name. - struct _saveData - { - float[6] unknown; - char[64] cell; // Cell name - float unk2; // Unknown value - char[32] player; // Player name - } - static assert(_saveData.sizeof == 124); - _saveData saveData; - - // Get file information - char[] getFilename() { return filename; } - ulong getFileSize() { return file.size; } - ulong getPosition() { return file.position; } - SpecialFile getSpecial() { return spf; } - - char[] retSubName() { return subName; } - - bool isVer12() { return ver == Version.v12;} - bool isVer13() { return ver == Version.v13;} - FileType getFileType() { return type; } - _mast[] getMasters() { return masters; } - uint getRecords() { return records; } - char[] getAuthor() { return author; } - RegionManager getRegion() { return region; } - - // Store the current file state (position, file name, version, debug - // info). The info should be enough to get us back on track for - // reading from a file, without having to reread the header or any - // previous records. - void getContext(ref TES3FileContext c) - { - c.filename = filename; - c.leftFile = leftFile; - c.leftRec = leftRec; - c.leftSub = leftSub; - c.recName[] = recName; - c.subName[] = subName; - c.type = type; - c.ver = ver; - c.filepos = file.position; - } - - // Opens the file if it is not already opened. A region manager has - // to be specified. - void restoreContext(TES3FileContext c, RegionManager r) - { - if(filename != c.filename) - openFile(c.filename, r); - file.seekSet(cast(long)c.filepos); - - // File is now open, copy state information - filename = c.filename; - leftFile = c.leftFile; - leftRec = c.leftRec; - leftSub = c.leftSub; - recName[] = c.recName; - subName[] = c.subName; - type = c.type; - ver = c.ver; - } - - // Open a new file and assign a region - private void openFile(char[] filename, RegionManager r) - { - close(); - debug writefln("Opening file"); - if(file is null) file = new BufferedFile(new File()); - file.open(filename); - - region = r; - } - - void open(char[] filename, RegionManager r) - { - uint flags; - - debug writefln("openFile(%s, %s)", filename, r); - openFile(filename, r); - - if(iEnds(filename, "Morrowind.esm")) spf = SpecialFile.Morrowind; - else if(iEnds(filename, "Tribunal.esm")) spf = SpecialFile.Tribunal; - else if(iEnds(filename, "Bloodmoon.esm")) spf = SpecialFile.Bloodmoon; - else spf = SpecialFile.Other; - - debug writefln("Reading header"); - - // Do NOT .dup this filename, since it is referenced outside the - // GC's reach and might be deleted. - this.filename = filename; - - leftFile = file.size; - - // First things first - if(getRecName() != "TES3") - fail("Not a valid Morrowind file"); - - // Record header - getRecHeader(flags); - if(flags) - writefln("WARNING: Header flags are non-zero"); - - // Read and analyse the header data - HEDRstruct hedr; - readHNExact(&hedr, hedr.sizeof, "HEDR"); - - // The float hedr.ver signifies the file format version. It can - // take on these two values: - // 0x3f99999a = 1.2 - // 0x3fa66666 = 1.3 - if( hedr.verHex == 0x3f99999a ) - ver = Version.v12; - else if( hedr.verHex == 0x3fa66666 ) - ver = Version.v13; - else - { - ver = Version.Unknown; - writefln("WARNING: Unknown version: ", hedr.ver); - writefln(" Hex: %X h", *(cast(uint*)&hedr.ver)); - } - - switch(hedr.type) - { - case 0: type = FileType.Esp; break; - case 1: type = FileType.Esm; break; - case 32: type = FileType.Ess; break; - default: - type = FileType.Unknown; - writefln("WARNING: Unknown file type: ", hedr.type); - } - - author = region.copy(stripz(hedr.author)); - desc = region.copy(stripz(hedr.desc)); - records = hedr.records; - - masters = null; - // Reads a MAST and a DATA fields - while(isNextSub("MAST")) - { - _mast ma; - - // MAST entry - master file name - ma.name = getHString(); - - // DATA entry - master file size - ma.size = getHNUlong("DATA"); - - // Add to the master list! - masters ~= ma; - } - - if(type == FileType.Savegame) - { - // Savegame-related data - - // Cell name, player name and player position - readHNExact(&saveData, 124, "GMDT"); - - // Contains eg. 0xff0000, 0xff00, 0xff, 0x0, 0x20. No idea. - getSubNameIs("SCRD"); - skipHSubSize(20); - - // Screenshot. Fits with 128x128x4 bytes - getSubNameIs("SCRS"); - skipHSubSize(65536); - } - } - - // Close the file. We do not clear any object data at this point. - void close() - { - debug writefln("close()"); - if(file !is null) - file.close(); - leftFile = leftRec = leftSub = 0; - debug writefln("Clearing strings"); - - recName[] = '\0'; - subName[] = '\0'; - - // This tells restoreContext() that we have to reopen the file - filename = null; - - debug writefln("exit close()"); - } - - /* - * Error reporting - */ - - void fail(char[] msg) - { - throw new TES3FileException - (msg ~ "\nFile: " ~ filename ~ "\nRecord name: " ~ recName - ~ "\nSubrecord name: " ~ subName); - } - - /************************************************************************ - * - * Highest level readers, reads a name and looks it up in the given - * list. - * - ************************************************************************/ - - // This should be more than big enough for references. - private char lookupBuffer[200]; - - // Get a temporary string. This is faster and more memory efficient - // that the other string functions (because it is allocation free), - // but the returned string is only valid until tmpHString() is - // called again. - char[] tmpHString() - { - getSubHeader(); - assert(leftSub <= lookupBuffer.length, "lookupBuffer wasn't large enough"); - - // Use this to test the difference in memory consumption. - return getString(lookupBuffer[0..leftSub]); - } - - // These are used for file lookups - MeshIndex getMesh() - { getSubNameIs("MODL"); return resources.lookupMesh(tmpHString()); } - SoundIndex getSound() - { getSubNameIs("FNAM"); return resources.lookupSound(tmpHString()); } - IconIndex getIcon(char[] s = "ITEX") - { getSubNameIs(s); return resources.lookupIcon(tmpHString()); } - TextureIndex getTexture() - { getSubNameIs("DATA"); return resources.lookupTexture(tmpHString()); } - - // The getO* functions read optional records. If they are not - // present, return null. - - MeshIndex getOMesh() - { return isNextSub("MODL") ? resources.lookupMesh(tmpHString()) : MeshIndex.init; } - /* - SoundIndex getOSound() - { return isNextSub("FNAM") ? resources.lookupSound(tmpHString()) : SoundIndex.init; } - */ - IconIndex getOIcon() - { return isNextSub("ITEX") ? resources.lookupIcon(tmpHString()) : IconIndex.init; } - TextureIndex getOTexture(char[] s="TNAM") - { return isNextSub(s) ? resources.lookupTexture(tmpHString()) : TextureIndex.init; } - - // Reference with name s - template getHNPtr(Type) - { - Type* getHNPtr(char[] s, ListKeeper list) - { getSubNameIs(s); return cast(Type*) list.lookup(tmpHString()); } - } - - // Reference, only get header - template getHPtr(Type) - { - Type* getHPtr(ListKeeper list) - { return cast(Type*) list.lookup(tmpHString()); } - } - - // Optional reference with name s - template getHNOPtr(Type) - { - Type* getHNOPtr(char[] s, ListKeeper list) - { return isNextSub(s) ? cast(Type*)list.lookup(tmpHString()) : null; } - } - - /************************************************************************ - * - * Somewhat high level reading methods. Knows about headers and - * leftFile/leftRec/leftSub. - * - ************************************************************************/ - - // "Automatic" versions. Sets and returns recName and subName and - // updates leftFile/leftRec. - char[] getRecName() - { - if(!hasMoreRecs()) - fail("No more records, getRecName() failed"); - getName(recName); - leftFile-= 4; - return recName; - } - - // This is specially optimized for LoadINFO - bool isEmptyOrGetName() - { - if(leftRec) - { - file.readBlock(subName.ptr, 4); - leftRec -= 4; - return false; - } - return true; - } - - // I've tried to optimize this slightly, since it gets called a LOT. - void getSubName() - { - if(leftRec <= 0) - fail("No more sub-records, getSubName() failed"); - - // Don't bother with error checking, we will catch an EOF upon - // reading the subrecord data anyway. - file.readBlock(subName.ptr, 4); - - leftRec -= 4; - } - - // We often expect a certain subrecord type, this makes it easy to - // check. - void getSubNameIs(char[] s) - { - getSubName(); - if( subName != s ) - fail("Expected subrecord "~s~" but got "~subName); - } - - // Checks if the next sub-record is called s. If it is, run - // getSubName, if not, return false. - bool isNextSub(char[] s) - { - if(!leftRec) return false; - - getName(subName); - if(subName != s) - { - file.seekCur(-4); - return false; - } - leftRec -= 4; - - //getSubName(); - return true; - } - - // Same as isNextSub, only it works on records instead of - // sub-records. It also loads the record header. - bool isNextHRec(char[] s) - { - if(!leftFile) return false; - getName(recName); - if(recName != s) - { - file.seekCur(-4); - return false; - } - leftFile -= 4; - - uint flags; - getRecHeader(flags); - - return true; - } - - bool hasMoreSubs() { return leftRec > 0; } - bool hasMoreRecs() { return leftFile > 0; } - - // Remaining size of current record - uint getRecLeft() { return leftRec; } - // Size of current sub record - uint getSubSize() { return leftSub; } - - // Skip the rest of this record. Assumes the name and header have - // already been read - void skipRecord() - { - file.seekCur(leftRec); - leftRec = 0; - } - - // Skip an entire record - void skipHRecord() - { - if(!leftFile) return; - - uint flags; - - getRecName(); - getRecHeader(flags); - skipRecord(); - } - - // Skip current sub record and return size - uint skipHSub() - { - getSubHeader(); - file.seekCur(leftSub); - return leftSub; - } - - // Skip sub record and check it's size - void skipHSubSize(uint size) - { - getSubHeader(); - if(leftSub != size) - fail(format("Size mismatch: got %d, wanted %d", leftSub, size)); - file.seekCur(leftSub); - } - - // Check the name and size before skipping - void skipHNSub(char[] name, uint size) - { - getSubNameIs(name); - skipHSubSize(size); - } - - // These read an entire sub-record, including the header. They also - // adjust and check leftSub and leftRecord variables through calling - // getSubHeader(). - void readHExact(void * p, uint size) - { - getSubHeader(); - if(leftSub != size) - fail(format("Size mismatch: got %d, wanted %d", leftSub, size)); - readExact(p, leftSub); - } - - template TgetHType(T) - { T TgetHType() { T t; readHExact(&t, t.sizeof); return t;} } - - // To make these easier to use (and to further distinguish them from - // the above "raw" versions), these return their value instead of - // using an ref argument. - alias TgetHType!(uint) getHUint; - alias TgetHType!(int) getHInt; - alias TgetHType!(float) getHFloat; - alias TgetHType!(ulong) getHUlong; - alias TgetHType!(byte) getHByte; - - // Reads a string sub-record, including header - char[] getHString() - { - getSubHeader(); - - // Hack to make MultiMark.esp load. Zero-length strings do not - // occur in any of the official mods, but MultiMark makes use of - // them. For some reason, they break the rules, and contain a - // byte (value 0) even if the header says there is no data. If - // Morrowind accepts it, so should we. - if(leftSub == 0) - { - // Skip the following zero byte - leftRec--; - assert(file.getc() == 0); - // TODO: Report this by setting a flag or something? - return null; - } - - return getString(region.getString(leftSub)); - } - - // Other quick aliases (this is starting to get messy) - // Get string sub record string with name s - char[] getHNString(char[] s) - { getSubNameIs(s); return getHString(); } - - // Get optional sub record string with name s - char[] getHNOString(char[] s) - { return isNextSub(s) ? getHString() : null; } - - template TgetHNType(T) - { T TgetHNType(char[] s) { T t; readHNExact(&t, t.sizeof, s); return t;} } - - template TgetHNOType(T) - { - T TgetHNOType(char[] s, T def) - { - if(isNextSub(s)) - { - T t; - readHExact(&t, t.sizeof); - return t; - } - else return def; - } - } - - alias TgetHNType!(uint) getHNUint; - alias TgetHNType!(int) getHNInt; - alias TgetHNType!(float) getHNFloat; - alias TgetHNType!(ulong) getHNUlong; - alias TgetHNType!(byte) getHNByte; - alias TgetHNType!(short) getHNShort; - alias TgetHNType!(byte) getHNByte; - - alias TgetHNOType!(float) getHNOFloat; - alias TgetHNOType!(int) getHNOInt; - alias TgetHNOType!(byte) getHNOByte; - - void readHNExact(void* p, uint size, char[] s) - { getSubNameIs(s); readHExact(p,size); } - - // Record header - // This updates the leftFile variable BEYOND the data that follows - // the header, ie beyond the entire record. You are supposed to use - // the leftRec variable when reading record data. - void getRecHeader(out uint flags) - { - // General error checking - if(leftFile < 12) - fail("End of file while reading record header"); - if(leftRec) - fail(format("Previous record contains %d unread bytes", leftRec)); - - getUint(leftRec); - getUint(flags);// This header entry is always zero - assert(flags == 0); - getUint(flags); - leftFile -= 12; - - // Check that sizes add up - if(leftFile < leftRec) - fail(format(leftFile, " bytes left in file, but next record contains ", - leftRec," bytes")); - - // Adjust number of bytes left in file - leftFile -= leftRec; - } - - // Sub-record head - // This updates leftRec beyond the current sub-record as - // well. leftSub contains size of current sub-record. - void getSubHeader() - { - if(leftRec < 4) - fail("End of record while reading sub-record header"); - - if(file.readBlock(&leftSub, 4) != 4) - fail("getSubHeader could not read header length"); - - leftRec -= 4; - - // Adjust number of record bytes left - leftRec -= leftSub; - - // Check that sizes add up - if(leftRec < 0) - fail(format(leftRec+leftSub, - " bytes left in record, but next sub-record contains ", - leftSub," bytes")); - } - - void getSubHeaderIs(uint size) - { - getSubHeader(); - if(leftSub != size) - fail(format("Expected header size to be ", size, ", not ", leftSub)); - } - - /************************************************************************* - * - * Low level reading methods - * - *************************************************************************/ - - /// Raw data of any size - void readExact(void *buf, uint size) - { - assert(size != 0); - file.readExact(buf,size); - } - - // One byte - void getByte(out byte b) { file.read(b); } - void getUByte(out ubyte b) { file.read(b); } - // Two bytes - void getUShort(out ushort s) { file.read(s); } - // Four bytes - void getUint(out uint u) { file.read(u); } - void getInt(out int i) { file.read(i); } - void getFloat(out float f) { file.read(f); } - // Eight bytes - void getUlong(out ulong l) { file.read(l); } - - // Get a record or subrecord name, four bytes - void getName(NAME name) - { - file.readBlock(name.ptr, 4); - /* - if(file.readBlock(name.ptr, 4) != 4) - fail("getName() could not find more data"); - */ - } - - // Fill buffer of predefined length. If actual string is shorter - // (ie. null terminated), the buffer length is set - // accordingly. Chopped string is returned. All strings pass through - // this function, so any character encoding conversions should - // happen here. - char[] getString(char[] str) - { - if(str.length != file.readBlock(str.ptr,str.length)) - fail("getString() could not find enough data in stream"); - - str = stripz(str); - makeUTF8(str); // TODO: A hack. Will replace non-utf characters - // with question marks. This is neither a very - // desirable result nor a very optimized - // implementation of it. - return str; - } - - // Use this to allocate and read strings of predefined length - char[] getString(int l) - { - char[] str = region.getString(l); - return getString(str); - } -} diff --git a/old_d_version/esm/imports.d b/old_d_version/esm/imports.d deleted file mode 100644 index 69f1d33dc..000000000 --- a/old_d_version/esm/imports.d +++ /dev/null @@ -1,47 +0,0 @@ -module esm.imports; - -/* This is a file that imports common modules used by the load*.d - record loaders. It is really a cut down version of what used to be - the start of records.d. - - This file MUST NOT import records.d - directly or indirectly - - because that will trigger a nice three page long list of template - forwarding errors from the compiler. - - What happens is that when DMD/GDC compiles one of the load* files, - it is forced to read records.d first (since it is an imported - module) - but then it sees a template that referes to a struct in - the current load* file, before that struct is defined. Curriously - enough, DMD has no problems when you specify all the source files - on the command line simultaneously. This trick doesn't work with - GDC though, and DSSS doesn't use it either. - - This file was created to work around this compiler bug. -*/ - -public -{ -import esm.defs; -import esm.filereader; -import esm.listkeeper; - -import core.resource; -import core.memory; - -import util.regions; -import monster.util.aa; - -import std.stdio; -import std.string; - -alias RegionBuffer!(ENAMstruct) EffectList; - -// Records that are cross referenced often -import esm.loadscpt; -import esm.loadsoun; -import esm.loadspel; -import esm.loadench; - -import monster.monster; -} - diff --git a/old_d_version/esm/listkeeper.d b/old_d_version/esm/listkeeper.d deleted file mode 100644 index 3722a5d12..000000000 --- a/old_d_version/esm/listkeeper.d +++ /dev/null @@ -1,330 +0,0 @@ -/* - OpenMW - The completely unofficial reimplementation of Morrowind - Copyright (C) 2008 Nicolay Korslund - Email: < korslund@gmail.com > - WWW: http://openmw.snaptoad.com/ - - This file (listkeeper.d) is part of the OpenMW package. - - OpenMW is distributed as free software: you can redistribute it - and/or modify it under the terms of the GNU General Public License - version 3, as published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - version 3 along with this program. If not, see - http://www.gnu.org/licenses/ . - - */ - -module esm.listkeeper; - -import monster.util.aa; - -import core.memory; - -import esm.filereader; -import esm.defs; - -import std.stdio; - -// Item types, used in the lookup table for inventory items, creature -// lists and leveled lists. We also use it for all types of references -// that can exist in cells. -enum ItemType - { - // Items - None = 0, Potion, Apparatus, Armor, Weapon, Book, Clothing, - Light, Ingredient, Pick, Probe, Repair, Misc, ItemList, - - // Used for creature lists - Creature, CreatureList, NPC, - - // Other cell references - Door, Activator, Static, Container//, SoundGen - } - -abstract class ListKeeper -{ - int listIndex; - - new(uint size) - { - return esmRegion.allocate(size).ptr; - } - - delete(void *p) { assert(0); } - - this() - { - // Store our index for later use - listIndex = recordLists.length; - - // Add the class to the global list - recordLists ~= this; - } - - // Load a record from a master or plugin file - void load(); - - // Looks up a reference. If it does not exist it is assumed to be a - // forward reference within a file, and is inserted. - void* lookup(char[] s); - - // Tell the loader that current file has ended, so it can do things - // like check that all referenced objects have been loaded. - void endFile(); - - // Number of inserted elements - uint length(); - - void addToList(ref ItemBaseList l, ItemType t) { assert(0); } -} - -ListKeeper recordLists[]; - -// Keep the list of Type structures for records where the first -// subrecord is an id string called NAME. This id is used for -// lookup. Although almost all lookups match in case, there are a few -// sounds that don't, so we treat these id lookups as generally case -// insensitive. This hasn't posed any problems so far. -class ListID(Type) : ListKeeper -{ - HashTable!(char[], Type, ESMRegionAlloc, CITextHash) names; - - this(uint size) - { - names = names.init; - if(size) names.rehash(size); - } - - // Reads the id for this header. Override if the id is not simply - // getHNString("NAME") - char[] getID() - { - return esFile.getHNString("NAME"); - } - - // Load a record from a master of plugin file - void load() - { - assert(esFile.getFileType == FileType.Esm || - esFile.getFileType == FileType.Esp); - - // Get the identifier of this record - char[] id = getID(); - - // Get pointer to a new or existing object. - Type *p; - if(names.insertEdit(id, p)) - // A new item was inserted - { - p.state = LoadState.Unloaded; - p.id = id; - p.load(); - p.state = LoadState.Loaded; - } - else - // Item already existed, either from a previous file or as a - // forward reference from this file. Load on top of it. The - // LoadState tells the struct whether it contains loaded data. - { - /* - if(p.state == LoadState.Loaded) - // Make a special case for this, perhaps, or just ignore it. - writefln("WARNING: Duplicate record in file %s: '%s'", - esFile.getFilename(), id); - */ - - assert(icmp(p.id, id) == 0); - p.load(); - p.state = LoadState.Loaded; - } - } - - // Looks up a reference. If it does not exist it is assumed to be a - // forward reference within a file, and is inserted. - void* lookup(char[] id) - { - if(!id.length) return null; // Empty reference - - Type *p = names.lookup(id); - // Is the value in the list? - if(!p) - // No, assume it is a forward reference. - { - // Since the lookup name is stored in an internal buffer in - // esFile, we have to copy it. - id = esmRegion.copy(id); - - // To avoid copying the string on every lookup, we have to - // insert in a separate step. But a double lookup isn't - // really THAT expensive. Besides, my tests show that this - // is used in less than 10% of the cases. - names.insertEdit(id, p); - p.id = id; - p.state = LoadState.Unloaded; - } - return cast(void*)p; - } - - // Check that all referenced objects are actually loaded. - void endFile() - in - { - // We can skip this in release builds - names.validate(); - } - body - { - foreach(char[] id, ref Type t; names) - // Current file is now counted as done - if(t.state == LoadState.Loaded) t.state = LoadState.Previous; - else if(t.state == LoadState.Unloaded) - //writefln("WARNING: Unloaded reference " ~ id); - esFile.fail("Unloaded reference " ~ id); - } - - // Number of inserted elements - uint length() {return names.length;} - - // Add the names in this list to an ItemList - void addToList(ref ItemBaseList l, ItemType t) - { - foreach(char[] id, ref Type s; names) - l.insert(id, &s, t); - } -} - -// A pointer to an item -struct ItemBase -{ - ItemType type; - void *p; -} - -struct ItemBaseList -{ - HashTable!(char[],ItemBase,ESMRegionAlloc) list; - - void addList(ItemBaseList l) - { - foreach(char[] id, ItemBase b; l.list) - insert(id, b.p, b.type); - } - - void addList(ListKeeper source, ItemType type) - { - source.addToList(*this, type); - } - - void insert(char[] id, void* p, ItemType type) - { - ItemBase *b; - if(!list.insertEdit(id, b)) - { - //writefln("Replacing item ", id); - if(b.type != ItemType.None) - esFile.fail("Replaced valid item: " ~ id); - } - //else writefln("Inserting new item ", id); - - b.type = type; - b.p = p; - } - - // Called at the end to check that all referenced items have been resolved - void endMerge() - { - foreach(char[] id, ref ItemBase t; list) - // Current file is now counted as done - if(t.type == ItemType.None) - // TODO: Don't use esFile.fail for this - esFile.fail("ItemBaseList: Unresolved forward reference: " ~ id); - } - - // Look up an item, return a pointer to the ItemBase representing - // it. If it does not exist, it is inserted. - ItemBase *lookup(char[] id) - { - if(!id.length) return null; // Empty reference - ItemBase *b = list.lookup(id); - // Is the value in the list? - if(!b) - // No, assume it is a forward reference. - { - // Since the lookup name is stored in an internal buffer in - // esFile, we have to copy it. - id = esmRegion.copy(id); - - // To avoid copying the string on every lookup, we have to - // insert in a separate step. But a double lookup isn't - // really THAT expensive. - list.insertEdit(id, b); - - b.p = null; - b.type = ItemType.None; - } - return b; - } -} - -// An item. Contains a reference to an ItemBase, which again is a -// reference to an item. The ItemBase might change after we have -// looked it up (for forward references), so we have to use a pointer. -struct Item -{ - ItemBase *i; - - void* getPtr(ItemType type) - { - if(i != null && i.type == type) return i.p; - return null; - } - - T* getType(T, ItemType Type)() - { - return cast(T*)getPtr(Type); - } -} - -struct ItemList -{ - private: - ItemBaseList list; - - public: - void addList(ItemList l) - { list.addList(l.list); } - - void addList(ListKeeper source, ItemType type) - { list.addList(source, type); } - - Item lookup(char[] id) - { - Item i; - i.i = list.lookup(id); - return i; - } - - void endMerge() - { list.endMerge(); } - - void endFile() - in { list.list.validate(); } - body {} - - void rehash(uint size) - { list.list.rehash(size); } - - uint length() { return list.list.length(); } -} - -// Aggregate lists, made by concatinating several other lists. -ItemList items; // All inventory items, including leveled item lists -ItemList actors; // All actors, ie. NPCs, creatures and leveled lists -ItemList cellRefs; // All things that are referenced from cells diff --git a/old_d_version/esm/loadcell.d b/old_d_version/esm/loadcell.d deleted file mode 100644 index 6f987e2e0..000000000 --- a/old_d_version/esm/loadcell.d +++ /dev/null @@ -1,189 +0,0 @@ -/* - This file contains some leftovers which have not yet been ported to - C++. - */ - -align(1) struct AMBIStruct -{ - Color ambient, sunlight, fog; - float fogDensity; - - static assert(AMBIStruct.sizeof == 16); -} - -int max(int x, int y) -{ return x>=y?x:y; } - -struct ExtCellHash -{ - // This is a pretty good hash, gives no collisions for all of - // Morrowind.esm when the table size is 2048, and it gives very few - // collisions overall. Not that it matters that much. - static uint hash(uint val) - { - uint res = cast(ushort)val; - res += *(cast(ushort*)&val+1)*41; - return res; - } - - static bool isEqual(uint a, uint b) { return a==b; } -} - -class CellList : ListKeeper -{ - // Again, these are here to avoid DMD template bugs - alias _aaNode!(char[], InteriorCell) _unused1; - alias _aaNode!(uint, ExteriorCell) _unused2; - - HashTable!(char[], InteriorCell, ESMRegionAlloc) in_cells; - HashTable!(uint, ExteriorCell, ESMRegionAlloc, ExtCellHash) ex_cells; - - // Store the maximum x or y coordinate (in absolute value). This is - // used in the landscape pregen process. - int maxXY; - - this() - { - in_cells = in_cells.init; - in_cells.rehash(1600); - - ex_cells = ex_cells.init; - ex_cells.rehash(1800); - } - - align(1) struct DATAstruct - { - CellFlags flags; - int gridX, gridY; - static assert(DATAstruct.sizeof==12); - } - - DATAstruct data; - - // Look up an interior cell, throws an error if not found (might - // change later) - InteriorCell *getInt(char[] s) - { - return in_cells.getPtr(s); - } - - // Exterior cell, same as above - ExteriorCell *getExt(int x, int y) - { - return ex_cells.getPtr(compound(x,y)); - } - - // Check whether we have a given exterior cell - bool hasExt(int x, int y) - { - return ex_cells.inList(compound(x,y)); - } - - void *lookup(char[] s) - { assert(0); } - - void endFile() - out - { - in_cells.validate(); - ex_cells.validate(); - } - body - { - foreach(id, ref c; in_cells) - { - if(c.state == LoadState.Loaded) c.state = LoadState.Previous; - // We never forward reference cells! - assert(c.state != LoadState.Unloaded); - } - - foreach(id, ref c; ex_cells) - { - if(c.state == LoadState.Loaded) c.state = LoadState.Previous; - // We never forward reference cells! - assert(c.state != LoadState.Unloaded); - } - } - - uint length() { return numInt() + numExt(); } - uint numInt() { return in_cells.length; } - uint numExt() { return ex_cells.length; } - - // Turn an exterior cell grid position into a unique number - static uint compound(int gridX, int gridY) - { - return cast(ushort)gridX + ((cast(ushort)gridY)<<16); - } - - static void decompound(uint val, out int gridX, out int gridY) - { - gridX = cast(short)(val&0xffff); - gridY = cast(int)(val&0xffff0000) >> 16; - } - - void load() - {with(esFile){ - char[] id = getHNString("NAME"); - - // Just ignore this, don't know what it does. I assume it - // deletes the cell, but we can't handle that yet. - if(isNextSub("DELE")) getHInt(); - - readHNExact(&data, data.sizeof, "DATA"); - - if(data.flags & CellFlags.Interior) - { - InteriorCell *p; - if(in_cells.insertEdit(id, p)) - // New item was inserted - { - p.state = LoadState.Unloaded; - p.id = id; - p.flags = data.flags; - p.load(); - p.state = LoadState.Loaded; - } - else - // Overloading an existing cell - { - if(p.state != LoadState.Previous) - fail("Duplicate interior cell " ~ id); - - assert(id == p.id); - p.load(); - p.state = LoadState.Loaded; - } - } - else // Exterior cell - { - uint key = compound(data.gridX, data.gridY); - - ExteriorCell *p; - if(ex_cells.insertEdit(key, p)) - // New cell - { - p.state = LoadState.Unloaded; - p.name = id; - p.flags = data.flags; - p.gridX = data.gridX; - p.gridY = data.gridY; - p.load(); - p.state = LoadState.Loaded; - - int mx = max(abs(p.gridX), abs(p.gridY)); - maxXY = max(maxXY, mx); - } - else - { - if(p.state != LoadState.Previous) - fail(format("Duplicate exterior cell %d %d", - data.gridX, data.gridY)); - assert(p.gridX == data.gridX); - assert(p.gridY == data.gridY); - p.load(); - p.state = LoadState.Loaded; - } - } - }} -} -CellList cells; diff --git a/old_d_version/esm/loaddial.d b/old_d_version/esm/loaddial.d deleted file mode 100644 index 6e36f0b88..000000000 --- a/old_d_version/esm/loaddial.d +++ /dev/null @@ -1,289 +0,0 @@ -/* - OpenMW - The completely unofficial reimplementation of Morrowind - Copyright (C) 2008 Nicolay Korslund - Email: < korslund@gmail.com > - WWW: http://openmw.snaptoad.com/ - - This file (loaddial.d) is part of the OpenMW package. - - OpenMW is distributed as free software: you can redistribute it - and/or modify it under the terms of the GNU General Public License - version 3, as published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - version 3 along with this program. If not, see - http://www.gnu.org/licenses/ . - - */ - -module esm.loaddial; -import esm.imports; -import esm.loadinfo; - -/* - * Dialogue topic and journal entries. The acutal data is contained in - * the following INFO records. - */ - -// Keep a list of possible hyper links. This list is used when parsing -// text and finding references to topic names. Eg. if a character says -// "Would you like to join the mages guild?", we must be able to pick -// out the phrase "join the mages guild?" and make a hyperlink of -// it. Each link is indexed by their first word. The structure -// contains the rest of the phrase, so the phrase above would be -// indexed by "join" and contain the string "the mages guild?", for -// quick comparison with the text are currently parsing. It also -// contains a pointer to the corresponding dialogue struct. The lists -// are sorted by descending string length, in order to match the -// longest possible term first. - -struct Hyperlink -{ - Dialogue *ptr; - char[] rest; - - // Returns a < b if a.length > b.length. - int opCmp(Hyperlink *h) {return h.rest.length - rest.length;} -} - -alias RegionBuffer!(Hyperlink) HyperlinkArray; - -// This is much nicer now that we use our own AA. -struct HyperlinkList -{ - // Make a case insensitive hash table of Hyperlink arrays. - HashTable!(char[], HyperlinkArray, ESMRegionAlloc, CITextHash) list; - - void add(char[] topic, Dialogue* ptr) - { - // Only add dialogues - if(ptr.type != Dialogue.Type.Topic) return; - - Hyperlink l; - l.ptr = ptr; - l.rest = topic; - - // Use the first word as the index - topic = nextWord(l.rest); - - // Insert a new array, or get an already existing one - HyperlinkArray ha = list.get(topic, - // Create a new array - delegate void (ref HyperlinkArray a) - { a = esmRegion.getBuffer!(Hyperlink)(0,1); } - ); - - // Finally, add it to the list - ha ~= l; - } - - Hyperlink[] getList(char[] word) - { - HyperlinkArray p; - if(list.inList(word, p)) return p.array(); - return null; - } - - void rehash(uint size) - { - list.rehash(size); - } - - // We wouldn't need this if we only dealt with one file, since the - // topics are already sorted in Morrowind.esm. However, other files - // might add items out of order later, so we have to sort it. To - // understand why this is needed, consider the following example: - // - // Morrowind.esm contains the topic 'join us'. When ever the text - // ".. join us blahblah ..." is encountered, this match is - // found. However, if a plugin adds the topic 'join us today', we - // have to place this _before_ 'join us' in the list, or else it - // will never be matched. - void sort() - { - foreach(char[] s, HyperlinkArray l; list) - { - l.array().sort; - /* - writefln("%s: ", s, l.length); - foreach(Hyperlink h; l.array()) - writefln(" %s (%s)", h.rest, h.ptr.id); - */ - } - } -} - -// List of dialogue hyperlinks -HyperlinkList hyperlinks; - -struct Dialogue -{ - enum Type - { - Topic = 0, - Voice = 1, - Greeting = 2, - Persuasion = 3, - Journal = 4, - Deleted = -1 - } - - //Type type; - DialogueType type; - - DialInfoList infoList; - - char[] id; // This is the 'dialogue topic' that the user actually - // sees. - LoadState state; - - void load() - {with(esFile){ - getSubNameIs("DATA"); - - getSubHeader(); - int si = getSubSize(); - if(si == 1) - { - byte b; - getByte(b); - DialogueType t = cast(DialogueType)b; - - // Meet the new type, same as the old type - if(t != this.type && state == LoadState.Previous) - fail("Type changed in dialogue " ~ id); - - this.type = t; - } - else if(si == 4) - { - // These are just markers, their values are not used. - int i; - getInt(i); - //writefln("In file %s:", getFilename()); - //writefln(" WARNING: DIAL.DATA was size 4 and contains: ", i); - i = getHNInt("DELE"); - //writefln(" DELE contains ", i); - this.type = Type.Deleted; - } - else fail("Unknown sub record size " ~ toString(si)); - - infoList.state = state; - while(isNextHRec("INFO")) - infoList.load(this.type); - //skipRecord(); - }} -} - -typedef Dialogue.Type DialogueType; - -/+ - // I don't remember when I commented out this code or what state - // it is in. Probably highly experimental. - // -------------- - - // Loop through the info blocks in this dialogue, and update the - // master as necessary. - - // TODO: Note that the dialogue system in Morrowind isn't very - // robust. If several mods insert dialogues at exactly the same - // place, the mods loaded last might overwrite the previous mods, - // completely removing the previous entry even if the two entries - // do not have the same id. This is because a change also - // overwrites the previous and the next entry, in order to update - // their "previous" and "next" fields. Furthermore, we might put - // ourselves in a situation where the forward and backward chains - // do not match, or in a situation where we update a deleted - // info. For now I do nothing about it, but I will have to develop - // a "conflict manager" later on. It SHOULD be possible to merge - // these info lists automatically in most cases, but it - // complicates the code. - - // Whoa, seems we have a case study already with just tribunal and - // bloodmoon loaded! See comments below. - - foreach(char[] id, ref DialInfoLoad m; mod.infoList) - { - // Remove the response if it is marked as deleted. - if(m.deleted) - { - if((id in master.infoList) == null) - writefln("Cannot delete info %s, does not exist", id); - else master.infoList.remove(id); - } - else - // Just plain copy it in. - master.infoList[id] = m; - } - } - - // Here we have to fix inconsistencies. A good case example is the - // dialogue "Apelles Matius" in trib/blood. Trib creates a - // dialogue of a few items, bloodmoon adds another. But since the - // two are independent, the list in bloodmoon does not change the - // one in trib but rather creates a new one. In other words, we - // will have to deal with the possibility of several "independent" - // lists within each topic. We can do this by looking for several - // start points (ie. infos with prev="") and just latch them onto - // each other. I'm not sure it gives the correct result, - // though. For example, which list comes first would be rather - // arbitrarily decided by the order we traverse the infoList AA. I - // will just have to assume that they truly are "independent". - - // There still seems to be a problem though. Bloodmoon overwrites - // some stuff added by Tribunal, see "Boots of the Apostle" for an - // example. Looks like the editor handles it just fine... We need - // to make sure that all the items in our AA are put into the - // list, and in the right place too. We obviously cannot fully - // trust the 'next' and 'prev' fields, but they are the only - // guidance we have. Deal with it later! - - // At this point we assume "master" to contain the final dialogue - // list, so at this point we can set it in stone. - infoList.length = master.infoList.length; - - // Find the first entry - DialInfoLoad* starts[]; // starting points for linked lists - DialInfoLoad *current; - foreach(char[] id, ref DialInfoLoad l; master.infoList) - if(l.prev == "") starts ~= &l; - - foreach(int num, ref DialInfo m; infoList) - { - if(current == null) - { - if(starts.length == 0) - { - writefln("Error: No starting points!"); - infoList.length = num; - break; - } - // Pick the next starting point - current = starts[0]; - starts = starts[1..$]; - } - m.copy(*current, this); - - if((*current).next == "") - current = null; - else - { - current = (*current).next in master.infoList; - if(current == null) - { - writefln("Error in dialouge info lookup!"); - break; - } - } - } - if(infoList.length != master.infoList.length) - writefln("Dialogue list lengths do not match, %d != %d", - infoList.length, master.infoList.length); - } -} -+/ diff --git a/old_d_version/esm/records.d b/old_d_version/esm/records.d deleted file mode 100644 index d5dd211a1..000000000 --- a/old_d_version/esm/records.d +++ /dev/null @@ -1,219 +0,0 @@ -/* - OpenMW - The completely unofficial reimplementation of Morrowind - Copyright (C) 2008 Nicolay Korslund - Email: < korslund@gmail.com > - WWW: http://openmw.snaptoad.com/ - - This file (records.d) is part of the OpenMW package. - - OpenMW is distributed as free software: you can redistribute it - and/or modify it under the terms of the GNU General Public License - version 3, as published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - version 3 along with this program. If not, see - http://www.gnu.org/licenses/ . - - */ -module esm.records; - -public -{ - import monster.util.aa; - import util.regions; - - import core.memory; - import core.resource; - - import esm.filereader; - import esm.defs; - import esm.listkeeper; - - import std.stdio; // Remove later -} - -public import - esm.loadacti, esm.loaddoor, esm.loadglob, esm.loadscpt, esm.loadsoun, esm.loadgmst, - esm.loadfact, esm.loadstat, esm.loadspel, esm.loadalch, esm.loadappa, esm.loadarmo, - esm.loadbody, esm.loadench, esm.loadbook, esm.loadbsgn, esm.loadltex, esm.loadmgef, - esm.loadweap, esm.loadlocks,esm.loadcell, esm.loadregn, esm.loadligh, esm.loadskil, - esm.loadsndg, esm.loadrace, esm.loadmisc, esm.loadclot, esm.loadingr, esm.loadclas, - esm.loadcont, esm.loadcrea, esm.loadnpc, esm.loaddial, esm.loadinfo, esm.loadsscr, - esm.loadlevlist; - -void loadRecord(char[] recName) -{ - switch(recName) - { - case "ACTI": activators.load(); break; - case "DOOR": doors.load(); break; - case "GLOB": globals.load(); break; - case "SCPT": scripts.load(); break; - case "SOUN": sounds.load(); break; - case "GMST": gameSettings.load(); break; - case "FACT": factions.load(); break; - case "STAT": statics.load(); break; - case "SPEL": spells.load(); break; - case "ALCH": potions.load(); break; - case "APPA": appas.load(); break; - case "ARMO": armors.load(); break; - case "BODY": bodyParts.load(); break; - case "ENCH": enchants.load(); break; - case "BOOK": books.load(); break; - case "BSGN": birthSigns.load(); break; - case "LTEX": landTextures.load(); break; - case "MGEF": effects.load(); break; - case "WEAP": weapons.load(); break; - case "REPA": repairs.load(); break; - case "LOCK": lockpicks.load(); break; - case "PROB": probes.load(); break; - case "CELL": cells.load(); break; - case "REGN": regions.load(); break; - case "LIGH": lights.load(); break; - case "SKIL": skills.load(); break; - case "SNDG": soundGens.load(); break; - case "RACE": races.load(); break; - case "MISC": miscItems.load(); break; - case "CLOT": clothes.load(); break; - case "INGR": ingreds.load(); break; - case "CLAS": classes.load(); break; - case "CONT": containers.load(); break; - case "CREA": creatures.load(); break; - case "LEVI": itemLists.load(); break; - case "LEVC": creatureLists.load(); break; - case "NPC_": npcs.load(); break; - case "DIAL": dialogues.load(); break; - case "SSCR": startScripts.load(); break; - /* - - // Tribunal / Bloodmoon only - case "SSCR": loadSSCR.load(); break; - - */ - // For save games: - // case "NPCC": loadNPCC; - // case "CNTC": loadCNTC; - // case "CREC": loadCREC; - - // These should never be looked up - case "TES3": - case "INFO": - case "LAND": - case "PGRD": - esFile.fail("Misplaced record " ~ recName); - default: - esFile.fail("Unknown record type " ~ recName); - } - //*/ -} - -// Um, this has to be in this file for some reason. -ListID!(Dialogue) dialogues; - -struct ItemT -{ - Item item; - ItemBase *i; - - T* getType(T, ItemType Type)() - { - return item.getType!(T, Type)(); - } - - alias getType!(Potion, ItemType.Potion) getPotion; - alias getType!(Apparatus, ItemType.Apparatus) getApparatus; - alias getType!(Armor, ItemType.Armor) getArmor; - alias getType!(Weapon, ItemType.Weapon) getWeapon; - alias getType!(Book, ItemType.Book) getBook; - alias getType!(Clothing, ItemType.Clothing) getClothing; - alias getType!(Light, ItemType.Light) getLight; - alias getType!(Ingredient, ItemType.Ingredient) getIngredient; - alias getType!(Tool, ItemType.Pick) getPick; - alias getType!(Tool, ItemType.Probe) getProbe; - alias getType!(Tool, ItemType.Repair) getRepair; - alias getType!(Misc, ItemType.Misc) getMisc; - alias getType!(LeveledItems, ItemType.ItemList) getItemList; - alias getType!(Creature, ItemType.Creature) getCreature; - alias getType!(LeveledCreatures, ItemType.CreatureList) getCreatureList; - alias getType!(NPC, ItemType.NPC) getNPC; - alias getType!(Door, ItemType.Door) getDoor; - alias getType!(Activator, ItemType.Activator) getActivator; - alias getType!(Static, ItemType.Static) getStatic; - alias getType!(Container, ItemType.Container) getContainer; - - static ItemT opCall(Item it) - { - ItemT itm; - itm.item = it; - itm.i = it.i; - return itm; - } -} - -void endFiles() -{ - foreach(ListKeeper l; recordLists) - l.endFile(); - - items.endFile(); -} - -void initializeLists() -{ - recordLists = null; - - // Initialize all the lists here. The sizes have been chosen big - // enough to hold the main ESM files and a large number of mods - // without rehashing. - - activators = new ListID!(Activator)(1400); - doors = new ListID!(Door)(300); - globals = new ListID!(Global)(300); - scripts = new ScriptList(1800); - sounds = new ListID!(Sound)(1000); - gameSettings = new ListID!(GameSetting)(1600); - factions = new ListID!(Faction)(30); - statics = new ListID!(Static)(4000); - spells = new ListID!(Spell)(1300); - potions = new ListID!(Potion)(300); - appas = new ListID!(Apparatus)(30); - armors = new ListID!(Armor)(500); - bodyParts = new ListID!(BodyPart)(2300); - enchants = new ListID!(Enchantment)(1000); - books = new ListID!(Book)(700); - birthSigns = new ListID!(BirthSign)(30); - landTextures = new LandTextureList; - effects = new MagicEffectList; - weapons = new ListID!(Weapon)(700); - lockpicks = new ListID!(Tool)(10); - probes = new ListID!(Tool)(10); - repairs = new ListID!(Tool)(10); - cells = new CellList; - regions = new ListID!(Region)(20); - lights = new ListID!(Light)(1000); - skills = new SkillList; - soundGens = new ListID!(SoundGenerator)(500); - races = new ListID!(Race)(100); - miscItems = new ListID!(Misc)(700); - clothes = new ListID!(Clothing)(700); - ingreds = new ListID!(Ingredient)(200); - classes = new ListID!(Class)(100); - containers = new ListID!(Container)(1200); - creatures = new ListID!(Creature)(800); - itemLists = new ListID!(LeveledItems)(600); - creatureLists = new ListID!(LeveledCreatures)(400); - npcs = new ListID!(NPC)(3500); - dialogues = new ListID!(Dialogue)(3000); - startScripts.init(); - - hyperlinks.rehash(1600); - - items.rehash(5500); - actors.rehash(5000); - cellRefs.rehash(17000); -} diff --git a/old_d_version/esmtool.d b/old_d_version/esmtool.d deleted file mode 100644 index a43361803..000000000 --- a/old_d_version/esmtool.d +++ /dev/null @@ -1,377 +0,0 @@ -/* - OpenMW - The completely unofficial reimplementation of Morrowind - Copyright (C) 2008 Nicolay Korslund - Email: < korslund@gmail.com > - WWW: http://openmw.snaptoad.com/ - - This file (esmtool.d) is part of the OpenMW package. - - OpenMW is distributed as free software: you can redistribute it - and/or modify it under the terms of the GNU General Public License - version 3, as published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - version 3 along with this program. If not, see - http://www.gnu.org/licenses/ . - - */ - -module esmtool; - -import std.stdio; - -import core.memory; -import esm.esmmain; -import monster.util.string; -import mscripts.setup; - -import std.gc; -import gcstats; - - -// Not used, but we have to link it in along with the C++ stuff. -import input.events; - -void poolSize() -{ - GCStats gc; - getStats(gc); - writefln("Pool size: ", comma(gc.poolsize)); - writefln("Used size: ", comma(gc.usedsize)); -} - -//alias int[Dialogue*] TopicList; - -void main(char[][] args) -{ - char[][] files; - bool raw; - - bool scptList; // List scripts - bool scptShow; // Show a script - char[] scptName; // Script to show - - bool ciList; // List interior cells - bool ceList; // List exterior cells that have names - - bool weList; // List weapons - - bool gmst; // List game settings - - bool numbers; // List how many there are of each record type - - foreach(char[] a; args[1..$]) - if(a == "-r") raw = true; - - else if(a == "-sl") scptList = true; - else if(a == "-s") scptShow = true; - else if(scptShow && scptName == "") scptName = a; - - else if(a == "-g") gmst = true; - else if(a == "-cil") ciList = true; - else if(a == "-cel") ceList = true; - - else if(a == "-wl") weList = true; - - else if(a == "-n") numbers = true; - - else if(a.begins("-")) writefln("Ignoring unknown option %s", a); - else files ~= a; - - int help(char[] msg) - { - writefln("%s", msg); - writefln("Syntax: %s [options] esm-file [esm-file ... ]", args[0]); - writefln(" Options:"); - writefln(" -r Display all records in raw format"); - writefln(" -n List the number of each record type"); - writefln(" -sl List scripts"); - writefln(" -g List game settings (GMST)"); - writefln(" -s name Show given script"); - writefln(" -cil List interior cells"); - writefln(" -cel List exterior cells with names"); - writefln(" -wl List weapons"); - return 1; - } - if(files.length == 0) return help("No input files given"); - - if(scptShow && scptName == "") return help("No script name given"); - - initializeMemoryRegions(); - initMonsterScripts(); - - if(raw) - { - foreach(int fileNum, char[] filename; files) - { - try - { - esFile.open(filename, esmRegion); - printRaw(); - } - catch(Exception e) - { - try {writefln(e);} - catch {} - writefln("Error on file %s", filename); - } - catch - { - writefln("Error: Unkown failure on file %s", filename); - } - } - return; - } - - // Disable resource lookups. - resources.dummy = true; - - try loadTESFiles(files); - catch(Exception e) - { - writefln(e); - } - catch { writefln("Error: Unkown failure"); } - - // List weapons - if(weList) foreach(n, m; weapons.names) - { - alias Weapon.Type WT; - switch(m.data.type) - { - case WT.ShortBladeOneHand: writef("Short Sword"); break; - case WT.LongBladeOneHand: writef("Long Sword, One-Handed"); break; - case WT.LongBladeTwoHand: writef("Long Sword, Two-Handed"); break; - case WT.BluntOneHand: writef("Blunt, One-Handed"); break; - case WT.BluntTwoClose: writef("Blunt, Two-Handed"); break; - case WT.BluntTwoWide: writef("Blunt, Two-Handed Wide"); break; - case WT.SpearTwoWide: writef("Spear, Two-Handed"); break; - case WT.AxeOneHand: writef("Axe, One-Handed"); break; - case WT.AxeTwoHand: writef("Axe, Two-Handed"); break; - case WT.MarksmanBow: writef("Bow"); break; - case WT.MarksmanCrossbow: writef("Crossbow"); break; - case WT.MarksmanThrown: writef("Thrown weapon"); break; - case WT.Arrow: writef("Arrow"); break; - case WT.Bolt: writef("Bolt"); break; - default: assert(0); - } - writefln(" id '%s': name '%s'", n, m.name); - - if(m.data.flags & Weapon.Flags.Magical) - writefln("Magical"); - if(m.data.flags & Weapon.Flags.Silver) - writefln("Silver"); - - writefln("Weight: ", m.data.weight); - writefln("Value: ", m.data.value); - writefln("Health: ", m.data.health); - writefln("Speed: ", m.data.speed); - writefln("Reach: ", m.data.reach); - writefln("Enchantment points: ", m.data.enchant); - writefln("Combat: ", m.data.chop, m.data.slash, m.data.thrust); - - if(m.enchant) writefln("Has enchantment '%s'", m.enchant.id); - if(m.script) writefln("Has script '%s'", m.script.id); - - writefln(); - } - - if(numbers) - { - writefln("Activators: ", activators.length); - writefln("Doors: ", doors.length); - writefln("Globals: ", globals.length); - writefln("Sounds: ", sounds.length); - writefln("Game Settings: ", gameSettings.length); - writefln("Factions: ", factions.length); - writefln("Statics: ", statics.length); - writefln("Spells: ", spells.length); - writefln("Potions: ", potions.length); - writefln("Apparatus: ", appas.length); - writefln("Armors: ", armors.length); - writefln("Body parts: ", bodyParts.length); - writefln("Enchantments: ", enchants.length); - writefln("Books: ", books.length); - writefln("Birth signs: ", birthSigns.length); - writefln("Land texture files: ", landTextures.length); - writefln("Weapons: ", weapons.length); - writefln("Lockpicks: ", lockpicks.length); - writefln("Probes: ", probes.length); - writefln("Repairs: ", repairs.length); - writefln("Cells: ", cells.length); - writefln(" Interior: ", cells.numInt); - writefln(" Exterior: ", cells.numExt); - writefln("Regions: ", regions.length); - writefln("Lights: ", lights.length); - writefln("Skills: ", skills.length); - writefln("Sound generators: ", soundGens.length); - writefln("Races: ", races.length); - writefln("Misc items: ", miscItems.length); - writefln("Cloths: ", clothes.length); - writefln("Ingredients: ", ingreds.length); - writefln("Classes: ", classes.length); - writefln("Containers: ", containers.length); - writefln("Creatures: ", creatures.length); - writefln("Leveled item lists: ", itemLists.length); - writefln("Leveled creature lists: ", creatureLists.length); - writefln("NPCs: ", npcs.length); - writefln("Scripts: ", scripts.length); - writefln("Dialogues: ", dialogues.length); - writefln("Hyperlinks: ", hyperlinks.list.length); - writefln("Start scripts: ", startScripts.length); - writefln("\nTotal items: ", items.length); - writefln("Total actors: ", actors.length); - writefln("Total cell placable items: ", cellRefs.length); - } - if(gmst) - { - foreach(a, b; gameSettings.names) - { - writef(a, " ("); - if(b.type == VarType.Int) writefln("int) = ", b.i); - else if(b.type == VarType.Float) writefln("float) = ", b.f); - else if(b.type == VarType.String) writefln("string) = '%s'", b.str); - else writefln("no value)", cast(int)b.type); - } - } - - if(scptList) foreach(a, b; scripts.names) writefln(a); - if(ciList) - foreach(a, b; cells.in_cells) - writefln(a); - if(ceList) - foreach(uint i, c; .cells.ex_cells) - { - int x, y; - CellList.decompound(i, x, y); - if(c.name.length) - writefln("%s,%s: %s", x, y, c.name); - } - - if(scptShow) - { - Script *p = scripts.names.lookup(scptName); - if(p) - writefln("Script '%s', text is:\n-------\n%s\n-------", p.id, p.scriptText); - else writefln("Script '%s' not found", scptName); - writefln(); - } - - writefln(esmRegion); - - poolSize(); -} - -// Quick function that simply iterates through an ES file and prints -// out all the records and subrecords. Some of this code is really old -// (about 2004-2005) -void printRaw() -{ - with(esFile) - { - // Variable length integer (this only works for unsigned ints!) - ulong getHVUint() - { - ulong l; - - getSubHeader(); - if( (getSubSize != 4) && - (getSubSize != 2) && - (getSubSize != 8) ) - fail(format("Unknown integer size: ", getSubSize)); - - readExact(&l, getSubSize); - return l; - } - - writefln("Filename: ", getFilename); - writef("Filetype: "); - switch(getFileType()) - { - case FileType.Plugin: writefln("Plugin"); break; - case FileType.Master: writefln("Master"); break; - case FileType.Savegame: writefln("Savegame"); break; - case FileType.Unknown: writefln("Unknown"); break; - default: assert(0); - } - writef("Version: "); - if(isVer12()) writefln("1.2"); - else if(isVer13()) writefln("1.3"); - else writefln("Unknown"); - - writefln("Records: ", getRecords); - writefln("Master files:"); - for(int i; i - WWW: http://openmw.snaptoad.com/ - - This file (events.d) is part of the OpenMW package. - - OpenMW is distributed as free software: you can redistribute it - and/or modify it under the terms of the GNU General Public License - version 3, as published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - version 3 along with this program. If not, see - http://www.gnu.org/licenses/ . - - */ - -module input.events; - -import std.stdio; -import std.string; - -import sound.audio; - -import core.config; - -import scene.soundlist; -import scene.player; - -import bullet.bindings; - -import monster.monster; -import monster.vm.dbg; - -import ogre.bindings; -import gui.bindings; -import gui.gui; - -import input.keys; -import input.ois; - -// Debug output -//debug=printMouse; // Mouse button events -//debug=printMouseMove; // Mouse movement events -//debug=printKeys; // Keypress events - -// TODO: Jukebox controls and other state-related data will later be -// handled entirely in script code, as will some of the key bindings. - -// Pause? -bool pause = false; -int *guiMode; - -const float volDiff = 0.05; - -void musVolume(bool increase) -{ - float diff = -volDiff; - if(increase) diff = -diff; - config.setMusicVolume(diff + config.getMusicVolume); - writefln(increase?"Increasing":"Decreasing", " music volume to ", config.getMusicVolume); -} - -void sfxVolume(bool increase) -{ - float diff = -volDiff; - if(increase) diff = -diff; - config.setSfxVolume(diff + config.getSfxVolume); - writefln(increase?"Increasing":"Decreasing", " sound effect volume to ", config.getSfxVolume); -} - -void mainVolume(bool increase) -{ - float diff = -volDiff; - if(increase) diff = -diff; - config.setMainVolume(diff + config.getMainVolume); - writefln(increase?"Increasing":"Decreasing", " main volume to ", config.getMainVolume); -} - -void updateMouseSensitivity() -{ - effMX = *config.mouseSensX; - effMY = *config.mouseSensY; - if(*config.flipMouseY) effMY = -effMY; -} - -void togglePause() -{ - pause = !pause; - if(pause) writefln("Pause"); - else writefln("Pause off"); -} - -extern(C) void d_handleMouseButton(MouseState *state, int button) -{ - debug(printMouse) - writefln("handleMouseButton %s: Abs(%s, %s, %s)", button, - state.X.abs, state.Y.abs, state.Z.abs); - - // For the moment, just treat mouse clicks as normal key presses. - d_handleKey(cast(KC) (KC.Mouse0 + button)); -} - -// Handle a keyboard event through key bindings. Direct movement -// (eg. arrow keys) is not handled here, see d_frameStarted() below. -extern(C) void d_handleKey(KC keycode, dchar text = 0) -{ - // Do some preprocessing on the data to account for OIS - // shortcommings. - - // Some keys (especially international keys) have no key code but - // return a character instead. - if(keycode == 0) - { - // If no character is given, just drop this event since OIS did - // not manage to give us any useful data at all. - if(text == 0) return; - - keycode = KC.CharOnly; - } - - // Debug output - debug(printKeys) - { - char[] str; - if(keycode >= 0 && keycode < keysymToString.length) - str = keysymToString[keycode]; - else str = "OUT_OF_RANGE"; - writefln("Key %s, text '%s', name '%s'", keycode, text, str); - } - - // Look up the key binding. We have to send both the keycode and the - // text. - Keys k = keyBindings.findMatch(keycode, text); - - // These are handled even if we are in gui mode: - if(k) - switch(k) - { - case Keys.ToggleGui: gui_toggleGui(); return; - case Keys.Console: gui_toggleConsole(); return; - case Keys.ScreenShot: takeScreenShot(); return; - default: - } - - if(*guiMode) return; - - if(k) - switch(k) - { - case Keys.ToggleBattleMusic: - Music.toggle(); - return; - - case Keys.MainVolUp: mainVolume(true); return; - case Keys.MainVolDown: mainVolume(false); return; - case Keys.MusVolUp: musVolume(true); return; - case Keys.MusVolDown: musVolume(false); return; - case Keys.SfxVolUp: sfxVolume(true); return; - case Keys.SfxVolDown: sfxVolume(false); return; - case Keys.Mute: Music.toggleMute(); return; - case Keys.Fullscreen: toggleFullscreen(); return; - - case Keys.PhysMode: bullet_nextMode(); return; - case Keys.Nighteye: ogre_toggleLight(); return; - - case Keys.Debug: return; - case Keys.Pause: togglePause(); return; - case Keys.Exit: exitProgram(); return; - default: - assert(k >= 0 && k < keyToString.length); - writefln("WARNING: Event %s has no effect", keyToString[k]); - } - return; -} - -// Refresh rate for sfx placements, in seconds. -const float sndRefresh = 0.17; - -// Refresh rate for music fadeing, seconds. -const float musRefresh = 0.05; - -// Walking / floating speed, in points per second. -float speed = 300; - -float sndCumTime = 0; -float musCumTime = 0; - -// Move the player according to playerData.position -void movePlayer() -{ - // Move the player into place. TODO: This isn't really input-related - // at all, and should be moved. - with(*playerData.position) - { - ogre_moveCamera(position[0], position[1], position[2]); - ogre_setCameraRotation(rotation[0], rotation[1], rotation[2]); - - bullet_movePlayer(position[0], position[1], position[2]); - } -} - -void initializeInput() -{ - movePlayer(); - - // TODO/FIXME: This should have been in config, but DMD's module - // system is on the brink of collapsing, and it won't compile if I - // put another import in core.config. I should probably check the - // bug list and report it. - updateMouseSensitivity(); - - // Get a pointer to the 'guiMode' flag in cpp_ogre.cpp - guiMode = gui_getGuiModePtr(); -} - -extern(C) int ois_isPressed(int keysym); - -// Check if a key is currently down -bool isPressed(Keys key) -{ - KeyBind *b = &keyBindings.bindings[key]; - foreach(i; b.syms) - if(i != 0 && ois_isPressed(i)) return true; - return false; -} - -// Enable superman mode, ie. flight and super-speed. Only used for -// debugging the terrain mode. -extern(C) void d_terr_superman() -{ - bullet_fly(); - speed = 8000; - - with(*playerData.position) - { - position[0] = 20000; - position[1] = -70000; - position[2] = 30000; - } - movePlayer(); -} - -extern(C) int d_frameStarted(float time) -{ - if(doExit) return 0; - - dbg.trace("d_frameStarted"); - scope(exit) dbg.untrace(); - - // Run the Monster scheduler - vm.frame(time); - - musCumTime += time; - if(musCumTime > musRefresh) - { - Music.updateBuffers(); - musCumTime -= musRefresh; - } - - // The rest is ignored in pause or GUI mode - if(pause || *guiMode > 0) return 1; - - // Check if the movement keys are pressed - float moveX = 0, moveY = 0, moveZ = 0; - float x, y, z, ox, oy, oz; - - if(isPressed(Keys.MoveLeft)) moveX -= speed; - if(isPressed(Keys.MoveRight)) moveX += speed; - if(isPressed(Keys.MoveForward)) moveZ -= speed; - if(isPressed(Keys.MoveBackward)) moveZ += speed; - - // TODO: These should be enabled for floating modes (like swimming - // and levitation) and disabled for everything else. - if(isPressed(Keys.MoveUp)) moveY += speed; - if(isPressed(Keys.MoveDown)) moveY -= speed; - - // This isn't very elegant, but it's simple and it works. - - // Get the current coordinates - ogre_getCameraPos(&ox, &oy, &oz); - - // Move camera using relative coordinates. TODO: We won't really - // need to move the camera here (since it's moved below anyway), we - // only want the transformation from camera space to world - // space. This can likely be done more efficiently. - ogre_moveCameraRel(moveX, moveY, moveZ); - - // Get the result - ogre_getCameraPos(&x, &y, &z); - - // The result is the real movement direction, in world coordinates - moveX = x-ox; - moveY = y-oy; - moveZ = z-oz; - - // Tell Bullet that this is where we want to go - bullet_setPlayerDir(moveX, moveY, moveZ); - - // Perform a Bullet time step - bullet_timeStep(time); - - // Get the final (actual) player position and update the camera - bullet_getPlayerPos(&x, &y, &z); - ogre_moveCamera(x,y,z); - - // Store it in the player object - playerData.position.position[0] = x; - playerData.position.position[1] = y; - playerData.position.position[2] = z; - - if(!config.noSound) - { - // Tell the sound scene that the player has moved - sndCumTime += time; - if(sndCumTime > sndRefresh) - { - float fx, fy, fz; - float ux, uy, uz; - - ogre_getCameraOrientation(&fx, &fy, &fz, &ux, &uy, &uz); - - soundScene.update(x,y,z,fx,fy,fz,ux,uy,uz); - sndCumTime -= sndRefresh; - } - } - return 1; -} - -bool collides = false; diff --git a/old_d_version/input/ois.d b/old_d_version/input/ois.d deleted file mode 100644 index 60129cadf..000000000 --- a/old_d_version/input/ois.d +++ /dev/null @@ -1,428 +0,0 @@ -/* - OpenMW - The completely unofficial reimplementation of Morrowind - Copyright (C) 2008 Nicolay Korslund - Email: < korslund@gmail.com > - WWW: http://openmw.snaptoad.com/ - - This file (ois.d) is part of the OpenMW package. - - OpenMW is distributed as free software: you can redistribute it - and/or modify it under the terms of the GNU General Public License - version 3, as published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - version 3 along with this program. If not, see - http://www.gnu.org/licenses/ . - - */ - -module input.ois; - -// Mouse buttons -enum MB : int - { - Button0 = 0, - Left = Button0, - Button1 = 1, - Right = Button1, - Button2 = 2, - Middle = Button2, - - Button3 = 3, - Button4 = 4, - Button5 = 5, - Button6 = 6, - Button7 = 7, - - LastMouse - } - - // Keyboard scan codes -enum KC : int - { - UNASSIGNED = 0x00, - ESCAPE = 0x01, - N1 = 0x02, - N2 = 0x03, - N3 = 0x04, - N4 = 0x05, - N5 = 0x06, - N6 = 0x07, - N7 = 0x08, - N8 = 0x09, - N9 = 0x0A, - N0 = 0x0B, - MINUS = 0x0C, // - on main keyboard - EQUALS = 0x0D, - BACK = 0x0E, // backspace - TAB = 0x0F, - Q = 0x10, - W = 0x11, - E = 0x12, - R = 0x13, - T = 0x14, - Y = 0x15, - U = 0x16, - I = 0x17, - O = 0x18, - P = 0x19, - LBRACKET = 0x1A, - RBRACKET = 0x1B, - RETURN = 0x1C, // Enter on main keyboard - LCONTROL = 0x1D, - A = 0x1E, - S = 0x1F, - D = 0x20, - F = 0x21, - G = 0x22, - H = 0x23, - J = 0x24, - K = 0x25, - L = 0x26, - SEMICOLON = 0x27, - APOSTROPHE = 0x28, - GRAVE = 0x29, // accent - LSHIFT = 0x2A, - BACKSLASH = 0x2B, - Z = 0x2C, - X = 0x2D, - C = 0x2E, - V = 0x2F, - B = 0x30, - N = 0x31, - M = 0x32, - COMMA = 0x33, - PERIOD = 0x34, // . on main keyboard - SLASH = 0x35, // / on main keyboard - RSHIFT = 0x36, - MULTIPLY = 0x37, // * on numeric keypad - LMENU = 0x38, // left Alt - SPACE = 0x39, - CAPITAL = 0x3A, - F1 = 0x3B, - F2 = 0x3C, - F3 = 0x3D, - F4 = 0x3E, - F5 = 0x3F, - F6 = 0x40, - F7 = 0x41, - F8 = 0x42, - F9 = 0x43, - F10 = 0x44, - NUMLOCK = 0x45, - SCROLL = 0x46, // Scroll Lock - NUMPAD7 = 0x47, - NUMPAD8 = 0x48, - NUMPAD9 = 0x49, - SUBTRACT = 0x4A, // - on numeric keypad - NUMPAD4 = 0x4B, - NUMPAD5 = 0x4C, - NUMPAD6 = 0x4D, - ADD = 0x4E, // + on numeric keypad - NUMPAD1 = 0x4F, - NUMPAD2 = 0x50, - NUMPAD3 = 0x51, - NUMPAD0 = 0x52, - DECIMAL = 0x53, // . on numeric keypad - OEM_102 = 0x56, // < > | on UK/Germany keyboards - F11 = 0x57, - F12 = 0x58, - F13 = 0x64, // (NEC PC98) - F14 = 0x65, // (NEC PC98) - F15 = 0x66, // (NEC PC98) - KANA = 0x70, // (Japanese keyboard) - ABNT_C1 = 0x73, // / ? on Portugese (Brazilian) keyboards - CONVERT = 0x79, // (Japanese keyboard) - NOCONVERT = 0x7B, // (Japanese keyboard) - YEN = 0x7D, // (Japanese keyboard) - ABNT_C2 = 0x7E, // Numpad . on Portugese (Brazilian) keyboards - NUMPADEQUALS= 0x8D, // = on numeric keypad (NEC PC98) - PREVTRACK = 0x90, // Previous Track (CIRCUMFLEX on Japanese keyboard) - AT = 0x91, // (NEC PC98) - COLON = 0x92, // (NEC PC98) - UNDERLINE = 0x93, // (NEC PC98) - KANJI = 0x94, // (Japanese keyboard) - STOP = 0x95, // (NEC PC98) - AX = 0x96, // (Japan AX) - UNLABELED = 0x97, // (J3100) - NEXTTRACK = 0x99, // Next Track - NUMPADENTER = 0x9C, // Enter on numeric keypad - RCONTROL = 0x9D, - MUTE = 0xA0, // Mute - CALCULATOR = 0xA1, // Calculator - PLAYPAUSE = 0xA2, // Play / Pause - MEDIASTOP = 0xA4, // Media Stop - VOLUMEDOWN = 0xAE, // Volume - - VOLUMEUP = 0xB0, // Volume + - WEBHOME = 0xB2, // Web home - NUMPADCOMMA = 0xB3, // , on numeric keypad (NEC PC98) - DIVIDE = 0xB5, // / on numeric keypad - SYSRQ = 0xB7, // Also called print screen - RMENU = 0xB8, // right Alt - PAUSE = 0xC5, // Pause - HOME = 0xC7, // Home on arrow keypad - UP = 0xC8, // UpArrow on arrow keypad - PGUP = 0xC9, // PgUp on arrow keypad - LEFT = 0xCB, // LeftArrow on arrow keypad - RIGHT = 0xCD, // RightArrow on arrow keypad - END = 0xCF, // End on arrow keypad - DOWN = 0xD0, // DownArrow on arrow keypad - PGDOWN = 0xD1, // PgDn on arrow keypad - INSERT = 0xD2, // Insert on arrow keypad - DELETE = 0xD3, // Delete on arrow keypad - LWIN = 0xDB, // Left Windows key - RWIN = 0xDC, // Right Windows key - APPS = 0xDD, // AppMenu key - POWER = 0xDE, // System Power - SLEEP = 0xDF, // System Sleep - WAKE = 0xE3, // System Wake - WEBSEARCH = 0xE5, // Web Search - WEBFAVORITES= 0xE6, // Web Favorites - WEBREFRESH = 0xE7, // Web Refresh - WEBSTOP = 0xE8, // Web Stop - WEBFORWARD = 0xE9, // Web Forward - WEBBACK = 0xEA, // Web Back - MYCOMPUTER = 0xEB, // My Computer - MAIL = 0xEC, // Mail - MEDIASELECT = 0xED, // Media Select - - CharOnly = 0xFF, // Set when the keysym is 0 but the - // character is set. This happens with many - // international characters or reassigned - // characters. - - Mouse0 = 0x100, // Mouse button events can be handled as - Mouse1 = 0x101, // keypresses too. - Mouse2 = 0x102, - Mouse3 = 0x103, - Mouse4 = 0x104, - Mouse5 = 0x105, - Mouse6 = 0x106, - Mouse7 = 0x107, - } - -// Sigh. I guess we have to do this for Monster at some poing anyway, -// so this work isn't completely wasted. Later we can make a generic -// conversion between OIS-keysyms, SDL-keysyms and others to the -// Monster keysyms. It sucks that everybody tries to reinvent the -// wheel as often as they can, but that's the way it goes. - -const char[][] keysymToString = -[ - KC.UNASSIGNED : "UNASSIGNED", - KC.ESCAPE : "escape", - KC.N1 : "1", - KC.N2 : "2", - KC.N3 : "3", - KC.N4 : "4", - KC.N5 : "5", - KC.N6 : "6", - KC.N7 : "7", - KC.N8 : "8", - KC.N9 : "9", - KC.N0 : "0", - KC.MINUS : "minus", - KC.EQUALS : "equals", - KC.BACK : "backspace", - KC.TAB : "tab", - KC.Q : "q", - KC.W : "w", - KC.E : "e", - KC.R : "r", - KC.T : "t", - KC.Y : "y", - KC.U : "u", - KC.I : "i", - KC.O : "o", - KC.P : "p", - KC.LBRACKET : "{", - KC.RBRACKET : "}", - KC.RETURN : "enter", - KC.LCONTROL : "left_ctrl", - KC.A : "a", - KC.S : "s", - KC.D : "d", - KC.F : "f", - KC.G : "g", - KC.H : "h", - KC.J : "j", - KC.K : "k", - KC.L : "l", - KC.SEMICOLON : "semicolon", - KC.APOSTROPHE : "apostrophe", - KC.GRAVE : "grave", - KC.LSHIFT : "left_shift", - KC.BACKSLASH : "backslash", - KC.Z : "z", - KC.X : "x", - KC.C : "c", - KC.V : "v", - KC.B : "b", - KC.N : "n", - KC.M : "m", - KC.COMMA : "comma", - KC.PERIOD : "period", - KC.SLASH : "slash", - KC.RSHIFT : "right_shift", - KC.MULTIPLY : "numpad_mult", - KC.LMENU : "left_alt", - KC.SPACE : "space", - KC.CAPITAL : "capital", - KC.F1 : "f1", - KC.F2 : "f2", - KC.F3 : "f3", - KC.F4 : "f4", - KC.F5 : "f5", - KC.F6 : "f6", - KC.F7 : "f7", - KC.F8 : "f8", - KC.F9 : "f9", - KC.F10 : "f10", - KC.NUMLOCK : "numlock", - KC.SCROLL : "scroll", - KC.NUMPAD7 : "numpad_7", - KC.NUMPAD8 : "numpad_8", - KC.NUMPAD9 : "numpad_9", - KC.SUBTRACT : "numpad_minus", - KC.NUMPAD4 : "numpad_4", - KC.NUMPAD5 : "numpad_5", - KC.NUMPAD6 : "numpad_6", - KC.ADD : "numpad_plus", - KC.NUMPAD1 : "numpad_1", - KC.NUMPAD2 : "numpad_2", - KC.NUMPAD3 : "numpad_3", - KC.NUMPAD0 : "numpad_0", - KC.DECIMAL : "numpad_period", - KC.OEM_102 : "oem102", - KC.F11 : "f11", - KC.F12 : "f12", - KC.F13 : "f13", - KC.F14 : "f14", - KC.F15 : "f15", - KC.KANA : "kana", - KC.ABNT_C1 : "abnt_c1", - KC.CONVERT : "convert", - KC.NOCONVERT : "noconvert", - KC.YEN : "yen", - KC.ABNT_C2 : "abnt_c2", - KC.NUMPADEQUALS: "numpad_equals", - KC.PREVTRACK : "prev_track", - KC.AT : "at", - KC.COLON : "colon", - KC.UNDERLINE : "underline", - KC.KANJI : "kanji", - KC.STOP : "stop", - KC.AX : "ax", - KC.UNLABELED : "unlabeled", - KC.NEXTTRACK : "next_track", - KC.NUMPADENTER : "numpad_enter", - KC.RCONTROL : "right_control", - KC.MUTE : "mute", - KC.CALCULATOR : "calculator", - KC.PLAYPAUSE : "play_pause", - KC.MEDIASTOP : "media_stop", - KC.VOLUMEDOWN : "volume_down", - KC.VOLUMEUP : "volume_up", - KC.WEBHOME : "webhome", - KC.NUMPADCOMMA : "numpad_comma", - KC.DIVIDE : "numpad_divide", - KC.SYSRQ : "print_screen", - KC.RMENU : "right_alt", - KC.PAUSE : "pause", - KC.HOME : "home", - KC.UP : "up", - KC.PGUP : "page_up", - KC.LEFT : "left", - KC.RIGHT : "right", - KC.END : "end", - KC.DOWN : "down", - KC.PGDOWN : "page_down", - KC.INSERT : "insert", - KC.DELETE : "delete", - KC.LWIN : "left_win", - KC.RWIN : "right_win", - KC.APPS : "app_menu", - KC.POWER : "power", - KC.SLEEP : "sleep", - KC.WAKE : "wake", - KC.WEBSEARCH : "web_search", - KC.WEBFAVORITES: "web_favorites", - KC.WEBREFRESH : "web_refresh", - KC.WEBSTOP : "web_stop", - KC.WEBFORWARD : "web_forward", - KC.WEBBACK : "web_back", - KC.MYCOMPUTER : "my_computer", - KC.MAIL : "mail", - KC.MEDIASELECT : "media_select", - - - KC.CharOnly : "CHAR_ONLY", // Set when the keysym is 0 but the - // character is set. This happens - // with many international - // characters or reassigned - // characters in OIS (and it - // SUCKS.) - - KC.Mouse0 : "mouse0", - KC.Mouse1 : "mouse1", - KC.Mouse2 : "mouse2", - KC.Mouse3 : "mouse3", - KC.Mouse4 : "mouse4", - KC.Mouse5 : "mouse5", - KC.Mouse6 : "mouse6", - KC.Mouse7 : "mouse7", - ]; - -enum ComponentType : int - { - Unknown = 0, - Button = 1, // ie. Key, mouse button, joy button, etc - Axis = 2, // ie. A joystick or mouse axis - Slider = 3, // - POV = 4, // ie. Arrow direction keys - Vector3 = 5 // ie. WiiMote orientation - } - -align(4) struct Axis -{ - ComponentType type; - int abs, rel; - bool absOnly; -} - -// The C++ size of Axis is 16 -static assert(Axis.sizeof == 16); - -struct MouseState -{ - /* Represents the height/width of your display area.. used if mouse - clipping or mouse grabbed in case of X11 - defaults to 50.. Make - sure to set this and change when your size changes.. */ - int width, height; - - // X Axis component - Axis X; - - // Y Axis Component - Axis Y; - - // Z Axis Component - Axis Z; - - // represents all buttons - bit position indicates button down - int buttons; - - // Button down test - bool buttonDown( MB button ) - { - return (buttons & ( 1 << button )) != 0; - } -} - -// Check that we match the C++ size -static assert(MouseState.sizeof == 60); diff --git a/old_d_version/ogre/cpp_framelistener.cpp b/old_d_version/ogre/cpp_framelistener.cpp deleted file mode 100644 index 8c7a37044..000000000 --- a/old_d_version/ogre/cpp_framelistener.cpp +++ /dev/null @@ -1,41 +0,0 @@ -// Get current camera orientation, in the form of 'front' and 'up' -// vectors. -extern "C" void ogre_getCameraOrientation(float *fx, float *fy, float *fz, - float *ux, float *uy, float *uz) -{ - Vector3 front = mCamera->getDirection(); - Vector3 up = mCamera->getUp(); - *fx = front[0]; - *fy = -front[2]; - *fz = front[1]; - *ux = up[0]; - *uy = -up[2]; - *uz = up[1]; -} - -// Move camera -extern "C" void ogre_moveCamera(float x, float y, float z) -{ - // Transforms Morrowind coordinates to OGRE coordinates. The camera - // is not affected by the rotation of the root node, so we must - // transform this manually. - mCamera->setPosition(Vector3(x,z+90,-y)); -} - -// Rotate camera using Morrowind rotation specifiers -extern "C" void ogre_setCameraRotation(float r1, float r2, float r3) -{ - // TODO: This translation is probably not correct, but for now I - // have no reference point. Fix it later when we teleport from one - // cell to another, so we have something to compare against. - - // Rotate around X axis - Quaternion xr(Radian(-r1), Vector3::UNIT_X); - // Rotate around Y axis - Quaternion yr(Radian(r3+3.14), Vector3::UNIT_Y); - // Rotate around Z axis - Quaternion zr(Radian(-r2), Vector3::UNIT_Z); - - // Rotates first around z, then y, then x - mCamera->setOrientation(xr*yr*zr); -} diff --git a/old_d_version/ogre/cpp_interface.cpp b/old_d_version/ogre/cpp_interface.cpp deleted file mode 100644 index 98df9125e..000000000 --- a/old_d_version/ogre/cpp_interface.cpp +++ /dev/null @@ -1,135 +0,0 @@ -// Copy a scene node and all its children -void cloneNode(SceneNode *from, SceneNode *to, char* name) -{ - to->setPosition(from->getPosition()); - to->setOrientation(from->getOrientation()); - to->setScale(from->getScale()); - - SceneNode::ObjectIterator it = from->getAttachedObjectIterator(); - while(it.hasMoreElements()) - { - // We can't handle non-entities. - Entity *e = dynamic_cast (it.getNext()); - if(e) - { - e = e->clone(String(name) + ":" + e->getName()); - to->attachObject(e); - } - } - - // Recursively clone all child nodes - SceneNode::ChildNodeIterator it2 = from->getChildIterator(); - while(it2.hasMoreElements()) - { - cloneNode((SceneNode*)it2.getNext(), to->createChildSceneNode(), name); - } -} - -// Supposed to insert a copy of the node, for now it just inserts the -// actual node. -extern "C" SceneNode *ogre_insertNode(SceneNode *base, char* name, - float *pos, float *quat, - float scale) -{ - //std::cout << "ogre_insertNode(" << name << ")\n"; - SceneNode *node = mwRoot->createChildSceneNode(name); - - // Make a copy of the node - cloneNode(base, node, name); - - // Apply transformations - node->setPosition(pos[0], pos[1], pos[2]); - node->setOrientation(quat[0], quat[1], quat[2], quat[3]); - - node->setScale(scale, scale, scale); - - return node; -} - -// Get the world transformation of a node (the total transformation of -// this node and all parent nodes). Return it as a translation -// (3-vector) and a rotation / scaling part (3x3 matrix) -extern "C" void ogre_getWorldTransform(SceneNode *node, - float *trans, // Storage for translation - float *matrix)// For 3x3 matrix -{ - // Get the world transformation first - Matrix4 trafo; - node->getWorldTransforms(&trafo); - - // Extract the translation part and pass it to the caller - Vector3 tr = trafo.getTrans(); - trans[0] = tr[0]; - trans[1] = tr[1]; - trans[2] = tr[2]; - - // Next extract the matrix - Matrix3 mat; - trafo.extract3x3Matrix(mat); - matrix[0] = mat[0][0]; - matrix[1] = mat[0][1]; - matrix[2] = mat[0][2]; - matrix[3] = mat[1][0]; - matrix[4] = mat[1][1]; - matrix[5] = mat[1][2]; - matrix[6] = mat[2][0]; - matrix[7] = mat[2][1]; - matrix[8] = mat[2][2]; -} - -// Create the water plane. It doesn't really resemble "water" yet -// though. -extern "C" void ogre_createWater(float level) -{ - // Create a plane aligned with the xy-plane. - MeshManager::getSingleton().createPlane("water", - ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - Plane(Vector3::UNIT_Z, level), - 150000,150000 - ); - Entity *ent = mSceneMgr->createEntity( "WaterEntity", "water" ); - mwRoot->createChildSceneNode()->attachObject(ent); - ent->setCastShadows(false); -} - -extern "C" SceneNode *ogre_getDetachedNode() -{ - SceneNode *node = mwRoot->createChildSceneNode(); - mwRoot->removeChild(node); - return node; -} - -extern "C" SceneNode* ogre_createNode( - char *name, - float *trafo, - SceneNode *parent, - int32_t noRot) -{ - //std::cout << "ogre_createNode(" << name << ")"; - SceneNode *node = parent->createChildSceneNode(name); - //std::cout << " ... done\n"; - - // First is the translation vector - - // TODO should be "if(!noRot)" only for exterior cells!? Yay for - // consistency. Apparently, the displacement of the base node in NIF - // files must be ignored for meshes in interior cells, but not for - // exterior cells. Or at least that's my hypothesis, and it seems - // work. There might be some other NIF trickery going on though, you - // never know when you're reverse engineering someone else's file - // format. We will handle this later. - if(!noRot) - node->setPosition(trafo[0], trafo[1], trafo[2]); - - // Then a 3x3 rotation matrix. - if(!noRot) - node->setOrientation(Quaternion(Matrix3(trafo[3], trafo[4], trafo[5], - trafo[6], trafo[7], trafo[8], - trafo[9], trafo[10], trafo[11] - ))); - - // Scale is at the end - node->setScale(trafo[12],trafo[12],trafo[12]); - - return node; -} diff --git a/old_d_version/ogre/meshloader.d b/old_d_version/ogre/meshloader.d deleted file mode 100644 index 5065cc87a..000000000 --- a/old_d_version/ogre/meshloader.d +++ /dev/null @@ -1,391 +0,0 @@ -/* - OpenMW - The completely unofficial reimplementation of Morrowind - Copyright (C) 2008 Nicolay Korslund - Email: < korslund@gmail.com > - WWW: http://openmw.snaptoad.com/ - - This file (meshloader.d) is part of the OpenMW package. - - OpenMW is distributed as free software: you can redistribute it - and/or modify it under the terms of the GNU General Public License - version 3, as published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - version 3 along with this program. If not, see - http://www.gnu.org/licenses/ . - - */ - -module ogre.meshloader; - -import std.stdio; -import std.stream; - -import nif.nif; -import nif.record; - -import core.resource; -import ogre.bindings; - -import bullet.bindings; - -import util.uniquename; - -/* - There are some problems that will have to be looked into later: - - - Some meshes crash Ogre when shadows are turned on. (Not tested in - newer versions of Ogre). Shadows are completely disabled for now. - - There are obviously some boundry problems, some times the mesh - disappears even though some part of it is inside the screen. This - is especially a problem with animated meshes, since the animation - might step outside the original bounding box. - */ - -MeshLoader meshLoader; - -struct MeshLoader -{ - // Not sure how to handle the bounding box, just ignore it for now. - - char[] baseName; // NIF file name. Used in scene node names etc. so - // that we can identify where they came from in - // case of error messages. - - // Load a NIF mesh. Assumes nifMesh is already opened. This creates - // a "template" scene node containing this mesh, and removes it from - // the main scene. This node can later be "cloned" so that multiple - // instances of the object can be inserted into the world without - // inserting the mesh more than once. - void loadMesh(char[] name, out NodePtr base, out BulletShape shape) - { - baseName = name; - - // Check if the first record is a node - Node n = cast(Node) nifMesh.records[0]; - - if(n is null) - { - // TODO: Figure out what to do in this case, we should - // probably throw. - writefln("NIF '%s' IS NOT A MESH", name); - return; - } - - // Get a fresh SceneNode and detatch it from the root. We use this - // as the base for our mesh. - base = ogre_getDetachedNode(); - - // Recursively insert nodes (don't rotate the first node) - insertNode(n, base, 0, true); - - // Get the final shape, if any - shape = bullet_getFinalShape(); - - return base; - } - - private: - - void insertNode(Node data, NodePtr parent, - int flags, - bool noRot = false) - { - // Add the flags to the previous node's flags - flags = data.flags | flags; - - // Create a scene node, move and rotate it into place. The name - // must be unique, however we might have to recognize some special - // names later, in order to attach arms and legs on NPCs - // etc. Always ignore transformation of the first node? This is a - // problem, I don't know when to do this and when not to. Neither - // is always right. Update: I originally thought noRot should be - // false for exteriors and true for interiors, but this isn't so. - NodePtr node = ogre_createNode(UniqueName(data.name).ptr, &data.trafo, - parent, cast(int)noRot); - - // Handle any general properties here - - // Call functions that do node-specific things, like handleNiNode - // or handleNiTriShape. - { - NiNode n = cast(NiNode)data; - if(n !is null) - // Handle the NiNode, and any children it might have - handleNiNode(n, node, flags); - } - - { - NiTriShape n = cast(NiTriShape)data; - if(n !is null) - // Trishape, with a mesh - handleNiTriShape(n, node, flags); - } - } - - void handleNiNode(NiNode data, NodePtr node, int flags) - { - // Ignore sound activators and similar objects. - NiStringExtraData d = cast(NiStringExtraData) data.extra; - if(d !is null) - { - // Marker objects are only visible in the editor. We - // completely ignore them. - if(d.string == "MRK") - return; - - // No collision - if(d.string == "NCO") - flags |= 0x800; // Temporary internal marker - } - - // Handle any effects here - - // In the cases where meshes have skeletal animations, we must - // insert the children as bones in a skeleton instead, like we - // originally did for all nodes. Update: A much better way is to - // first insert the nodes normally, and then create the - // skeleton. The nodes can then be moved one by one over to the - // appropriate bones. - - // Check the controller - auto cont = data.controller; - while(cont !is null) - { - auto kc = cast(NiKeyframeController)cont; - auto pc = cast(NiPathController)cont; - if(kc !is null) - { - /* - writefln("Found keyframe controller"); - writefln(" Node name was: %s", data.name); - assert(cont.target is data); - - auto kcd = kc.data; - writefln(" Types: %s %s %s", - kcd.rotType, kcd.traType, kcd.scaleType); - */ - - /* - Adding keyframes: - - Skeleton -> Animation -> NodeAnimationTrack nt; - - TransformKeyFrame * tf = nt->createNodeKeyFrame(time); - tf->setTranslate(Vector3); - tf->setScale(Vector3); - tf->setRotation(Quaternion); - - nt->applyToNode(node, time); - evt - Animation an; - an->apply(skeleton, time); - */ - } - else if(pc !is null) - { - //writefln("Found path controller"); - assert(cont.target is data); - } - //else writefln("Other controller (%s)", cont); - cont = cont.next; - } - - // Loop through children - foreach(Node n; data.children) - insertNode(n, node, flags); - } - - void handleNiTriShape(NiTriShape shape, NodePtr node, int flags) - { - char[] texture; - char[] material; - char[] newName = UniqueName(baseName); - NiMaterialProperty mp; - - // Special alpha settings, if the NiAlphaProperty is present - int alphaFlags = -1; - ubyte alphaTest; - - bool hidden = (flags & 0x01) != 0; // Not displayed - bool collide = (flags & 0x02) != 0; // Use this mesh for collision - bool bbcollide = (flags & 0x04) != 0; // Use bounding box for - // collision - // Always use mesh collision for now - if(bbcollide) collide = true; - bbcollide = false; - - // Things marked "NCO" should not collide with anything. - if(flags & 0x800) - { collide = false; bbcollide=false; } - - // Skip the entire material phase for hidden nodes - if(hidden) goto nomaterial; - - // Scan the property list for textures - foreach(Property p; shape.properties) - { - // NiTexturingProperty block - { - NiTexturingProperty t = cast(NiTexturingProperty) p; - if(t !is null && t.textures[0].inUse) - { - // Ignore all other options for now - NiSourceTexture st = t.textures[0].texture; - if(st.external) - { - // Find the resource for this texture - TextureIndex ti = resources.lookupTexture(st.filename); - // Insert a manual loader into OGRE - // ti.load(); - - // Get the resource name. We use getNewName to get - // the real texture name, not the lookup - // name. NewName has been converted to .dds if - // necessary, to match the file name in the bsa - // archives. - texture = ti.getNewName(); - } - else - { - // Internal textures - texture = "BLAH"; - writefln("Internal texture, cannot read this yet."); - writefln("Final resource name: '%s'", texture); - } - continue; - } - } - - // NiAlphaProperty - { - NiAlphaProperty a = cast(NiAlphaProperty) p; - if(a !is null) - { - alphaFlags = a.flags; - alphaTest = a.threshold; - } - } - - // NiMaterialProperty block - { - NiMaterialProperty tmp = cast(NiMaterialProperty) p; - if(tmp !is null) - { - if(mp !is null) writefln("WARNING: More than one material!"); - mp = tmp; - material = newName; - continue; - } - } - } - // Get a pointer to the texture name - char* texturePtr; - if(texture.length) texturePtr = toStringz(texture); - else texturePtr = null; - - // Create the material - if(material.length) - // A material is present. Use it. - ogre_createMaterial(material.ptr, mp.ambient.array.ptr, - mp.diffuse.array.ptr, - mp.specular.array.ptr, mp.emissive.array.ptr, - mp.glossiness, mp.alpha, texturePtr, - alphaFlags, alphaTest); - else if(texturePtr) - { - // Texture, but no material. Make a default one. - writefln("WARNING: Making default material for %s", texture); - float[3] zero; - float[3] one; - zero[] = 0.0; - one[] = 1.0; - - ogre_createMaterial(newName.ptr, one.ptr, one.ptr, zero.ptr, zero.ptr, - 0.0, 1.0, texturePtr, alphaFlags, alphaTest); - } - - nomaterial: - - with(shape.data) - { - //writefln("Number of vertices: ", vertices.length); - - float *normalsPtr; - float *colorsPtr; - float *uvsPtr; - short *facesPtr; - - // Point pointers into the correct arrays, if they are present. If - // not, the pointers retain their values of null. - if(normals.length) normalsPtr = normals.ptr; - if(colors.length) colorsPtr = colors.ptr; - if(uvlist.length) uvsPtr = uvlist.ptr; - if(triangles.length) facesPtr = triangles.ptr; - - float - minX = float.infinity, - minY = float.infinity, - minZ = float.infinity, - maxX = -float.infinity, - maxY = -float.infinity, - maxZ = -float.infinity; - - // Calculate the bounding box. TODO: This is really a - // hack. IIRC the bounding box supplied by the NIF could not - // be trusted, but I can't remember why :/ - for( int i; i < vertices.length; i+=3 ) - { - if( vertices[i] < minX ) minX = vertices[i]; - if( vertices[i+1] < minY ) minY = vertices[i+1]; - if( vertices[i+2] < minZ) minZ = vertices[i+2]; - - if( vertices[i] > maxX) maxX = vertices[i]; - if( vertices[i+1] > maxY) maxY = vertices[i+1]; - if( vertices[i+2] > maxZ) maxZ = vertices[i+2]; - } - - // Get the node world transformation, needed to set up - // the collision shape properly. - float[3] trans; - float[9] matrix; - ogre_getWorldTransform(node, trans.ptr, matrix.ptr); - - // Next we must create the actual OGRE mesh and the collision - // objects, based on the flags we have been given. TODO: I - // guess the NIF bounding box is better than the one we have - // calculated ourselves? This code definitely doesn't work, - // but I haven't look into why yet. - assert(!bbcollide); - if(bbcollide) - // Insert the bounding box into the collision system - bullet_createBoxShape(minX, minY, minZ, maxX, maxY, maxZ, - trans.ptr, matrix.ptr); - - // Create a bullet collision shape from the trimesh. Pass - // along the world transformation as well, since we must - // transform the trimesh data manually. - else if(collide) - { - assert(facesPtr !is null, - "cannot create collision shape without a mesh"); - bullet_createTriShape(triangles.length, facesPtr, - vertices.length, vertices.ptr, - trans.ptr, matrix.ptr); - } - - // Create the ogre mesh, associate it with the node. Skip for - // hidden nodes. - if(!hidden) - ogre_createMesh(newName.ptr, vertices.length, vertices.ptr, - normalsPtr, colorsPtr, uvsPtr, triangles.length, - facesPtr, radius, material.ptr, minX, minY, minZ, - maxX, maxY, maxZ, node); - } - } -} diff --git a/old_d_version/openmw.d b/old_d_version/openmw.d deleted file mode 100644 index 095e8cdd8..000000000 --- a/old_d_version/openmw.d +++ /dev/null @@ -1,471 +0,0 @@ -/* - OpenMW - The completely unofficial reimplementation of Morrowind - Copyright (C) 2008-2009 Nicolay Korslund - Email: < korslund@gmail.com > - WWW: http://openmw.snaptoad.com/ - - This file (openmw.d) is part of the OpenMW package. - - OpenMW is distributed as free software: you can redistribute it - and/or modify it under the terms of the GNU General Public License - version 3, as published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - version 3 along with this program. If not, see - http://www.gnu.org/licenses/ . - - */ - -module openmw; - -import std.stdio; -import std.string; -import std.cstream; -import std.file; - -import ogre.ogre; -import ogre.bindings; -import gui.bindings; -import gui.gui; - -import bullet.bullet; - -import scene.celldata; -import scene.soundlist; -import scene.gamesettings; -import scene.player; - -import core.resource; -import core.memory; -import core.config; - -import monster.util.string; -import monster.vm.mclass; -import monster.vm.dbg; -import mscripts.setup; - -import sound.audio; - -import input.events; - -import terrain.terrain; - -// Set up exit handler -alias void function() c_func; -extern(C) int atexit(c_func); - -bool cleanExit = false; - -void exitHandler() -{ - // If we exit uncleanly, print the function stack. - if(!cleanExit) - writefln(dbg.getTrace()); -} - - -//* -import std.gc; -import gcstats; - -void poolSize() -{ - GCStats gc; - getStats(gc); - writefln("Pool size: ", comma(gc.poolsize)); - writefln("Used size: ", comma(gc.usedsize)); -} -//*/ - -void main(char[][] args) -{ - bool render = true; - bool help = false; - bool resetKeys = false; - bool showOgreFlag = false; - bool debugOut = false; - bool extTest = false; - bool doGen = false; - bool nextSave = false; - bool loadSave = false; - - // Some examples to try: - // - // "Abandoned Shipwreck, Upper Level"; - // "Gro-Bagrat Plantation"; - // "Abinabi"; - // "Abebaal Egg Mine"; - // "Ald-ruhn, Ald Skar Inn"; - // "Koal Cave"; - // "Ald-ruhn, Arobar Manor Bedrooms" - // "Sud"; - // "Vivec, The Lizard's Head"; - // "ToddTest"; - - // Cells to load - char[][] cells; - - // Savegame to load - char[] savefile; - - foreach(char[] a; args[1..$]) - if(a == "-n") render = false; - else if(a == "-ex") extTest = true; - else if(a == "-gen") doGen = true; - else if(a == "-h") help=true; - else if(a == "-rk") resetKeys = true; - else if(a == "-oc") showOgreFlag = true; - else if(a == "-ns") config.noSound = true; - else if(a == "-save") nextSave = true; - else if(a == "-debug") - { - // Enable Monster debug output - dbg.dbgOut = dout; - - // Tell OGRE to do the same later on - debugOut = true; - } - else if(nextSave) - { - savefile = a; - nextSave = false; - } - else cells ~= a; - - if(cells.length > 1) - { - writefln("More than one cell specified, rendering disabled"); - render=false; - } - - void showHelp() - { - writefln("Syntax: %s [options] cell-name [cell-name]", args[0]); - writefln(" Options:"); - writefln(" -n Only load, do not render"); - writefln(" -ex Test the terrain system"); - writefln(" -gen Generate landscape cache"); - writefln(" -rk Reset key bindings to default"); - writefln(" -oc Show the Ogre config dialogue"); - writefln(" -ns Completely disable sound"); - writefln(" -debug Print debug information"); - writefln(" -save Load cell/pos from savegame"); - writefln(" -h Show this help"); - writefln(""); - writefln("Specifying more than one cell implies -n"); - } - - if(help) - { - showHelp(); - return; - } - - initializeMemoryRegions(); - initMonsterScripts(); - - // This is getting increasingly hackish, but this entire engine - // design is now quickly outgrowing its usefulness, and a rewrite is - // coming soon anyway. - PlayerSaveInfo pi; - if(savefile != "") - { - if(cells.length) - { - writefln("Please don't specify both a savegame file (%s) and cell names (%s)", savefile, cells); - return; - } - - loadSave = true; - writefln("Loading savegame %s", savefile); - pi = importSavegame(savefile); - writefln(" Player name: %s", pi.playerName); - writefln(" Cell name: %s", pi.cellName); - writefln(" Pos: %s", pi.pos.position); - writefln(" Rot: %s", pi.pos.rotation); - - cells = [pi.cellName]; - } - - config.initialize(resetKeys); - scope(exit) config.writeConfig(); - - // Check if the data directory exists - if(!exists(config.dataDir) || !isdir(config.dataDir)) - { - writefln("Cannot find data directory '", config.dataDir, - "' - please edit openmw.ini."); - return; - } - - // If the -oc parameter is specified, we override any config - // setting. - if(showOgreFlag) config.finalOgreConfig = true; - - if(cells.length == 0) - if(config.defaultCell.length) - cells ~= config.defaultCell; - - if(cells.length == 1 && !loadSave) - config.defaultCell = cells[0]; - - if(cells.length == 0) - { - showHelp(); - return; - } - - if(!config.noSound) initializeSound(); - resources.initResources(); - - // Load all ESM and ESP files - loadTESFiles(config.gameFiles); - - scene.gamesettings.loadGameSettings(); - - CellData cd = cellList.get(); - - foreach(char[] cName; cells) - { - // Release the last cell data - cellList.release(cd); - - // Get a cell data holder and load an interior cell - cd = cellList.get(); - - try cd.loadIntCell(cName); - catch(Exception e) - { - writefln("\nUnable to load cell '%s'.", cName); - writefln("\nDetails: %s", e); - writefln(" -Perhaps this cell does not exist in your Morrowind language version? -Try specifying another cell name on the command line, or edit openmw.ini."); - return; - } - - // If we're loading from save, override the player position - if(loadSave) - *playerData.position = pi.pos; - } - - // Simple safety hack - NodePtr putObject(MeshIndex m, Placement *pos, float scale, - bool collide=false) - { - if(m is null) - writefln("WARNING: CANNOT PUT NULL OBJECT"); - else if(!m.isEmpty) - return placeObject(m, pos, scale, collide); - - //writefln("WARNING: CANNOT INSERT EMPTY MESH '%s'", m.getName); - return null; - } - - if(cd.inCell !is null) - // Set the name for the GUI (temporary hack) - gui_setCellName(cd.inCell.id.ptr); - - // Set up the exit handler - atexit(&exitHandler); - scope(exit) cleanExit = true; - - if(render) - { - // Warm up OGRE - setupOgre(debugOut); - scope(exit) cleanupOgre(); - - // Create the GUI system - initGUI(debugOut); - - // Set up Bullet - initBullet(); - scope(exit) cleanupBullet(); - - // Initialize the internal input and event manager. The - // lower-level input system (OIS) is initialized by the - // setupOgre() call further up. - initializeInput(); - - // Play some old tunes - if(extTest) - { - // Exterior cell - /* - Color c; - c.red = 180; - c.green = 180; - c.blue = 180; - setAmbient(c, c, c, 0); - - // Put in the water - ogre_createWater(cd.water); - - // Create an ugly sky - ogre_makeSky(); - */ - - initTerrain(doGen); - } - else - { - // Interior cell - assert(cd.inCell !is null); - setAmbient(cd.ambi.ambient, cd.ambi.sunlight, - cd.ambi.fog, cd.ambi.fogDensity); - - // Not all interior cells have water - if(cd.inCell.flags & CellFlags.HasWater) - ogre_createWater(cd.water); - - // Insert the meshes of statics into the scene - foreach(ref LiveStatic ls; cd.statics) - putObject(ls.m.model, ls.getPos(), ls.getScale(), true); - // Inventory lights - foreach(ref LiveLight ls; cd.lights) - { - NodePtr n = putObject(ls.m.model, ls.getPos(), ls.getScale()); - ls.lightNode = attachLight(n, ls.m.data.color, ls.m.data.radius); - if(!config.noSound) - { - Sound *s = ls.m.sound; - if(s) - { - ls.loopSound = soundScene.insert(s, true); - if(ls.loopSound) - { - auto p = ls.getPos(); - ls.loopSound.setPos(p.position[0], - p.position[1], - p.position[2]); - } - } - } - } - // Static lights - foreach(ref LiveLight ls; cd.statLights) - { - NodePtr n = putObject(ls.m.model, ls.getPos(), ls.getScale(), true); - ls.lightNode = attachLight(n, ls.m.data.color, ls.m.data.radius); - if(!config.noSound) - { - Sound *s = ls.m.sound; - if(s) - { - ls.loopSound = soundScene.insert(s, true); - if(ls.loopSound) - { - auto p = ls.getPos(); - ls.loopSound.setPos(p.position[0], - p.position[1], - p.position[2]); - } - } - } - } - // Misc items - foreach(ref LiveMisc ls; cd.miscItems) - putObject(ls.m.model, ls.getPos(), ls.getScale()); - /* - // NPCs (these are complicated, usually do not have normal meshes) - foreach(ref LiveNPC ls; cd.npcs) - putObject(ls.m.model, ls.getPos(), ls.getScale()); - */ - // Containers - foreach(ref LiveContainer ls; cd.containers) - putObject(ls.m.model, ls.getPos(), ls.getScale(), true); - // Doors - foreach(ref LiveDoor ls; cd.doors) - putObject(ls.m.model, ls.getPos(), ls.getScale()); - // Activators (including beds etc) - foreach(ref LiveActivator ls; cd.activators) - putObject(ls.m.model, ls.getPos(), ls.getScale(), true); - // Potions - foreach(ref LivePotion ls; cd.potions) - putObject(ls.m.model, ls.getPos(), ls.getScale()); - // Apparatus - foreach(ref LiveApparatus ls; cd.appas) - putObject(ls.m.model, ls.getPos(), ls.getScale()); - // Ingredients - foreach(ref LiveIngredient ls; cd.ingredients) - putObject(ls.m.model, ls.getPos(), ls.getScale()); - // Armors - foreach(ref LiveArmor ls; cd.armors) - putObject(ls.m.model, ls.getPos(), ls.getScale()); - // Weapons - foreach(ref LiveWeapon ls; cd.weapons) - putObject(ls.m.model, ls.getPos(), ls.getScale()); - // Books - foreach(ref LiveBook ls; cd.books) - putObject(ls.m.model, ls.getPos(), ls.getScale()); - // Clothes - foreach(ref LiveClothing ls; cd.clothes) - putObject(ls.m.model, ls.getPos(), ls.getScale()); - // Tools - foreach(ref LiveTool ls; cd.tools) - putObject(ls.m.model, ls.getPos(), ls.getScale()); - // Creatures (not displayed very well yet) - foreach(ref LiveCreature ls; cd.creatures) - putObject(ls.m.model, ls.getPos(), ls.getScale()); - - // End of interior cell - } - - // Run GUI system - startGUI(); - - // Play some old tunes - if(!config.noSound) - Music.play(); - - // Run it until the user tells us to quit - startRendering(); - } - else if(debugOut) writefln("Skipping rendering"); - - if(!config.noSound) - { - soundScene.kill(); - shutdownSound(); - } - - if(debugOut) - { - writefln(); - writefln("%d statics", cd.statics.length); - writefln("%d misc items", cd.miscItems.length); - writefln("%d inventory lights", cd.lights.length); - writefln("%d static lights", cd.statLights.length); - writefln("%d NPCs", cd.npcs.length); - writefln("%d containers", cd.containers.length); - writefln("%d doors", cd.doors.length); - writefln("%d activators", cd.activators.length); - writefln("%d potions", cd.potions.length); - writefln("%d apparatuses", cd.appas.length); - writefln("%d ingredients", cd.ingredients.length); - writefln("%d armors", cd.armors.length); - writefln("%d weapons", cd.weapons.length); - writefln("%d books", cd.books.length); - writefln("%d tools", cd.tools.length); - writefln("%d clothes", cd.clothes.length); - writefln("%d creatures", cd.creatures.length); - writefln(); - } - - // This isn't necessary but it's here for testing purposes. - cellList.release(cd); - - // Write some statistics - if(debugOut) - { - poolSize(); - writefln(esmRegion); - writefln("Total objects: ", MonsterClass.getTotalObjects); - } -} diff --git a/old_d_version/terrain/archive.d b/old_d_version/terrain/archive.d deleted file mode 100644 index 5f5d5fd80..000000000 --- a/old_d_version/terrain/archive.d +++ /dev/null @@ -1,461 +0,0 @@ -/* - OpenMW - The completely unofficial reimplementation of Morrowind - Copyright (C) 2009 Nicolay Korslund - WWW: http://openmw.sourceforge.net/ - - This file (archive.d) is part of the OpenMW package. - - OpenMW is distributed as free software: you can redistribute it - and/or modify it under the terms of the GNU General Public License - version 3, as published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - version 3 along with this program. If not, see - http://www.gnu.org/licenses/ . - -*/ - -module terrain.archive; - -const float TEX_SCALE = 1.0/16; - -// This should be part of the generic cache system. -const int CACHE_MAGIC = 0x345AF815; - -import std.mmfile; -import std.string; -import std.stdio; -import terrain.myfile; - -version(Windows) - static int pageSize = 64*1024; -else - static int pageSize = 4*1024; - -extern(C) -{ - // Convert a texture index to string - char *d_terr_getTexName(int index) - { return g_archive.getString(index).ptr; } - - // Fill various hardware buffers from cache - void d_terr_fillVertexBuffer(MeshInfo *mi, float *buffer, ulong size) - { mi.fillVertexBuffer(buffer[0..size]); } - - void d_terr_fillIndexBuffer(MeshInfo *mi, ushort *buffer, ulong size) - { mi.fillIndexBuffer(buffer[0..size]); } - - void d_terr_fillAlphaBuffer(AlphaInfo *mi, ubyte *buffer, ulong size) - { mi.fillAlphaBuffer(buffer[0..size]); } - - // Get a given alpha map struct belonging to a mesh - AlphaInfo *d_terr_getAlphaInfo(MeshInfo *mi, int index) - { return mi.getAlphaInfo(index); } - - int d_terr_getAlphaSize() { return g_archive.alphaSize; } -} - -// Info about the entire quad. TODO: Some of this (such as the texture -// scale and probably the width and radius) can be generated at -// loadtime and is common for all quads on the same level. We could -// just make a QuadLevelInfo struct. -struct QuadInfo -{ - // Basic info - int cellX, cellY; - int level; - - // Bounding box info - float minHeight, maxHeight; - float worldWidth; - float boundingRadius; - - // True if we should make the given child - bool hasChild[4]; - - // Number of mesh segments in this quad - int meshNum; - - // Location of this quad in the main archive file. The size includes - // everything related to this quad, including mesh data, alpha maps, - // etc. - size_t offset, size; -} - -// Info about an alpha map belonging to a mesh -struct AlphaInfo -{ - // Position of the actual image data - ulong bufSize, bufOffset; - - // The texture name for this layer. The actual string is stored in - // the archive's string buffer. - int texName = -1; - int alphaName = -1; - - // Fill the alpha texture buffer - void fillAlphaBuffer(ubyte abuf[]) - { - assert(abuf.length == bufSize); - g_archive.copy(abuf.ptr, bufOffset, bufSize); - } -} -static assert(AlphaInfo.sizeof == 6*4); - -// Info about each submesh -// If you change this struct please check whether align(1) still fits. -align(1) -struct MeshInfo -{ - // Bounding box info - float minHeight, maxHeight; - float worldWidth; - - // Vertex and index numbers - int vertRows, vertCols; - - // Height offset to apply to all vertices - float heightOffset; - - // Size and offset of the vertex buffer - ulong vertBufSize, vertBufOffset; - - // Texture name. Index to the string table. - int texName = -1; - - // Number and offset of AlphaInfo blocks - int alphaNum; - ulong alphaOffset; - - // Fill the given vertex buffer - void fillVertexBuffer(float vdest[]) - { - // The height map and normals from the archive - byte *hmap = cast(byte*)g_archive.getRelSlice(vertBufOffset, vertBufSize).ptr; - // The generic part, containing the x,y coordinates and the uv - // maps. - float *gmap = g_archive.getVertexBuffer(getLevel()).ptr; - - // Destination pointer - float *vbuf = vdest.ptr; - assert(vdest.length == vertRows*vertCols*8); - - // Merge the two data sets together into the output buffer. - float offset = heightOffset; - for(int y=0; y= 0); - assert(getLevel() == 1); - AlphaInfo *res = cast(AlphaInfo*)g_archive.getRelSlice - (alphaOffset, alphaNum*AlphaInfo.sizeof); - res += num; - return res; - } -} -static assert(MeshInfo.sizeof == 14*4); - -// The first part of the .index file -struct ArchiveHeader -{ - // "Magic" number to make sure we're actually reading an archive - // file - int magic; - - // Total number of quads in the archive - int quads; - - // Level of the 'root' quad. There will only be one quad on this - // level. - int rootLevel; - - // Size of the alpha maps, in pixels along one side. - int alphaSize; - - // Number of strings in the string table - int stringNum; - - // Size of the string buffer - size_t stringSize; -} - -TerrainArchive g_archive; - -// This class handles the cached terrain data. -struct TerrainArchive -{ - MeshInfo *curMesh; - QuadInfo *curQuad; - QuadInfo *rootQuad; - - void openFile(char[] name) - { - mmf = new MmFile(name, - MmFile.Mode.Read, - 0, null, pageSize); - - // Read the index file first - MyFile ifile = new MyFile(name ~ ".index"); - - ArchiveHeader head; - ifile.fill(head); - - // Reads data into an array. Would be better if this was part of - // the stream. - - // Sanity check - assert(head.magic == CACHE_MAGIC); - assert(head.quads > 0 && head.quads < 8192); - - // Store header info - alphaSize = head.alphaSize; - - // Read all the quads - quadList = new QuadInfo[head.quads]; - ifile.fillArray(quadList); - - // Create an index of all the quads - foreach(int index, qn; quadList) - { - int x = qn.cellX; - int y = qn.cellY; - int l = qn.level; - - assert(l >= 1); - - quadMap[l][x][y] = index; - assert(index == quadMap[l][x][y]); - - // Store the root quad - if(l == head.rootLevel) - { - assert(rootQuad == null); - rootQuad = &quadList[index]; - } - else - assert(l < head.rootLevel); - } - - // Make sure the root was set - assert(rootQuad !is null); - - // Next read the string table. First read the main string buffer. - stringBuf = new char[head.stringSize]; - ifile.fillArray(stringBuf); - - // Then read the string offsets - int[] offsets = new int[head.stringNum]; - ifile.fillArray(offsets); - - // Set up the string table - char *strptr = stringBuf.ptr; - strings.length = head.stringNum; - foreach(int i, ref str; strings) - { - // toString(char*) returns the string up to the zero - // terminator byte - str = toString(strptr + offsets[i]); - assert(str.ptr + str.length <= - stringBuf.ptr + stringBuf.length); - } - delete offsets; - - // Read the vertex buffer data - int bufNum = head.rootLevel; - assert(bufNum == 7); - vertBufData.length = bufNum; - - // Fill the vertex buffers. Start at level 1. - for(int i=1;i=1 && level= 0); - assert(index < strings.length); - - return strings[index]; - } - - void doMap(size_t offset, size_t size) - { - assert(mmf !is null); - assert(size); - mapped = cast(ubyte[])mmf[offset..offset+size]; - assert(mapped.length == size); - } - - // Get a slice of a given buffer within the mapped window. The - // offset is relative to the start of the window, and the size must - // fit inside the window. - ubyte[] getRelSlice(size_t offset, size_t size) - { - assert(mapped.length); - - return mapped[offset..offset+size]; - } - - // Copy a given buffer from the file. The buffer might be a - // compressed stream, so it's important that the buffers are written - // in the same block sizes as they are read. (Ie. you can't write a - // buffer as one operation and read it as two, or vice versa. Also, - // buffers cannot overlap.) The offset is relative to the current - // mapped file window. - void copy(void *dst, size_t offset, size_t inSize) - { - ubyte source[] = getRelSlice(offset, inSize); - - // Just copy it for now - ubyte* dest = cast(ubyte*)dst; - dest[0..source.length] = source[]; - } -} diff --git a/old_d_version/terrain/bindings.d b/old_d_version/terrain/bindings.d deleted file mode 100644 index 145d1a593..000000000 --- a/old_d_version/terrain/bindings.d +++ /dev/null @@ -1,27 +0,0 @@ -module terrain.bindings; - -alias void *SceneNode; -alias void *Bounds; -alias void *MeshObj; - -// These are all defined in cpp_terrain.cpp: -extern(C): - -SceneNode terr_createChildNode(float relX, float relY, SceneNode); -void terr_destroyNode(SceneNode); -Bounds terr_makeBounds(float minHeight, float maxHeight, float width, SceneNode); -void terr_killBounds(Bounds); -float terr_getSqCamDist(Bounds); -MeshObj terr_makeMesh(SceneNode,void*,int,float); -void terr_killMesh(MeshObj); - -void terr_genData(); -void terr_setupRendering(); - -void terr_makeLandMaterial(char*,float); -ubyte *terr_makeAlphaLayer(char*,int); -void terr_closeAlpha(char*,char*,float); -void terr_cleanupAlpha(char*,void*,int); - -void terr_resize(void*,void*,int,int); -void terr_saveImage(void*,int,char*); diff --git a/old_d_version/terrain/cachewriter.d b/old_d_version/terrain/cachewriter.d deleted file mode 100644 index 61a52addb..000000000 --- a/old_d_version/terrain/cachewriter.d +++ /dev/null @@ -1,337 +0,0 @@ -/* - OpenMW - The completely unofficial reimplementation of Morrowind - Copyright (C) 2009 Nicolay Korslund - WWW: http://openmw.sourceforge.net/ - - This file (cachewriter.d) is part of the OpenMW package. - - OpenMW is distributed as free software: you can redistribute it - and/or modify it under the terms of the GNU General Public License - version 3, as published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - version 3 along with this program. If not, see - http://www.gnu.org/licenses/ . - -*/ - -module terrain.cachewriter; - -import terrain.archive; - -import terrain.outbuffer; -import std.stdio, std.stream, std.string; -import terrain.myfile; -import std.math2; -import monster.util.string; -import monster.vm.dbg; - -// Helper structs -struct AlphaHolder -{ - AlphaInfo info; - - // Actual pixel buffer - ubyte[] buffer; -} - -struct MeshHolder -{ - MeshInfo info; - - // Actual buffers - byte[] vertexBuffer; - - // Alpha maps (if any) - AlphaHolder alphas[]; -} - -// A struct that gathers all the relevant quad data in one place. -struct QuadHolder -{ - QuadInfo info; - - MeshHolder meshes[]; -} - -struct CacheWriter -{ - // Opens the main archive file for output - void openFile(char[] fname) - { - mainFile = new File(fname, FileMode.OutNew); - iname = fname ~ ".index"; - - buf = new OutBuffer; - } - - void setParams(int mxLev, int alphSize) - { - maxLevel = mxLev; - alphaSize = alphSize; - - vertBuf.length = maxLevel; - } - - // Closes the main archive file and writes the index. - void finish() - { - mainFile.close(); - - // Write the index file - scope MyFile ofile = new MyFile(iname, FileMode.OutNew); - - // Header first - ArchiveHeader head; - head.magic = CACHE_MAGIC; - head.quads = quadList.length; - head.rootLevel = maxLevel; - head.alphaSize = alphaSize; - head.stringNum = stringList.length; - head.stringSize = totalStringLength; - ofile.dump(head); - - // Write the quads - ofile.dumpArray(quadList); - - // String table next. We need to sort it in order of the indices - // first. - char[][] strVector; - strVector.length = head.stringNum; - - foreach(char[] key, int value; stringList) - strVector[value] = key; - - // Next, write the strings to file while we fill in the offset - // list - int[] offsets = new int[head.stringNum]; - - size_t curOffs = 0; - for(int i=0; i level); - vertBuf[level] = buf; - } - - // Add a common index buffer - void setIndexBuffer(ushort[] buf) - { - indexBuf = buf; - } - - // Write a finished quad to the archive file. All the offsets and - // numbers in the *Info structs are filled in automatically based on - // the additional data in the Holder structs. - void writeQuad(ref QuadHolder qh) - { - scope auto _trc = new MTrace("writeQuad"); - - // Write the MeshInfo's first - int meshNum = qh.meshes.length; - - MeshInfo meshes[] = buf.write!(MeshInfo)(meshNum); - - float minh = float.infinity; - float maxh = -float.infinity; - - // Then write the mesh data in approximately the order it's read - for(int i=0; igetFarClipDistance(); - // Recreate the mesh if the view distance has increased - if ( vd > mMeshDistance ) - { - destroyMesh(); - createMesh(); - } - - Ogre::Vector3 p = mCamera->getDerivedPosition(); - p.x -= ((int)p.x % CELL_WIDTH); - p.z -= ((int)p.z % CELL_WIDTH); - - float h = (p.y + 2048)*2.0/CELL_WIDTH; - h *= h; - - mNode->setPosition(p.x, -p.z, -32 -h); - } - -private: - void createMesh() - { - float vd = mCamera->getFarClipDistance(); - - mMeshDistance = vd; - - vd = vd/CELL_WIDTH * 32; - - mMat = Ogre::MaterialManager::getSingleton(). - create("BaseLandMat", - Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); - - Ogre::TextureUnitState* us = mMat->getTechnique(0)->getPass(0)->createTextureUnitState("_land_default.dds"); - us->setTextureScale(1.0f/vd,1.0f/vd); - - mMat->getTechnique(0)->getPass(0)->setDepthBias(-1); - - mObject = mSceneMgr->createManualObject("BaseLand"); - mObject->begin("BaseLandMat", Ogre::RenderOperation::OT_TRIANGLE_LIST); - - vd = mMeshDistance; - - const int HEIGHT = -2048 - 10; - - mObject->position(-vd,vd,HEIGHT); - mObject->textureCoord(0, 1); - - mObject->position(-vd,-vd,HEIGHT); - mObject->textureCoord(0, 0); - - mObject->position(vd,-vd,HEIGHT); - mObject->textureCoord(1, 0); - - mObject->position(vd,vd,HEIGHT); - mObject->textureCoord(1, 1); - - mObject->quad(0,1,2,3); - - mObject->end(); - - mNode = g_rootTerrainNode->createChildSceneNode(); - mNode->attachObject(mObject); - } - - void destroyMesh() - { - mNode->detachAllObjects(); - mSceneMgr->destroyManualObject(mObject); - mNode->getParentSceneNode()->removeAndDestroyChild(mNode->getName()); - - mMat->getCreator()->remove(mMat->getHandle()); - mMat = Ogre::MaterialPtr(); - } - - ///the created mesh - Ogre::ManualObject* mObject; - - ///The material for the mesh - Ogre::MaterialPtr mMat; - - ///scene node for the mesh - Ogre::SceneNode* mNode; - - ///In essence, the farViewDistance of the camera last frame - Ogre::Real mMeshDistance; -}; diff --git a/old_d_version/terrain/cpp_mesh.cpp b/old_d_version/terrain/cpp_mesh.cpp deleted file mode 100644 index f5f6a26f5..000000000 --- a/old_d_version/terrain/cpp_mesh.cpp +++ /dev/null @@ -1,286 +0,0 @@ -// The Ogre renderable used to hold and display the terrain meshes. -class TerrainMesh : public Ogre::Renderable, public Ogre::MovableObject -{ -public: - - TerrainMesh(Ogre::SceneNode *parent, const MeshInfo &info, - int level, float scale) - : Ogre::Renderable(), - Ogre::MovableObject() - { - TRACE("TerrainMesh()"); - - mLevel = level; - - // This is a bit messy, with everything in one function. We could - // split it up later. - - // Use MW coordinates all the way - assert(info.worldWidth > 0); - assert(info.minHeight <= info.maxHeight); - mBounds.setExtents(0,0,info.minHeight, - info.worldWidth, info.worldWidth, - info.maxHeight); - mCenter = mBounds.getCenter(); - mBoundingRadius = mBounds.getHalfSize().length(); - - // TODO: VertexData has a clone() function. This probably means we - // can set this up once and then clone it, to get a completely - // unnoticable increase in performance :) - mVertices = new VertexData(); - mVertices->vertexStart = 0; - mVertices->vertexCount = info.vertRows*info.vertCols; - - VertexDeclaration* vertexDecl = mVertices->vertexDeclaration; - size_t currOffset = 0; - - vertexDecl->addElement(0, currOffset, VET_FLOAT3, VES_POSITION); - currOffset += VertexElement::getTypeSize(VET_FLOAT3); - - vertexDecl->addElement(0, currOffset, VET_FLOAT3, VES_NORMAL); - currOffset += VertexElement::getTypeSize(VET_FLOAT3); - - vertexDecl->addElement(0, currOffset, VET_FLOAT2, - VES_TEXTURE_COORDINATES, 0); - currOffset += VertexElement::getTypeSize(VET_FLOAT2); - - assert(vertexDecl->getVertexSize(0) == currOffset); - - HardwareVertexBufferSharedPtr mMainBuffer; - mMainBuffer = HardwareBufferManager::getSingleton().createVertexBuffer - ( - vertexDecl->getVertexSize(0), // size of one whole vertex - mVertices->vertexCount, // number of vertices - HardwareBuffer::HBU_STATIC_WRITE_ONLY, // usage - false); // no shadow buffer - - // Bind the data - mVertices->vertexBufferBinding->setBinding(0, mMainBuffer); - - // Fill the buffer - float* verts = static_cast - (mMainBuffer->lock(HardwareBuffer::HBL_DISCARD)); - info.fillVertexBuffer(verts,8*mVertices->vertexCount); - mMainBuffer->unlock(); - - // Create the index data holder - mIndices = new IndexData(); - mIndices->indexCount = 64*64*6; // TODO: Shouldn't be hard-coded - mIndices->indexBuffer = - HardwareBufferManager::getSingleton().createIndexBuffer - ( HardwareIndexBuffer::IT_16BIT, - mIndices->indexCount, - HardwareBuffer::HBU_STATIC_WRITE_ONLY, - false); - - // Fill the buffer with warm fuzzy archive data - unsigned short* indices = static_cast - (mIndices->indexBuffer->lock - (0, mIndices->indexBuffer->getSizeInBytes(), - HardwareBuffer::HBL_DISCARD)); - info.fillIndexBuffer(indices,mIndices->indexCount); - mIndices->indexBuffer->unlock(); - - // Finally, create the material - const std::string texName = info.getTexName(); - - // Set up the scene node. - mNode = parent->createChildSceneNode(); - mNode->attachObject(this); - - // Finally, create or retrieve the material - if(MaterialManager::getSingleton().resourceExists(texName)) - { - mMaterial = MaterialManager::getSingleton().getByName - (texName); - return; - } - - // No existing material. Create a new one. - mMaterial = MaterialManager::getSingleton().create - (texName, ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); - - Pass* pass = mMaterial->getTechnique(0)->getPass(0); - pass->setLightingEnabled(false); - - if(level > 1) - { - // This material just has a normal texture - pass->createTextureUnitState(texName) - //->setTextureAddressingMode(TextureUnitState::TAM_CLAMP) - ; - } - else - { - assert(level == 1); - - // Get the background texture. TODO: We should get this from - // somewhere, no file names should be hard coded. The texture - // might exist as a .tga in earlier versions of the game, and - // we might also want to specify a different background - // texture on some meshes. - //const char *bgTex = info.getBackgroundTex(); - - const char *bgTex = "_land_default.dds"; - pass->createTextureUnitState(bgTex) - ->setTextureScale(scale,scale); - - // Loop through all the textures in this mesh - for(int tnum=0; tnumgetBuffer(); - pixelBuffer->lock(Ogre::HardwareBuffer::HBL_DISCARD); - const Ogre::PixelBox& pixelBox = pixelBuffer->getCurrentLock(); - Ogre::uint8* pDest = static_cast(pixelBox.data); - - // Copy alpha data from file - alpha.fillAlphaBuffer(pDest,g_alphaSize*g_alphaSize); - - // Close the buffer - pixelBuffer->unlock(); - } - - pass = mMaterial->getTechnique(0)->createPass(); - pass->setSceneBlending(Ogre::SBT_TRANSPARENT_ALPHA); - pass->setLightingEnabled(false); - pass->setDepthFunction(Ogre::CMPF_EQUAL); - - Ogre::TextureUnitState* tus = pass->createTextureUnitState(alphaName); - //tus->setTextureAddressingMode(Ogre::TextureUnitState::TAM_CLAMP); - - tus->setAlphaOperation(Ogre::LBX_BLEND_TEXTURE_ALPHA, - Ogre::LBS_TEXTURE, - Ogre::LBS_TEXTURE); - tus->setColourOperationEx(Ogre::LBX_BLEND_DIFFUSE_ALPHA, - Ogre::LBS_TEXTURE, - Ogre::LBS_TEXTURE); - tus->setIsAlpha(true); - - // Add the actual texture on top of the alpha map. - tus = pass->createTextureUnitState(tname); - tus->setColourOperationEx(Ogre::LBX_BLEND_DIFFUSE_ALPHA, - Ogre::LBS_TEXTURE, - Ogre::LBS_CURRENT); - - tus->setTextureScale(scale, scale); - } - } - } - - ~TerrainMesh() - { - assert(mNode); - mNode->detachAllObjects(); - mNode->getCreator()->destroySceneNode(mNode); - - // TODO: This still crashes on level1 meshes. Find out why! - if(mLevel!=1)delete mVertices; - delete mIndices; - } - - //----------------------------------------------------------------------- - // These are all Ogre functions that we have to override - //----------------------------------------------------------------------- - - // Internal Ogre function. We should call visitor->visit on all - // Renderables that are part of this object. In our case, this is - // only ourselves. - void visitRenderables(Renderable::Visitor* visitor, - bool debugRenderables = false) { - visitor->visit(this, 0, false); - } - - void getRenderOperation( Ogre::RenderOperation& op ) { - op.useIndexes = true; - op.operationType = Ogre::RenderOperation::OT_TRIANGLE_LIST; - op.vertexData = mVertices; - op.indexData = mIndices; - } - - void getWorldTransforms( Ogre::Matrix4* xform ) const { - *xform = mNode->_getFullTransform(); - } - - const Ogre::Quaternion& getWorldOrientation(void) const { - return mNode->_getDerivedOrientation(); - } - - const Ogre::Vector3& getWorldPosition(void) const { - return mNode->_getDerivedPosition(); - } - - Ogre::Real getSquaredViewDepth(const Ogre::Camera *cam) const { - Ogre::Vector3 diff = mCenter - cam->getDerivedPosition(); - // Use squared length to avoid square root - return diff.squaredLength(); - } - - const Ogre::LightList& getLights(void) const { - if (mLightListDirty) { - getParentSceneNode()->getCreator()->_populateLightList - (mCenter, mBoundingRadius, mLightList); - mLightListDirty = false; - } - return mLightList; - } - virtual const Ogre::String& getMovableType( void ) const { - static Ogre::String t = "MW_TERRAIN"; - return t; - } - void _updateRenderQueue( Ogre::RenderQueue* queue ) { - mLightListDirty = true; - queue->addRenderable(this, mRenderQueueID); - } - const Ogre::AxisAlignedBox& getBoundingBox( void ) const - { - return mBounds; - } - - Ogre::Real getBoundingRadius(void) const { - return mBoundingRadius; - } - virtual const MaterialPtr& getMaterial(void) const - { return mMaterial; } - - //----------------------------------------------------------------------- - //----------------------------------------------------------------------- - -private: - - int mLevel; - - Ogre::SceneNode* mNode; - - Ogre::MaterialPtr mMaterial; - - Ogre::VertexData* mVertices; - Ogre::IndexData* mIndices; - - mutable bool mLightListDirty; - - Ogre::Real mBoundingRadius; - Ogre::AxisAlignedBox mBounds; - Ogre::Vector3 mCenter; -}; diff --git a/old_d_version/terrain/cpp_terrain.cpp b/old_d_version/terrain/cpp_terrain.cpp deleted file mode 100644 index ad2711036..000000000 --- a/old_d_version/terrain/cpp_terrain.cpp +++ /dev/null @@ -1,413 +0,0 @@ -/* - OpenMW - The completely unofficial reimplementation of Morrowind - Copyright (C) 2009 Jacob Essex, Nicolay Korslund - WWW: http://openmw.sourceforge.net/ - - This file (cpp_terrain.cpp) is part of the OpenMW package. - - OpenMW is distributed as free software: you can redistribute it - and/or modify it under the terms of the GNU General Public License - version 3, as published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - version 3 along with this program. If not, see - http://www.gnu.org/licenses/ . - -*/ - -const int CELL_WIDTH = 8192; - -SceneNode *g_rootTerrainNode; -int g_alphaSize; - -struct MeshInfo; -struct AlphaInfo; - -// D functions -extern "C" -{ - void d_terr_superman(); - void d_terr_terrainUpdate(); - - char *d_terr_getTexName(int32_t); - - void d_terr_fillVertexBuffer(const MeshInfo*,float*,uint64_t); - void d_terr_fillIndexBuffer(const MeshInfo*,uint16_t*,uint64_t); - AlphaInfo *d_terr_getAlphaInfo(const MeshInfo*,int32_t); - - void d_terr_fillAlphaBuffer(const AlphaInfo*,uint8_t*,uint64_t); - - int32_t d_terr_getAlphaSize(); -} - -// Info about a submesh. This is a clone of the struct defined in -// archive.d. TODO: Make sure the D and C++ structs are of the same -// size and alignment. -struct MeshInfo -{ - // Bounding box info - float minHeight, maxHeight; - float worldWidth; - - // Vertex and index numbers - int32_t vertRows, vertCols; - - // Height offset to apply to all vertices - float heightOffset; - - // Size and offset of the vertex buffer - int64_t vertBufSize, vertBufOffset; - - // Texture name. Index to the string table. - int32_t texName; - - // Number and offset of AlphaInfo blocks - int32_t alphaNum; - uint64_t alphaOffset; - - inline void fillVertexBuffer(float *buffer, uint64_t size) const - { - d_terr_fillVertexBuffer(this, buffer, size); - } - - inline void fillIndexBuffer(uint16_t *buffer, uint64_t size) const - { - d_terr_fillIndexBuffer(this, buffer, size); - } - - inline char* getTexName() const - { - return d_terr_getTexName(texName); - } - - inline AlphaInfo *getAlphaInfo(int tnum) const - { - return d_terr_getAlphaInfo(this, tnum); - } -}; - -// Info about an alpha map belonging to a mesh -struct AlphaInfo -{ - // Position of the actual image data - uint64_t bufSize, bufOffset; - - // The texture name for this layer. The actual string is stored in - // the archive's string buffer. - int32_t texName; - int32_t alphaName; - - inline char* getTexName() const - { - return d_terr_getTexName(texName); - } - - inline char* getAlphaName() const - { - return d_terr_getTexName(alphaName); - } - - inline void fillAlphaBuffer(uint8_t *buffer, uint64_t size) const - { - return d_terr_fillAlphaBuffer(this, buffer, size); - } -}; - -#include "cpp_baseland.cpp" -#include "cpp_mesh.cpp" - -BaseLand *g_baseLand; - -class TerrainFrameListener : public FrameListener -{ -protected: - bool frameEnded(const FrameEvent& evt) - { - TRACE("Terrain frame"); - d_terr_terrainUpdate(); - if(g_baseLand) - g_baseLand->update(); - return true; - } -}; - -// Renders a material into a texture -Ogre::TexturePtr getRenderedTexture(Ogre::MaterialPtr mp, - const std::string& name, - int texSize, Ogre::PixelFormat tt) -{ - Ogre::CompositorPtr cp = Ogre::CompositorManager::getSingleton(). - create("Rtt_Comp", - Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); - - Ogre::CompositionTargetPass* ctp = cp->createTechnique()->getOutputTargetPass(); - Ogre::CompositionPass* cpass = ctp->createPass(); - cpass->setType(Ogre::CompositionPass::PT_RENDERQUAD); - cpass->setMaterial(mp); - - // Create the destination texture - Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton(). - createManual(name + "_T", - Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - Ogre::TEX_TYPE_2D, - texSize, - texSize, - 0, - tt, - Ogre::TU_RENDERTARGET - ); - - Ogre::RenderTexture* renderTexture = texture->getBuffer()->getRenderTarget(); - Ogre::Viewport* vp = renderTexture->addViewport(mCamera); - - Ogre::CompositorManager::getSingleton().addCompositor(vp, "Rtt_Comp"); - Ogre::CompositorManager::getSingleton().setCompositorEnabled(vp,"Rtt_Comp", true); - - renderTexture->update(); - - // Call the OGRE renderer. - Ogre::Root::getSingleton().renderOneFrame(); - - Ogre::CompositorManager::getSingleton().removeCompositor(vp, "Rtt_Comp"); - Ogre::CompositorManager::getSingleton().remove(cp->getHandle()); - - renderTexture->removeAllViewports(); - - return texture; -} - -// These are used between some functions below. Kinda messy. Since -// these are GLOBAL instances, they are terminated at program -// exit. However, OGRE itself is terminated before that, so we have to -// make sure we have no 'active' shared pointers after OGRE is -// finished (otherwise we get a segfault at exit.) -std::list createdResources; -Ogre::HardwarePixelBuffer *pixelBuffer; -MaterialPtr mat; - -// Functions called from D -extern "C" -{ - SceneNode* terr_createChildNode(float x, float y, - SceneNode *parent) - { - Ogre::Vector3 pos(x,y,0); - if(parent == NULL) - parent = g_rootTerrainNode; - - assert(parent); - return parent->createChildSceneNode(pos); - } - - void terr_destroyNode(SceneNode *node) - { - node->removeAndDestroyAllChildren(); - mSceneMgr->destroySceneNode(node); - } - - // TODO: We could make allocation a little more refined than new and - // delete. But that's true for everything here. A freelist based - // approach is best in most of these cases, as we have continuous - // allocation/deallocation of fixed-size structs. - Ogre::AxisAlignedBox *terr_makeBounds(float minHeight, float maxHeight, - float width, SceneNode* node) - { - TRACE("terr_makeBounds"); - AxisAlignedBox *mBounds = new AxisAlignedBox; - - assert(maxHeight >= minHeight); - - mBounds->setExtents(0,0,minHeight, - width,width,maxHeight); - - // Transform the box to world coordinates, so it can be compared - // with the camera later. - mBounds->transformAffine(node->_getFullTransform()); - - return mBounds; - } - - void terr_killBounds(AxisAlignedBox *bounds) - { - TRACE("terr_killBounds"); - delete bounds; - } - - float terr_getSqCamDist(AxisAlignedBox *mBounds) - { - TRACE("terr_getSqCamDist"); - Ogre::Vector3 cpos = mCamera->getDerivedPosition(); - Ogre::Vector3 diff(0, 0, 0); - diff.makeFloor(cpos - mBounds->getMinimum() ); - diff.makeCeil(cpos - mBounds->getMaximum() ); - return diff.squaredLength(); - } - - TerrainMesh *terr_makeMesh(SceneNode *parent, - MeshInfo *info, - int level, float scale) - { - return new TerrainMesh(parent, *info, level, scale); - } - - void terr_killMesh(TerrainMesh *mesh) - { - TRACE("terr_killMesh"); - delete mesh; - } - - // Set up the rendering system - void terr_setupRendering() - { - TRACE("terr_setupRendering()"); - // Make sure the C++ sizes match the D sizes, since the structs - // are shared between the two. - assert(sizeof(MeshInfo) == 14*4); - assert(sizeof(AlphaInfo) == 6*4); - - // Add the terrain directory as a resource location. TODO: Get the - // name from D. - ResourceGroupManager::getSingleton(). - addResourceLocation("cache/terrain/", "FileSystem", "General"); - - // Enter superman mode - mCamera->setFarClipDistance(40*CELL_WIDTH); - //ogre_setFog(0.7, 0.7, 0.7, 200, 32*CELL_WIDTH); - d_terr_superman(); - - // Create a root scene node first. The 'root' node is rotated to - // match the MW coordinate system - g_rootTerrainNode = mwRoot->createChildSceneNode("TERRAIN_ROOT"); - - // Add the base land. This is the ground beneath the actual - // terrain mesh that makes the terrain look infinite. - //g_baseLand = new BaseLand(); - - g_alphaSize = d_terr_getAlphaSize(); - - // Add the frame listener - mRoot->addFrameListener(new TerrainFrameListener); - } - - // The next four functions are called in the function genLevel2Map() - // only. This is very top-down-programming-ish and a bit messy, but - // that's what I get for mixing C++ and D like this. - void terr_makeLandMaterial(const char* name, float scale) - { - // Get a new material - mat = Ogre::MaterialManager::getSingleton(). - create(name, - Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); - - // Put the default texture in the bottom 'layer', so that we don't - // end up seeing through the landscape. - Ogre::Pass* np = mat->getTechnique(0)->getPass(0); - np->setLightingEnabled(false); - np->createTextureUnitState("_land_default.dds") - ->setTextureScale(scale,scale); - } - - uint8_t *terr_makeAlphaLayer(const char* name, int32_t width) - { - // Create alpha map for this texture. - Ogre::TexturePtr texPtr = Ogre::TextureManager::getSingleton(). - createManual(name, - Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - Ogre::TEX_TYPE_2D, - width, width, - 1,0, // depth, mipmaps - Ogre::PF_A8, // One-channel alpha - Ogre::TU_STATIC_WRITE_ONLY); - - createdResources.push_back(texPtr); - - pixelBuffer = texPtr->getBuffer().get(); - pixelBuffer->lock(Ogre::HardwareBuffer::HBL_DISCARD); - const Ogre::PixelBox& pixelBox = pixelBuffer->getCurrentLock(); - - return static_cast(pixelBox.data); - } - - void terr_closeAlpha(const char *alphaName, - const char *texName, float scale) - { - // Close the alpha pixel buffer opened in the previous function - pixelBuffer->unlock(); - - // Create a pass containing the alpha map - Pass *np = mat->getTechnique(0)->createPass(); - np->setSceneBlending(Ogre::SBT_TRANSPARENT_ALPHA); - np->setLightingEnabled(false); - np->setDepthFunction(Ogre::CMPF_EQUAL); - Ogre::TextureUnitState* tus = np->createTextureUnitState(alphaName); - tus->setTextureAddressingMode(Ogre::TextureUnitState::TAM_CLAMP); - - // Set various blending options - tus->setAlphaOperation(Ogre::LBX_BLEND_TEXTURE_ALPHA, - Ogre::LBS_TEXTURE, - Ogre::LBS_TEXTURE); - tus->setColourOperationEx(Ogre::LBX_BLEND_DIFFUSE_ALPHA, - Ogre::LBS_TEXTURE, - Ogre::LBS_TEXTURE); - tus->setIsAlpha(true); - - // Add the terrain texture to the pass and scale it. - tus = np->createTextureUnitState(texName); - tus->setColourOperationEx(Ogre::LBX_BLEND_DIFFUSE_ALPHA, - Ogre::LBS_TEXTURE, - Ogre::LBS_CURRENT); - tus->setTextureScale(scale, scale); - } - - // Clean up after the above functions, render the material to - // texture and save the data in outdata and in the file outname. - void terr_cleanupAlpha(const char *outname, - void *outData, int32_t toSize) - { - TexturePtr tex1 = getRenderedTexture(mat,outname, - toSize,Ogre::PF_R8G8B8); - - // Blit the texture into the given memory buffer - PixelBox pb = PixelBox(toSize, toSize, 1, PF_R8G8B8); - pb.data = outData; - tex1->getBuffer()->blitToMemory(pb); - - // Clean up - TextureManager::getSingleton().remove(tex1->getHandle()); - const std::list::const_iterator iend = createdResources.end(); - for ( std::list::const_iterator itr = createdResources.begin(); - itr != iend; - ++itr) - (*itr)->getCreator()->remove((*itr)->getHandle()); - createdResources.clear(); - - MaterialManager::getSingleton().remove(mat->getHandle()); - mat.setNull(); - } - - void terr_resize(void* srcPtr, void* dstPtr, int32_t fromW, int32_t toW) - { - // Create pixelboxes - PixelBox src = PixelBox(fromW, fromW, 1, PF_R8G8B8); - PixelBox dst = PixelBox(toW, toW, 1, PF_R8G8B8); - - src.data = srcPtr; - dst.data = dstPtr; - - // Resize the image. The nearest neighbour filter makes sure - // there is no blurring. - Image::scale(src, dst, Ogre::Image::FILTER_NEAREST); - } - - void terr_saveImage(void *data, int32_t width, const char* name) - { - Image img; - img.loadDynamicImage((uchar*)data, width, width, PF_R8G8B8); - img.save(name); - } -} diff --git a/old_d_version/terrain/esmland.d b/old_d_version/terrain/esmland.d deleted file mode 100644 index 1ea169ade..000000000 --- a/old_d_version/terrain/esmland.d +++ /dev/null @@ -1,158 +0,0 @@ -module terrain.esmland; - -import esm.loadltex; -import esm.loadcell; -import util.regions; -import esm.filereader; - -import std.stdio; - -const int LAND_NUM_VERTS = 65*65; - -MWLand mwland; - -// Interface to the ESM landscape data -struct MWLand -{ - RegionManager reg; - - // These structs/types represent the way data is actually stored in - // the ESM files. - - // Heightmap - align(1) - struct VHGT - { - float heightOffset; - byte heightData[LAND_NUM_VERTS]; - short unknown1; - char unknown2; - } - - // Normals - typedef byte[LAND_NUM_VERTS*3] VNML; - - // Land textures. This is organized in 4x4 buffers of 4x4 squares - // each. This is how the original engine splits up the cell meshes, - // and it's probably a good idea for us to do the same. - typedef short[4][4][4][4] VTEX; - - static assert(VHGT.sizeof == 4232); - static assert(VNML.sizeof == 12675); - static assert(VTEX.sizeof == 512); - - // Landscape data for one cell - struct LandData - { - VHGT vhgt; - VNML normals; - } - - // Texture data for one cell - struct LTEXData - { - // Global list of land textures from the source ES file - LandTextureList.TextureList source; - - // Texture indices for this cell - VTEX vtex; - - // Get the texture x2,y2 from the sub map x1,x2 - char[] getTexture(int x1, int y1, int x2, int y2) - { - // Get the texture index relative to the current esm/esp file - short texID = vtex[y1][x1][y2][x2] - 1; - - if(texID == -1) - return null; - - // Return the 'new' texture name. This name was automatically - // been converted to .dds at load time if the .tga file was not - // found. - assert(source !is null); - assert(texID >= 0 && texID < source.length); - return source[texID].getNewName(); - } - - // Get a texture from the 16x16 grid in one cell - char[] getTexture(int x, int y) - { - return getTexture(x/4,y/4,x%4,y%4); - } - } - - // Get the maximum absolute coordinate value in any direction - int getMaxCoord() - { return cells.maxXY; } - - // Does the given cell exist and does it have land data? - bool hasData(int x, int y) - { - // Does the cell exist? - if(!cells.hasExt(x,y)) - return false; - - // And does it have terrain data? - auto ex = cells.getExt(x,y); - return ex.hasLand(); - } - - LandData *getLandData(int x, int y) - { - loadCell(x, y); - return ¤tLand; - } - - LTEXData *getLTEXData(int x, int y) - { - loadCell(x, y); - return ¤tLtex; - } - - private: - - int currentX = -1234; - int currentY = 4321; - - LandData currentLand; - LTEXData currentLtex; - - // Make sure the given cell is loaded - void loadCell(int x, int y) - { - // If the right cell is already loaded, don't do anything - if(x == currentX && y == currentY) - return; - - assert(hasData(x,y)); - - currentX = x; - currentY = y; - - // Get the file context for the terrain data. This can be used to - // skip to the right part of the ESM file. - auto cont = cells.getExt(x,y).land.context; - - // Get the land texture list from the file - currentLtex.source = landTextures.files[cont.filename]; - - // We should use an existing region later, or at least delete this - // once we're done with the gen process. - if(reg is null) - reg = new RegionManager(); - - // Open the ESM at this cell - esFile.restoreContext(cont, reg); - - // Store the cell-specific data - esFile.readHNExact(currentLand.normals.ptr, - currentLand.normals.length, "VNML"); - esFile.readHNExact(¤tLand.vhgt, VHGT.sizeof, "VHGT"); - - // These aren't used yet - if(esFile.isNextSub("WNAM")) esFile.skipHSubSize(81); - if(esFile.isNextSub("VCLR")) esFile.skipHSubSize(12675); - - esFile.readHNExact(¤tLtex.vtex, VTEX.sizeof, "VTEX"); - } -} diff --git a/old_d_version/terrain/generator.d b/old_d_version/terrain/generator.d deleted file mode 100644 index f1645c9ab..000000000 --- a/old_d_version/terrain/generator.d +++ /dev/null @@ -1,1114 +0,0 @@ -/* - OpenMW - The completely unofficial reimplementation of Morrowind - Copyright (C) 2008-2009 Nicolay Korslund - Email: < korslund@gmail.com > - WWW: http://openmw.snaptoad.com/ - - This file (generator.d) is part of the OpenMW package. - - OpenMW is distributed as free software: you can redistribute it - and/or modify it under the terms of the GNU General Public License - version 3, as published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - version 3 along with this program. If not, see - http://www.gnu.org/licenses/ . - -*/ - -// This module is responsible for generating the cache files. -module terrain.generator; - -import std.stdio; -import std.string; -import std.math2; -import std.c.string; - -import terrain.cachewriter; -import terrain.archive; -import terrain.esmland; -import terrain.terrain; -import terrain.bindings; - -import util.cachefile; - -import monster.util.aa; -import monster.util.string; -import monster.vm.dbg; - -int mCount; - -// Texture sizes for the various levels. For the most detailed level -// (level 1), this give the size of the alpha splatting textures rather -// than a final texture. -int[] texSizes; - -CacheWriter cache; - -void generate(char[] filename) -{ - makePath(cacheDir); - - cache.openFile(filename); - - // Find the maxiumum distance from (0,0) in any direction - int max = mwland.getMaxCoord(); - - // Round up to nearest power of 2 - int depth=1; - while(max) - { - max >>= 1; - depth++; - assert(depth <= 8); - } - max = 1 << depth-1; - - // We already know the answers - assert(max == 32); - assert(depth == 6); - - // Set the texture sizes. TODO: These should be config options, - // perhaps - or maybe a result of some higher-level detail setting. - texSizes.length = depth+1; - texSizes[6] = 1024; - texSizes[5] = 512; - texSizes[4] = 256; - texSizes[3] = 256; - texSizes[2] = 512; - texSizes[1] = 64; - - // Set some general parameters for the runtime - cache.setParams(depth+1, texSizes[1]); - - // Create some common data first - writefln("Generating common data"); - genDefaults(); - genIndexData(); - - writefln("Generating quad data"); - GenLevelResult gen; - - // Start at one level above the top, but don't generate data for it - genLevel(depth+1, -max, -max, gen, false); - - writefln("Writing index file"); - cache.finish(); - writefln("Pregeneration done. Results written to ", filename); -} - -struct GenLevelResult -{ - QuadHolder quad; - - bool hasMesh; - bool isAlpha; - - int width; - - ubyte[] data; - - void allocImage(int _width) - { - assert(isEmpty()); - - width = _width; - data.length = width*width*3; - quad.meshes.length = 1; - - assert(!hasAlpha()); - } - - void allocAlphas(int _width, int texNum) - { - assert(isEmpty() || hasMesh); - - width = _width; - data.length = width*width*texNum; - isAlpha = true; - - // Set up the alpha images. TODO: We have to split these over - // several meshes, but for now pretend that we're using only - // one. - assert(quad.meshes.length == 1); - quad.meshes[0].alphas.length = texNum; - assert(alphaNum() == texNum); - - int s = width*width; - for(int i=0;i 2); - genLevel2Map(null, defaults[2]); - - for(int i=3; i=0&&v>=0); - assert(u<=1&&v<=1); - - vertList[index++] = u; - vertList[index++] = v; - } - assert(index == vertList.length); - // Store the buffer - cache.addVertexBuffer(lev,vertList); - } - - // index stuff already ported -} - -void genLevel(int level, int X, int Y, ref GenLevelResult result, - bool makeData = true) -{ - scope auto _trc = new MTrace(format("genLevel(%s,%s,%s)",level,X,Y)); - result.quad.info.cellX = X; - result.quad.info.cellY = Y; - result.quad.info.level = level; - result.quad.info.worldWidth = 8192 << (level-1); - - assert(result.isEmpty); - - // Level 1 (most detailed) is handled differently from the - // other leves. - if(level == 1) - { - assert(makeData); - - if(!mwland.hasData(X,Y)) - // Oops, there's no data for this cell. Skip it. - return; - - // The mesh is generated in pieces rather than as one part. - genLevel1Meshes(result); - - // We also generate alpha maps instead of the actual textures. - genCellAlpha(result); - - if(!result.isEmpty()) - { - // Store the information we just created - assert(result.hasAlpha()); - cache.writeQuad(result.quad); - } - - return; - } - assert(level > 1); - - // Number of cells along one side in each sub-quad (not in this - // quad) - int cells = 1 << (level-2); - - // Call the sub-levels and store the result - GenLevelResult sub[4]; - genLevel(level-1, X, Y, sub[0]); // NW - genLevel(level-1, X+cells, Y, sub[1]); // NE - genLevel(level-1, X, Y+cells, sub[2]); // SW - genLevel(level-1, X+cells, Y+cells, sub[3]); // SE - - // Make sure we deallocate everything when the function exists - scope(exit) - { - foreach(ref s; sub) - s.kill(); - } - - // Mark the sub-quads that have data - bool anyUsed = false; - for(int i=0;i<4;i++) - { - bool used = !sub[i].isEmpty(); - result.quad.info.hasChild[i] = used; - anyUsed = anyUsed || used; - } - - if(!anyUsed) - { - // If our children are empty, then we are also empty. - assert(result.isEmpty()); - return; - } - - if(makeData) - { - if(level == 2) - // For level==2, generate a new texture from the alpha maps. - genLevel2Map(sub, result); - else - // Level 3+, merge the images from the previous levels - mergeMaps(sub, result); - - // Create the landscape mesh by merging the result from the - // children. - mergeMesh(sub, result); - } - - // Store the result in the cache file - cache.writeQuad(result.quad); -} - -// Generate mesh data for one cell -void genLevel1Meshes(ref GenLevelResult res) -{ - // Constants - int intervals = 64; - int vertNum = intervals+1; - int vertSep = 128; - - // Allocate the mesh buffer - res.allocMesh(vertNum); - - int cellX = res.quad.info.cellX; - int cellY = res.quad.info.cellY; - assert(res.quad.info.level==1); - - MeshHolder *mh = &res.quad.meshes[0]; - MeshInfo *mi = &mh.info; - - // Set some basic data - mi.worldWidth = vertSep*intervals; - assert(mi.worldWidth == 8192); - - auto land = mwland.getLandData(cellX, cellY); - - byte[] heightData = land.vhgt.heightData; - byte[] normals = land.normals; - mi.heightOffset = land.vhgt.heightOffset; - - float max=-1000000.0; - float min=1000000.0; - - byte[] verts = mh.vertexBuffer; - int index = 0; - - // Loop over all the vertices in the mesh - float rowheight = mi.heightOffset; - float height; - for(int y=0; y<65; y++) - for(int x=0; x<65; x++) - { - // Offset of this vertex within the source buffer - int offs=y*65+x; - - // The vertex data from the ESM - byte data = heightData[offs]; - - // Write the height value as a short (2 bytes) - *(cast(short*)&verts[index]) = data; - index+=2; - - // Calculate the height here, even though we don't store - // it. We use it to find the min and max values. - if(x == 0) - { - // Set the height to the row height - height = rowheight; - - // First value in each row adjusts the row height - rowheight += data; - } - // Adjust the accumulated height with the new data. - height += data; - - // Calculate the min and max - max = height > max ? height : max; - min = height < min ? height : min; - - // Store the normals - for(int k=0; k<3; k++) - verts[index++] = normals[offs*3+k]; - } - - // Make sure we wrote exactly the right amount of data - assert(index == verts.length); - - // Store the min/max values - mi.minHeight = min * 8; - mi.maxHeight = max * 8; -} - -// Generate the alpha splatting bitmap for one cell. -void genCellAlpha(ref GenLevelResult res) -{ - scope auto _trc = new MTrace("genCellAlpha"); - - int cellX = res.quad.info.cellX; - int cellY = res.quad.info.cellY; - assert(res.quad.info.level == 1); - - // Set the texture name - it's used internally as the material name - // at runtime. - assert(res.quad.meshes.length == 1); - res.setTexName("AMAT_"~toString(cellX)~"_"~toString(cellY)); - - // List of texture indices for this cell. A cell has 16x16 texture - // squares. - int ltex[16][16]; - - auto ltexData = mwland.getLTEXData(cellX, cellY); - - // A map from the global texture index to the local index for this - // cell. - int[int] textureMap; - - int texNum = 0; // Number of local indices - - // Loop through all the textures in the cell and get the indices - bool isDef = true; - for(int ty = 0; ty < 16; ty++) - for(int tx = 0; tx < 16; tx++) - { - // Get the texture in a given cell - char[] textureName = ltexData.getTexture(tx,ty); - - // If the default texture is used, skip it. The background - // texture covers it (for now - we might change that later.) - if(textureName == "") - { - ltex[ty][tx] = -1; - continue; - } - - isDef = false; - - // Store the global index - int index = cache.addTexture(textureName); - ltex[ty][tx] = index; - - // Add the index to the map - if(!(index in textureMap)) - textureMap[index] = texNum++; - } - - assert(texNum == textureMap.length); - - // If we only found default textures, exit now. - if(isDef) - return; - - int imageRes = texSizes[1]; - int dataSize = imageRes*imageRes; - - // Number of alpha pixels per texture square - int pps = imageRes/16; - - // Make sure there are at least as many alpha pixels as there are - // textures - assert(imageRes >= 16); - assert(imageRes%16 == 0); - assert(pps >= 1); - assert(texNum >= 1); - - // Allocate the alpha images - res.allocAlphas(imageRes, texNum); - assert(res.hasAlpha() && !res.isEmpty()); - - // Write the indices to the result list - foreach(int global, int local; textureMap) - res.setAlphaTex(local, global); - - ubyte *uptr = res.data.ptr; - - // Loop over all textures again. This time, do alpha splatting. - for(int ty = 0; ty < 16; ty++) - for(int tx = 0; tx < 16; tx++) - { - // Get the global texture index for this square, if any. - int index = ltex[ty][tx]; - if(index == -1) - continue; - - // Get the local index - index = textureMap[index]; - - // Get the offset of this square - long offs = index*dataSize + pps*(ty*imageRes + tx); - - // FIXME: Make real splatting later. This is just - // placeholder code. - - // Set alphas to full for this square - for(int y=0; y level); - assert(level > 2); - int fromSize = texSizes[level-1]; - int toSize = texSizes[level]; - - // Create a new image buffer large enough to hold the four - // sub textures - res.allocImage(fromSize*2); - - // Add the four sub-textures - for(int mi=0;mi<4;mi++) - { - ubyte[] src; - - // Use default texture if no source is present - if(maps.length == 0 || maps[mi].isEmpty()) - src = defaults[level-1].data; - else - src = maps[mi].data; - - assert(src.length == 3*fromSize*fromSize); - - // Find the sub-part of the destination buffer to write to - int x = (mi%2) * fromSize; - int y = (mi/2) * fromSize; - - // Copy the image into the new buffer - copyBox(src, res.data, fromSize, fromSize*2, x, y, 3); - } - - // Resize image if necessary - if(toSize != 2*fromSize) - res.resize(toSize); - - // Texture file name - char[] outname = res.getPNGName(maps.length == 0); - - // Save the image - res.save(outname); -} - -// Copy from one buffer into a sub-region of another buffer -void copyBox(ubyte[] src, ubyte[] dst, - int srcWidth, int dstWidth, - int dstX, int dstY, int pixSize) -{ - int fskip = srcWidth * pixSize; - int tskip = dstWidth * pixSize; - int rows = srcWidth; - int rowSize = srcWidth*pixSize; - - assert(src.length == pixSize*srcWidth*srcWidth); - assert(dst.length == pixSize*dstWidth*dstWidth); - assert(srcWidth <= dstWidth); - assert(dstX <= dstWidth-srcWidth && dstY <= dstWidth-srcWidth); - - // Source and destination pointers - ubyte *from = src.ptr; - ubyte *to = dst.ptr + dstY*tskip + dstX*pixSize; - - for(;rows>0;rows--) - { - memcpy(to, from, rowSize); - to += tskip; - from += fskip; - } -} - -// Create the mesh for this level, by merging the meshes from the -// previous levels. -void mergeMesh(GenLevelResult[] sub, ref GenLevelResult res) -{ - // How much to shift various numbers to the left at this level - // (ie. multiply by 2^shift). The height at each vertex is - // multiplied by 8 in each cell to get the final value. However, - // when we're merging two cells (in each direction), we have to - // scale down all the height values by 2 in order to fit the result - // in one byte. This means multiplying the factor by 2 for each - // level above the cell level. - int shift = res.quad.info.level - 1; - assert(shift >= 1); - assert(sub.length == 4); - - // Allocate the result buffer - res.allocMesh(65); - - MeshHolder *mh = &res.quad.meshes[0]; - MeshInfo *mi = &mh.info; - - // Basic info - mi.worldWidth = 8192 << shift; - - // Copy the mesh height from the top left mesh - mi.heightOffset = sub[0].getHeight(); - - // Output buffer - byte verts[] = mh.vertexBuffer; - - // Bytes per vertex - const int VSIZE = 5; - - // Used to calculate the max and min heights - float minh = 300000.0; - float maxh = -300000.0; - - foreach(si, s; sub) - { - int SX = si % 2; - int SY = si / 2; - - // Find the offset in the destination buffer - int dest = SX*32 + SY*65*32; - dest *= VSIZE; - - void putValue(int val) - { - assert(val >= short.min && val <= short.max); - *(cast(short*)&verts[dest]) = val; - dest += 2; - } - - if(s.hasMesh) - { - auto m = &s.quad.meshes[0]; - auto i = &m.info; - - minh = min(minh, i.minHeight); - maxh = max(maxh, i.maxHeight); - - byte[] source = m.vertexBuffer; - int src = 0; - - int getValue() - { - int s = *(cast(short*)&source[src]); - src += 2; - return s; - } - - // Loop through all the vertices in the mesh - for(int y=0;y<33;y++) - { - // Skip the first row in the mesh if there was a mesh - // above us. We assume that the previously written row - // already has the correct information. - if(y==0 && SY != 0) - { - src += 65*VSIZE; - dest += 65*VSIZE; - continue; - } - - // Handle the first vertex of the row outside the - // loop. - int height = getValue(); - - // If this isn't the very first row, sum up two row - // heights and skip the first row. - if(y!=0) - { - // Skip the rest of the row. - src += 64*VSIZE + 3; - - // Add the second height - height += getValue(); - } - - putValue(height); - - // Copy the normal - verts[dest++] = source[src++]; - verts[dest++] = source[src++]; - verts[dest++] = source[src++]; - - // Loop through the remaining 64 vertices in this row, - // processing two at a time. - for(int x=0;x<32;x++) - { - height = getValue(); - - // Sum up the next two heights - src += 3; // Skip normal - height += getValue(); - - // Set the height - putValue(height); - - // Copy the normal - verts[dest++] = source[src++]; - verts[dest++] = source[src++]; - verts[dest++] = source[src++]; - } - // Skip to the next row - dest += 32*VSIZE; - } - assert(src == source.length); - } - else - { - minh = min(minh, -2048); - maxh = max(maxh, -2048); - - // Set all the vertices to zero. - for(int y=0;y<33;y++) - { - if(y==0 && SY != 0) - { - dest += 65*VSIZE; - continue; - } - - for(int x=0;x<33;x++) - { - if(x==0 && SX != 0) - { - dest += VSIZE; - continue; - } - - // Zero height and vertical normal - verts[dest++] = 0; - verts[dest++] = 0; - verts[dest++] = 0; - verts[dest++] = 0; - verts[dest++] = 0x7f; - } - // Skip to the next row - dest += 32*VSIZE; - } - } - } - - mi.minHeight = minh; - mi.maxHeight = maxh; - assert(minh <= maxh); -} - -// ------- OLD CODE - use these snippets later ------- - -// About segments: -/* NOTES for the gen-phase: Was: -// This is pretty messy. Btw: 128*16 == 2048 == -// CELL_WIDTH/4 -// 65 points across one cell means 64 intervals, and 17 points - -// means 16=64/4 intervals. So IOW the number of verts when -// dividing by D is (65-1)/D + 1 = 64/D+1, which means that D -// should divide 64, that is, be a power of two < 64. - -addNewObject(Ogre::Vector3(x*16*128, 0, y*16*128), //pos -17, //size -false, //skirts -0.25f, float(x)/4.0f, float(y)/4.0f);//quad seg location -*/ - -/* This was also declared in the original code, you'll need it - when creating the cache data - - size_t vw = mWidth; // mWidth is 17 or 65 - if ( mUseSkirts ) vw += 2; // skirts are used for level 2 and up - vertCount=vw*vw; -*/ - -/** - * @brief fills the vertex buffer with data - * @todo I don't think tex co-ords are right - void calculateVertexValues() - { - int start = 0; - int end = mWidth; - - if ( mUseSkirts ) - { - --start; - ++end; - } - - for ( int y = start; y < end; y++ ) - for ( int x = start; x < end; x++ ) - { - if ( y < 0 || y > (mWidth-1) || x < 0 || x > (mWidth-1) ) - { - // These are the skirt vertices. 'Skirts' are simply a - // wall at the edges of the mesh that goes straight down, - // cutting off the posibility that you might see 'gaps' - // between the meshes. Or at least I think that's the - // intention. - - assert(mUseSkirts); - - // 1st coordinate - if ( x < 0 ) - *verts++ = 0; - else if ( x > (mWidth-1) ) - *verts++ = (mWidth-1)*getVertexSeperation(); - else - *verts++ = x*getVertexSeperation(); - - // 2nd coordinate - *verts++ = -4096; //2048 below base sea floor height - - // 3rd coordinate - if ( y < 0 ) - *verts++ = 0; - else if ( y > (mWidth-1) ) - *verts++ = (mWidth-1)*getVertexSeperation(); - else - *verts++ = y*getVertexSeperation(); - - // No normals - for ( Ogre::uint i = 0; i < 3; i++ ) - *verts++ = 0; - - // It shouldn't matter if these go over 1 - float u = (float)(x) / (mWidth-1); - float v = (float)(y) / (mWidth-1); - *verts++ = u; - *verts++ = v; - } - else // Covered already - - void calculateIndexValues() - { - size_t vw = mWidth-1; - if ( mUseSkirts ) vw += 2; - - const size_t indexCount = (vw)*(vw)*6; - - //need to manage allocation if not null - assert(mIndices==0); - - // buffer was created here - - bool flag = false; - Ogre::uint indNum = 0; - for ( Ogre::uint y = 0; y < (vw); y+=1 ) { - for ( Ogre::uint x = 0; x < (vw); x+=1 ) { - - const int line1 = y * (vw+1) + x; - const int line2 = (y + 1) * (vw+1) + x; - - if ( flag ) { - *indices++ = line1; - *indices++ = line2; - *indices++ = line1 + 1; - - *indices++ = line1 + 1; - *indices++ = line2; - *indices++ = line2 + 1; - } else { - *indices++ = line1; - *indices++ = line2; - *indices++ = line2 + 1; - - *indices++ = line1; - *indices++ = line2 + 1; - *indices++ = line1 + 1; - } - flag = !flag; //flip tris for next time - - indNum+=6; - } - flag = !flag; //flip tries for next row - } - assert(indNum==indexCount); - //return mIndices; - } -*/ diff --git a/old_d_version/terrain/myfile.d b/old_d_version/terrain/myfile.d deleted file mode 100644 index 15239ac2e..000000000 --- a/old_d_version/terrain/myfile.d +++ /dev/null @@ -1,71 +0,0 @@ -/* - OpenMW - The completely unofficial reimplementation of Morrowind - Copyright (C) 2009 Nicolay Korslund - WWW: http://openmw.sourceforge.net/ - - This file (archive.d) is part of the OpenMW package. - - OpenMW is distributed as free software: you can redistribute it - and/or modify it under the terms of the GNU General Public License - version 3, as published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - version 3 along with this program. If not, see - http://www.gnu.org/licenses/ . - -*/ - -module terrain.myfile; - -import std.stream; -import std.stdio; - -// Add a couple of helper functions to the file stream -class MyFile : File -{ - this(string filename, FileMode mode = FileMode.In) - { - super(filename, mode); - } - - void fill(T)(ref T t) - { - readExact(&t, T.sizeof); - } - - void dump(T)(ref T t) - { - writeExact(&t, T.sizeof); - } - - void fillArray(T)(T[] t) - { - readExact(t.ptr, t.length*T.sizeof); - } - - void dumpArray(T)(T[] t) - { - writeExact(t.ptr, t.length*T.sizeof); - } - - void readArray(T)(ref T[] arr) - { - int size; - read(size); - assert(size < 1024*1024 && size > 0); - arr = new T[size]; - fillArray!(T)(arr); - } - - void writeArray(T)(T[] t) - { - int size = t.length; - write(size); - dumpArray!(T)(t); - } -} diff --git a/old_d_version/terrain/outbuffer.d b/old_d_version/terrain/outbuffer.d deleted file mode 100644 index c496a4787..000000000 --- a/old_d_version/terrain/outbuffer.d +++ /dev/null @@ -1,94 +0,0 @@ -/* - OpenMW - The completely unofficial reimplementation of Morrowind - Copyright (C) 2008-2009 Nicolay Korslund - Email: < korslund@gmail.com > - WWW: http://openmw.sourceforge.net/ - - This file (outbuffer.d) is part of the OpenMW package. - - OpenMW is distributed as free software: you can redistribute it - and/or modify it under the terms of the GNU General Public License - version 3, as published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - version 3 along with this program. If not, see - http://www.gnu.org/licenses/ . - */ - -/* - This files provides a simple buffer class used for writing the cache - files. It lets you 'write' data to a growing memory buffer and - allows you to change the written data after the fact (since it's - retained in memory.) When you're done, you can write the entire - buffer to a stream in one operation. - */ - -module terrain.outbuffer; - -import util.regions; -import std.stream; - -class OutBuffer -{ - private: - RegionManager reg; - long used; - void[][] buffers; - - public: - this() - { - reg = new RegionManager("Outbuf", 200*1024); - } - - void reset() - { - if(buffers.length) - delete buffers; - - reg.freeAll(); - used = 0; - buffers = null; - } - - // Write everyting to a stream as one buffer - void writeTo(Stream str) - { - foreach(void[] v; buffers) - str.writeExact(v.ptr, v.length); - - reset(); - } - - // Get a pointer to a new block at least 'bytes' large, but don't - // add it to the list. - void[] reserve(size_t bytes) - { return reg.allocate(bytes); } - - // Get a new block which is 'bytes' size large. - void[] add(size_t bytes) - { - void[] p = reserve(bytes); - add(p); - return p; - } - - // Add an existing block to the write list - void add(void[] p) - { - buffers ~= p; - used += p.length; - } - - T[] write(T)(size_t num) - { - return cast(T[])add(num * T.sizeof); - } - - size_t size() { return used; } -} diff --git a/old_d_version/terrain/quad.d b/old_d_version/terrain/quad.d deleted file mode 100644 index c68afc1ca..000000000 --- a/old_d_version/terrain/quad.d +++ /dev/null @@ -1,286 +0,0 @@ -module terrain.quad; - -import terrain.archive; -import terrain.bindings; -import std.stdio; -import monster.vm.dbg; - -const int CELL_WIDTH = 8192; -const float SPLIT_FACTOR = 0.5; -const float UNSPLIT_FACTOR = 2.0; - -class Quad -{ - this(int cellX=0, int cellY=0, Quad parent = null) - { - scope auto _trc = new MTrace("Quad.this"); - - mCellX = cellX; - mCellY = cellY; - - // Do we have a parent? - if(parent !is null) - { - mLevel = parent.mLevel-1; - - // Coordinates relative to our parent - int relX = cellX - parent.mCellX; - int relY = cellY - parent.mCellY; - - // The coordinates give the top left corner of the quad, or our - // relative coordinates within that should always be positive. - assert(relX >= 0); - assert(relY >= 0); - - // Create a child scene node. The scene node position is given in - // world units, ie. CELL_WIDTH units per cell. - mNode = terr_createChildNode(relX*CELL_WIDTH, - relY*CELL_WIDTH, - parent.mNode); - - // Get the archive data for this quad. - mInfo = g_archive.getQuad(mCellX,mCellY,mLevel); - - // Set up the bounding box. Use MW coordinates all the - // way. - mBounds = terr_makeBounds(mInfo.minHeight, - mInfo.maxHeight, - mInfo.worldWidth, - mNode); - - float radius = mInfo.boundingRadius; - - mSplitDistance = radius * SPLIT_FACTOR; - mUnsplitDistance = radius * UNSPLIT_FACTOR; - - // Square the distances - mSplitDistance *= mSplitDistance; - mUnsplitDistance *= mUnsplitDistance; - - if(mLevel == 1) - { - // Create the terrain and leave it there. - buildTerrain(); - isStatic = true; - } - } - else - { - // No parent, this is the top-most quad. Get all the info from - // the archive. - mInfo = g_archive.rootQuad; - assert(mInfo); - - mLevel = mInfo.level; - cellX = mCellX = mInfo.cellX; - cellY = mCellY = mInfo.cellY; - - mNode = terr_createChildNode(cellX*CELL_WIDTH, - cellY*CELL_WIDTH, - null); - - // Split up - split(); - - // The root can never be unsplit - isStatic = true; - } - - assert(mLevel >= 1); - assert(mNode !is null); - - // Update the terrain. This will create the mesh or children if - // necessary. - update(); - } - - ~this() - { - scope auto _trc = new MTrace("Quad.~this"); - - // TODO: We might rewrite the code so that the quads are never - // actually destroyed, just 'inactivated' by hiding their scene - // node. We only call update on our children if we don't have a - // mesh ourselves. - if(hasMesh) - destroyTerrain(); - else if(hasChildren) - for (size_t i = 0; i < 4; i++) - delete mChildren[i]; - - terr_destroyNode(mNode); - if(mBounds !is null) - terr_killBounds(mBounds); - } - - // Remove the landscape for this quad, and create children. - void split() - { - scope auto _trc = new MTrace("split"); - // Never split a static quad or a quad that already has children. - assert(!isStatic); - assert(!hasChildren); - assert(mLevel > 1); - - if(hasMesh) - destroyTerrain(); - - // Find the cell width of our children - int cWidth = 1 << (mLevel-2); - - // Create children - for ( size_t i = 0; i < 4; ++i ) - { - if(!mInfo.hasChild[i]) - continue; - - // The cell coordinates for this child quad - int x = (i%2)*cWidth + mCellX; - int y = (i/2)*cWidth + mCellY; - - mChildren[i] = new Quad(x,y,this); - } - hasChildren = true; - } - - // Removes children and rebuilds terrain - void unsplit() - { - scope auto _trc = new MTrace("unsplit"); - // Never unsplit the root quad - assert(mLevel < g_archive.rootQuad.level); - // Never unsplit a static or quad that isn't split. - assert(!isStatic); - assert(hasChildren); - assert(!hasMesh); - - for( size_t i = 0; i < 4; i++ ) - { - delete mChildren[i]; - mChildren[i] = null; - } - - buildTerrain(); - - hasChildren = false; - } - - // Determines whether to split or unsplit the quad, and immediately - // does it. - void update() - { - scope auto _trc = new MTrace("Quad.update()"); - - // Static quads don't change - if(!isStatic) - { - assert(mUnsplitDistance > mSplitDistance); - - // Get (squared) camera distance. TODO: shouldn't this just - // be a simple vector difference from the mesh center? - assert(mBounds !is null); - float camDist = terr_getSqCamDist(mBounds); - - // No children? - if(!hasChildren) - { - // If we're close, split now. - if(camDist < mSplitDistance) - split(); - else - { - // We're not close, and don't have any children. Should we - // built terrain? - if(!hasMesh) - buildTerrain(); - return; - } - } - - // If we get here, we either had children when we entered, - // or we just performed a split. - assert(!hasMesh); - assert(hasChildren); - - // If the camera is too far away, kill the children. - if(camDist > mUnsplitDistance) - { - unsplit(); - return; - } - } - else if(!hasChildren) - return; - - // We have children and we're happy about it. Update them too. - for(int i; i < 4; ++i) - { - Quad q = mChildren[i]; - if(q !is null) q.update(); - } - } - - // Build the terrain for this quad - void buildTerrain() - { - scope auto _trc = new MTrace("buildTerrain"); - - assert(!hasMesh); - assert(!isStatic); - - // Map the terrain data into memory. - assert(mInfo); - g_archive.mapQuad(mInfo); - - // Create one mesh for each segment in the quad. TerrainMesh takes - // care of the loading. - meshList.length = mInfo.meshNum; - foreach(i, ref m; meshList) - { - MeshInfo *mi = g_archive.getMeshInfo(i); - m = terr_makeMesh(mNode, mi, mInfo.level, TEX_SCALE); - } - - hasMesh = true; - } - - void destroyTerrain() - { - scope auto _trc = new MTrace("destroyTerrain"); - - assert(hasMesh); - - foreach(m; meshList) - terr_killMesh(m); - - meshList[] = null; - hasMesh = false; - } - - private: - - // List of meshes, if any. The meshes are C++ objects. - MeshObj meshList[]; - - // Scene node. All child quads are added to this. - SceneNode mNode; - - // Bounding box, transformed to world coordinates. Used to calculate - // camera distance. - Bounds mBounds; - - float mSplitDistance,mUnsplitDistance; - - Quad mChildren[4]; - - // Contains the 'level' of this node. Level 1 is the closest and - // most detailed level - int mLevel; - int mCellX, mCellY; - - QuadInfo *mInfo; - - bool hasMesh; - bool hasChildren; - bool isStatic; // Static quads are never split or unsplit -} diff --git a/old_d_version/terrain/terrain.d b/old_d_version/terrain/terrain.d deleted file mode 100644 index 4bf03c87b..000000000 --- a/old_d_version/terrain/terrain.d +++ /dev/null @@ -1,103 +0,0 @@ -/* - OpenMW - The completely unofficial reimplementation of Morrowind - Copyright (C) 2008-2009 Nicolay Korslund - Email: < korslund@gmail.com > - WWW: http://openmw.snaptoad.com/ - - This file (terrain.d) is part of the OpenMW package. - - OpenMW is distributed as free software: you can redistribute it - and/or modify it under the terms of the GNU General Public License - version 3, as published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - version 3 along with this program. If not, see - http://www.gnu.org/licenses/ . - - */ - -module terrain.terrain; - -import terrain.generator; -import terrain.archive; -import terrain.bindings; -import terrain.quad; -import std.file, std.stdio; - -char[] cacheDir = "cache/terrain/"; - -// Enable this to render single terrain meshes instead of the entire -// data set -//debug=singleMesh; - -void initTerrain(bool doGen) -{ - char[] fname = cacheDir ~ "landscape.cache"; - - if(!exists(fname)) - { - writefln("Cache file '%s' not found. Creating:", - fname); - doGen = true; - } - - if(doGen) - generate(fname); - - // Load the archive file - g_archive.openFile(fname); - - terr_setupRendering(); - - debug(singleMesh) - { - int X = 22; - int Y = 0; - bool next = false; - - void doQuad(int x, int y, int lev, int diffx, int diffy) - { - if(!g_archive.hasQuad(x,y,lev)) - return; - - diffx *= 8192; - diffy *= 8192; - - auto node = terr_createChildNode(20000+diffx,-60000+diffy,null); - auto info = g_archive.getQuad(x,y,lev); - g_archive.mapQuad(info); - auto mi = g_archive.getMeshInfo(0); - terr_makeMesh(node, mi, info.level, TEX_SCALE); - } - - doQuad(X,Y,1, 0,0); - doQuad(X+1,Y,1, 1,0); - doQuad(X,Y+1,1, 0,1); - doQuad(X+1,Y+1,1, 1,1); - - doQuad(X + (next?2:0),Y,2, 2,0); - - doQuad(20,Y,3, 0,2); - } - else - { - // Create the root quad - rootQuad = new Quad; - } -} - -extern(C) void d_terr_terrainUpdate() -{ - debug(singleMesh) return; - - // Update the root quad each frame. - assert(rootQuad !is null); - rootQuad.update(); -} - -Quad rootQuad;