mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-10-31 19:56:38 +00:00 
			
		
		
		
	- integrated Yacobys landscape. First commit.
git-svn-id: https://openmw.svn.sourceforge.net/svnroot/openmw/trunk@112 ea6a568a-9f4f-0410-981a-c910a81bb256
This commit is contained in:
		
							parent
							
								
									ea486dd770
								
							
						
					
					
						commit
						a46804dae3
					
				
					 32 changed files with 3976 additions and 212 deletions
				
			
		
							
								
								
									
										26
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								Makefile
									
									
									
									
									
								
							|  | @ -7,8 +7,12 @@ DMD=gdmd -version=Posix | |||
| # Some extra flags for niftool and bsatool
 | ||||
| NIFFLAGS=-debug=warnstd -debug=check -debug=statecheck -debug=strict -debug=verbose | ||||
| 
 | ||||
| # Linker flags
 | ||||
| LFLAGS= -L-lopenal -L-lOgreMain -L-lOIS -L-lmygui -L-luuid -L-lavcodec -L-lavformat bullet/libbulletdynamics.a bullet/libbulletcollision.a bullet/libbulletmath.a -L-lboost_serialization | ||||
| 
 | ||||
| # Compiler settings for Ogre, OIS and MyGUI
 | ||||
| CF_OIS=$(shell pkg-config --cflags OIS OGRE MyGUI) | ||||
| # TODO: the -I when we're done
 | ||||
| CF_OIS=$(shell pkg-config --cflags OIS OGRE MyGUI) -Iterrain/ | ||||
| OGCC=$(CXX) $(CXXFLAGS) $(CF_OIS) | ||||
| 
 | ||||
| # Compiler settings for ffmpeg.
 | ||||
|  | @ -23,10 +27,15 @@ BGCC=$(CXX) $(CXXFLAGS) $(CF_BULLET) | |||
| # passed to the compiler, the rest are dependencies.
 | ||||
| ogre_cpp=ogre framelistener interface bsaarchive | ||||
| 
 | ||||
| # MyGUI C++ files, gui/cpp_X.cpp. These are currently included into
 | ||||
| # with cpp_ogre.cpp.
 | ||||
| # MyGUI C++ files, gui/cpp_X.cpp. These are currently included
 | ||||
| # cpp_ogre.o with cpp_ogre.cpp.
 | ||||
| mygui_cpp=mygui console | ||||
| 
 | ||||
| # Ditto for the landscape engine, in terrain/cpp_X.cpp
 | ||||
| terrain_cpp=baseland esm framelistener generator index landdata\
 | ||||
| materialgen meshinterface mwheightmap mwquadmatgen palette point2\ | ||||
| quad quaddata quadsegment terraincls terrain terrainmesh | ||||
| 
 | ||||
| # FFmpeg files, in the form sound/cpp_X.cpp.
 | ||||
| avcodec_cpp=avcodec | ||||
| 
 | ||||
|  | @ -35,7 +44,10 @@ bullet_cpp=bullet player scale | |||
| 
 | ||||
| #### No modifications should be required below this line. ####
 | ||||
| 
 | ||||
| ogre_cpp_files=$(ogre_cpp:%=ogre/cpp_%.cpp) $(mygui_cpp:%=gui/cpp_%.cpp) | ||||
| ogre_cpp_files=\
 | ||||
| 	$(ogre_cpp:%=ogre/cpp_%.cpp) \
 | ||||
| 	$(mygui_cpp:%=gui/cpp_%.cpp) \
 | ||||
| 	$(terrain_cpp:%=terrain/cpp_%.cpp) | ||||
| avcodec_cpp_files=$(avcodec_cpp:%=sound/cpp_%.cpp) | ||||
| bullet_cpp_files=$(bullet_cpp:%=bullet/cpp_%.cpp) | ||||
| 
 | ||||
|  | @ -43,7 +55,7 @@ bullet_cpp_files=$(bullet_cpp:%=bullet/cpp_%.cpp) | |||
| src := $(wildcard bsa/*.d) $(wildcard bullet/*.d) $(wildcard core/*.d) \
 | ||||
| $(wildcard esm/*.d) $(wildcard input/*.d) $(wildcard nif/*.d) $(wildcard ogre/*.d) \ | ||||
| $(wildcard scene/*.d) $(wildcard sound/*.d) $(wildcard util/*.d) $(wildcard gui/*.d) | ||||
| src := $(src) $(wildcard mscripts/*.d) | ||||
| src := $(src) $(wildcard mscripts/*.d) $(wildcard terrain/*.d) | ||||
| src := $(src) monster/monster.d \
 | ||||
| $(wildcard monster/vm/*.d) \ | ||||
| $(wildcard monster/compiler/*.d) \ | ||||
|  | @ -85,10 +97,10 @@ nifobjs/%.o: %.d | |||
| 	$(DMD) $(NIFFLAGS) -c $< -of$@ | ||||
| 
 | ||||
| openmw: openmw.d cpp_ogre.o cpp_avcodec.o cpp_bullet.o $(obj) | ||||
| 	$(DMD) $^ -of$@ -L-lopenal -L-lOgreMain -L-lOIS -L-lmygui -L-luuid -L-lavcodec -L-lavformat bullet/libbulletdynamics.a bullet/libbulletcollision.a bullet/libbulletmath.a | ||||
| 	$(DMD) $^ -of$@ $(LFLAGS) | ||||
| 
 | ||||
| esmtool: esmtool.d cpp_ogre.o cpp_avcodec.o cpp_bullet.o $(obj) | ||||
| 	$(DMD) $^ -of$@ -L-lopenal -L-lOgreMain -L-lOIS -L-lmygui -L-luuid -L-lavcodec -L-lavformat bullet/libbulletdynamics.a bullet/libbulletcollision.a bullet/libbulletmath.a | ||||
| 	$(DMD) $^ -of$@ $(LFLAGS) | ||||
| 
 | ||||
| niftool: niftool.d $(obj_nif) | ||||
| 	$(DMD) $^ -of$@ | ||||
|  |  | |||
|  | @ -32,6 +32,7 @@ typedef void* WidgetPtr; | |||
| void gui_setupGUI(int debugOut); | ||||
| void gui_toggleGui(); | ||||
| void gui_setCellName(char *str); | ||||
| void gui_showHUD(); | ||||
| 
 | ||||
| // Console stuff
 | ||||
| void gui_toggleConsole(); | ||||
|  |  | |||
|  | @ -450,6 +450,12 @@ extern "C" void gui_toggleGui() | |||
| 
 | ||||
| extern "C" int32_t* gui_getGuiModePtr() { return &guiMode; } | ||||
| 
 | ||||
| extern "C" void gui_showHUD() | ||||
| { | ||||
|   if(hud) | ||||
|     hud->setVisible(true); | ||||
| } | ||||
| 
 | ||||
| extern "C" void gui_setupGUI(int32_t debugOut) | ||||
| { | ||||
|   ResourceGroupManager::getSingleton(). | ||||
|  | @ -480,7 +486,6 @@ extern "C" void gui_setupGUI(int32_t debugOut) | |||
|   state.X.abs = mWidth / 2; | ||||
|   state.Y.abs = mHeight / 2; | ||||
| 
 | ||||
|   //*
 | ||||
|   // Set up the HUD
 | ||||
|   hud = new HUD(); | ||||
| 
 | ||||
|  | @ -494,7 +499,8 @@ extern "C" void gui_setupGUI(int32_t debugOut) | |||
|   hud->setSpellStatus(65, 100); | ||||
| 
 | ||||
|   hud->setEffect("icons\\s\\tx_s_chameleon.dds"); | ||||
|   //*/
 | ||||
| 
 | ||||
|   hud->setVisible(false); | ||||
| 
 | ||||
|   //new MainMenu();
 | ||||
| } | ||||
|  |  | |||
							
								
								
									
										18
									
								
								gui/gui.d
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								gui/gui.d
									
									
									
									
									
								
							|  | @ -232,6 +232,24 @@ void getHeight() | |||
|   stack.pushInt(gui_getHeight(null)); | ||||
| } | ||||
| 
 | ||||
| void initGUI(bool debugOut) | ||||
| { | ||||
|   // Load the GUI system
 | ||||
|   gui_setupGUI(debugOut); | ||||
| } | ||||
| 
 | ||||
| void startGUI() | ||||
| { | ||||
|   gui_showHUD(); | ||||
| 
 | ||||
|   // Run GUI scripts
 | ||||
|   // Create the HUD and windows
 | ||||
|   vm.run("makegui.mn"); | ||||
| 
 | ||||
|   // Run the fps ticker
 | ||||
|   vm.run("fpsticker.mn"); | ||||
| } | ||||
| 
 | ||||
| void setupGUIScripts() | ||||
| { | ||||
|   vm.addPath("mscripts/guiscripts/"); | ||||
|  |  | |||
|  | @ -214,10 +214,14 @@ 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; | ||||
| 
 | ||||
| void initializeInput() | ||||
| // 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.
 | ||||
|  | @ -228,6 +232,11 @@ void initializeInput() | |||
| 
 | ||||
|       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
 | ||||
|  | @ -250,6 +259,22 @@ bool isPressed(Keys key) | |||
|   return false; | ||||
| } | ||||
| 
 | ||||
| // Enable superman mode, ie. flight and super-speed. This is getting
 | ||||
| // very spaghetti-ish.
 | ||||
| extern(C) void d_superman() | ||||
| { | ||||
|   bullet_fly(); | ||||
|   speed = 8000; | ||||
| 
 | ||||
|   with(*playerData.position) | ||||
|     { | ||||
|       position[0] = 0; | ||||
|       position[1] = 0; | ||||
|       position[2] = 12000; | ||||
|     } | ||||
|   movePlayer(); | ||||
| } | ||||
| 
 | ||||
| extern(C) int d_frameStarted(float time) | ||||
| { | ||||
|   if(doExit) return 0; | ||||
|  | @ -270,9 +295,6 @@ extern(C) int d_frameStarted(float time) | |||
|   // The rest is ignored in pause or GUI mode
 | ||||
|   if(pause || *guiMode > 0) return 1; | ||||
| 
 | ||||
|   // Walking / floating speed, in points per second.
 | ||||
|   const float speed = 300; | ||||
| 
 | ||||
|   // Check if the movement keys are pressed
 | ||||
|   float moveX = 0, moveY = 0, moveZ = 0; | ||||
|   float x, y, z, ox, oy, oz; | ||||
|  |  | |||
|  | @ -54,17 +54,6 @@ void initMonsterScripts() | |||
|   setupGUIScripts(); | ||||
| } | ||||
| 
 | ||||
| // Run the GUI scripts. These should be run only after the
 | ||||
| // GUI/rendering system has been initialized
 | ||||
| void runGUIScripts() | ||||
| { | ||||
|   // Create the HUD and windows
 | ||||
|   vm.run("makegui.mn"); | ||||
| 
 | ||||
|   // Run the fps ticker
 | ||||
|   vm.run("fpsticker.mn"); | ||||
| } | ||||
| 
 | ||||
| // This should probably not be here:
 | ||||
| import monster.vm.dbg; | ||||
| 
 | ||||
|  |  | |||
|  | @ -59,34 +59,9 @@ extern "C" int32_t ogre_configure( | |||
| 
 | ||||
|   mRoot = new Root(plugincfg, "ogre.cfg", ""); | ||||
| 
 | ||||
|   // Add the BSA archive manager before reading the config file.
 | ||||
|   // Add the BSA archive manager
 | ||||
|   ArchiveManager::getSingleton().addArchiveFactory( &mBSAFactory ); | ||||
| 
 | ||||
|   /* The only entry we use from resources.cfg is the "BSA=internal"
 | ||||
|      entry, which we can put in manually. | ||||
| 
 | ||||
|   // Load resource paths from config file
 | ||||
|   ConfigFile cf; | ||||
|   cf.load("resources.cfg"); | ||||
| 
 | ||||
|   // Go through all sections & settings in the file
 | ||||
|   ConfigFile::SectionIterator seci = cf.getSectionIterator(); | ||||
| 
 | ||||
|   String secName, typeName, archName; | ||||
|   while (seci.hasMoreElements()) | ||||
|     { | ||||
|       secName = seci.peekNextKey(); | ||||
|       ConfigFile::SettingsMultiMap *settings = seci.getNext(); | ||||
|       ConfigFile::SettingsMultiMap::iterator i; | ||||
|       for (i = settings->begin(); i != settings->end(); ++i) | ||||
| 	{ | ||||
| 	  typeName = i->first; | ||||
| 	  archName = i->second; | ||||
| 	  ResourceGroupManager::getSingleton().addResourceLocation( | ||||
| 			     archName, typeName, secName); | ||||
| 	} | ||||
|     } | ||||
|   */ | ||||
|   ResourceGroupManager::getSingleton(). | ||||
|     addResourceLocation("internal", "BSA", "General"); | ||||
| 
 | ||||
|  | @ -328,10 +303,7 @@ void cloneNode(SceneNode *from, SceneNode *to, char* name) | |||
|   SceneNode::ObjectIterator it = from->getAttachedObjectIterator(); | ||||
|   while(it.hasMoreElements()) | ||||
|     { | ||||
|       // We can't handle non-entities. To be honest I have no idea
 | ||||
|       // what dynamic_cast does or if it's correct here. I used to be
 | ||||
|       // a C++ person but after discovering D I dropped C++ like it
 | ||||
|       // was red hot iron and never looked back.
 | ||||
|       // We can't handle non-entities.
 | ||||
|       Entity *e = dynamic_cast<Entity*> (it.getNext()); | ||||
|       if(e) | ||||
|         { | ||||
|  |  | |||
|  | @ -70,8 +70,11 @@ int32_t guiMode = 0; | |||
| // Morrowind.
 | ||||
| SceneNode *root; | ||||
| 
 | ||||
| // Include the other parts of the code, and make one big object file.
 | ||||
| // Include the other parts of the code, and make one big happy object
 | ||||
| // file. This is extremely against the grain of C++ "recomended
 | ||||
| // practice", but I don't care.
 | ||||
| #include "../gui/cpp_mygui.cpp" | ||||
| #include "cpp_framelistener.cpp" | ||||
| #include "cpp_bsaarchive.cpp" | ||||
| #include "cpp_interface.cpp" | ||||
| #include "../terrain/cpp_terrain.cpp" | ||||
|  |  | |||
|  | @ -119,12 +119,6 @@ void setupOgre(bool debugOut) | |||
|   // exterior cells differently, etc.
 | ||||
|   ogre_makeScene(); | ||||
| 
 | ||||
|   // Load the GUI system
 | ||||
|   gui_setupGUI(debugOut); | ||||
| 
 | ||||
|   // Run the GUI scripts
 | ||||
|   runGUIScripts(); | ||||
| 
 | ||||
|   ogreSetup = true; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										278
									
								
								openmw.d
									
									
									
									
									
								
							
							
						
						
									
										278
									
								
								openmw.d
									
									
									
									
									
								
							|  | @ -1,6 +1,6 @@ | |||
| /* | ||||
|   OpenMW - The completely unofficial reimplementation of Morrowind | ||||
|   Copyright (C) 2008  Nicolay Korslund | ||||
|   Copyright (C) 2008-2009  Nicolay Korslund | ||||
|   Email: < korslund@gmail.com > | ||||
|   WWW: http://openmw.snaptoad.com/
 | ||||
| 
 | ||||
|  | @ -31,6 +31,7 @@ import std.file; | |||
| import ogre.ogre; | ||||
| import ogre.bindings; | ||||
| import gui.bindings; | ||||
| import gui.gui; | ||||
| 
 | ||||
| import bullet.bullet; | ||||
| 
 | ||||
|  | @ -51,6 +52,8 @@ import sound.audio; | |||
| 
 | ||||
| import input.events; | ||||
| 
 | ||||
| import terrain.terrain; | ||||
| 
 | ||||
| //*
 | ||||
| import std.gc; | ||||
| import gcstats; | ||||
|  | @ -72,6 +75,7 @@ void main(char[][] args) | |||
|   bool showOgreFlag = false; | ||||
|   bool noSound = false; | ||||
|   bool debugOut = false; | ||||
|   bool extTest = false; | ||||
| 
 | ||||
|   // Some examples to try:
 | ||||
|   //
 | ||||
|  | @ -88,16 +92,10 @@ void main(char[][] args) | |||
| 
 | ||||
|   // Cells to load
 | ||||
|   char[][] cells; | ||||
|   int[] eCells; | ||||
| 
 | ||||
|   foreach(char[] a; args[1..$]) | ||||
|     if(a == "-n") render = false; | ||||
|     else if(a.begins("-e")) | ||||
|       { | ||||
| 	int i = find(a,','); | ||||
| 	eCells ~= atoi(a[2..i]); | ||||
| 	eCells ~= atoi(a[i+1..$]); | ||||
|       } | ||||
|     else if(a == "-ex") extTest = true; | ||||
|     else if(a == "-h") help=true; | ||||
|     else if(a == "-rk") resetKeys = true; | ||||
|     else if(a == "-oc") showOgreFlag = true; | ||||
|  | @ -112,7 +110,7 @@ void main(char[][] args) | |||
|       } | ||||
|     else cells ~= a; | ||||
| 
 | ||||
|   if(cells.length + eCells.length/2 > 1 ) | ||||
|   if(cells.length > 1) | ||||
|     { | ||||
|       writefln("More than one cell specified, rendering disabled"); | ||||
|       render=false; | ||||
|  | @ -123,7 +121,7 @@ void main(char[][] args) | |||
|       writefln("Syntax: %s [options] cell-name [cell-name]", args[0]); | ||||
|       writefln("  Options:"); | ||||
|       writefln("    -n            Only load, do not render"); | ||||
|       writefln("    -ex,y         Load exterior cell (x,y)"); | ||||
|       writefln("    -ex           Test the terrain system"); | ||||
|       writefln("    -rk           Reset key bindings to default"); | ||||
|       writefln("    -oc           Show the Ogre config dialogue"); | ||||
|       writefln("    -ns           Completely disable sound"); | ||||
|  | @ -163,14 +161,14 @@ void main(char[][] args) | |||
|   // setting.
 | ||||
|   if(showOgreFlag) config.finalOgreConfig = true; | ||||
| 
 | ||||
|   if(cells.length == 0 && eCells.length == 0) | ||||
|   if(cells.length == 0) | ||||
|     if(config.defaultCell.length) | ||||
|       cells ~= config.defaultCell; | ||||
| 
 | ||||
|   if(cells.length == 1) | ||||
|     config.defaultCell = cells[0]; | ||||
| 
 | ||||
|   if(cells.length == 0 && eCells.length == 0) | ||||
|   if(cells.length == 0) | ||||
|     { | ||||
|       showHelp(); | ||||
|       return; | ||||
|  | @ -206,27 +204,6 @@ Try specifying another cell name on the command line, or edit openmw.ini."); | |||
| 	} | ||||
|     } | ||||
| 
 | ||||
|   for(int i; i<eCells.length; i+=2) | ||||
|     { | ||||
|       int x = eCells[i]; | ||||
|       int y = eCells[i+1]; | ||||
| 
 | ||||
|       // Release the last cell data
 | ||||
|       cellList.release(cd); | ||||
| 
 | ||||
|       // Get a cell data holder and load an interior cell
 | ||||
|       cd = cellList.get(); | ||||
| 
 | ||||
|       if(debugOut) writefln("Will load %s,%s", x, y); | ||||
|       try cd.loadExtCell(x,y); | ||||
|       catch(Exception e) | ||||
| 	{ | ||||
| 	  writefln(e); | ||||
| 	  writefln("\nUnable to load cell (%s,%s). Aborting", x,y); | ||||
| 	  return; | ||||
| 	} | ||||
|     } | ||||
| 	     | ||||
|   // Simple safety hack
 | ||||
|   NodePtr putObject(MeshIndex m, Placement *pos, float scale, | ||||
|                     bool collide=false) | ||||
|  | @ -250,21 +227,23 @@ Try specifying another cell name on the command line, or edit openmw.ini."); | |||
|       setupOgre(debugOut); | ||||
|       scope(exit) cleanupOgre(); | ||||
| 
 | ||||
|       // Create the GUI system
 | ||||
|       initGUI(debugOut); | ||||
| 
 | ||||
|       // Set up Bullet
 | ||||
|       initBullet(); | ||||
|       scope(exit) cleanupBullet(); | ||||
| 
 | ||||
|       if(cd.inCell) | ||||
| 	{ | ||||
| 	  setAmbient(cd.ambi.ambient, cd.ambi.sunlight, | ||||
| 		     cd.ambi.fog, cd.ambi.fogDensity); | ||||
|       // Initialize the internal input and event manager. The
 | ||||
|       // lower-level input system (OIS) is initialized by the
 | ||||
|       // setupOgre() call further up.
 | ||||
|       initializeInput(); | ||||
| 
 | ||||
| 	  // Not all interior cells have water
 | ||||
| 	  if(cd.inCell.flags & CellFlags.HasWater) | ||||
| 	    ogre_createWater(cd.water); | ||||
| 	} | ||||
|       else | ||||
| 	{ | ||||
|       // Play some old tunes
 | ||||
|       if(extTest) | ||||
|         { | ||||
|           // Exterior cell
 | ||||
|           /* | ||||
| 	  Color c; | ||||
| 	  c.red = 180; | ||||
| 	  c.green = 180; | ||||
|  | @ -276,104 +255,118 @@ Try specifying another cell name on the command line, or edit openmw.ini."); | |||
| 
 | ||||
| 	  // Create an ugly sky
 | ||||
| 	  ogre_makeSky(); | ||||
| 	} | ||||
|           */ | ||||
| 
 | ||||
|       // 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(!noSound) | ||||
| 	  { | ||||
|             Sound *s = ls.m.sound; | ||||
|             if(s) | ||||
| 	    { | ||||
| 	      ls.loopSound = soundScene.insert(s, true); | ||||
| 	      if(ls.loopSound) | ||||
|           initTerrain(); | ||||
|         } | ||||
|       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(!noSound) | ||||
|                 { | ||||
|                   auto p = ls.getPos(); | ||||
|                   ls.loopSound.setPos(p.position[0], | ||||
|                                       p.position[1], | ||||
|                                       p.position[2]); | ||||
|                   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(!noSound) | ||||
|           { | ||||
| 	  Sound *s = ls.m.sound; | ||||
| 	  if(s) | ||||
| 	    { | ||||
|               ls.loopSound = soundScene.insert(s, true); | ||||
|               if(ls.loopSound) | ||||
|             } | ||||
|           // 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(!noSound) | ||||
|                 { | ||||
|                   auto p = ls.getPos(); | ||||
|                   ls.loopSound.setPos(p.position[0], | ||||
|                                       p.position[1], | ||||
|                                       p.position[2]); | ||||
|                   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()); | ||||
|             } | ||||
|           // 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()); | ||||
| 
 | ||||
|       // Initialize the internal input and event manager. The
 | ||||
|       // lower-level input system (OIS) is initialized by the
 | ||||
|       // setupOgre() call further up.
 | ||||
|       initializeInput(); | ||||
|           // End of interior cell
 | ||||
|         } | ||||
| 
 | ||||
|       // Start swangin'
 | ||||
|       // Run GUI system
 | ||||
|       startGUI(); | ||||
| 
 | ||||
|       // Play some old tunes
 | ||||
|       if(!noSound) | ||||
|         Music.play(); | ||||
| 
 | ||||
|  | @ -410,23 +403,6 @@ Try specifying another cell name on the command line, or edit openmw.ini."); | |||
|       writefln("%d creatures", cd.creatures.length); | ||||
|       writefln(); | ||||
|     } | ||||
|   /* | ||||
|   writefln("Races:"); | ||||
|   foreach(s; races.names) | ||||
|     { | ||||
|       writefln("%s:", s.id); | ||||
|       writefln("  strength: ", s.data.strength[0]); | ||||
|       writefln("  intelligence: ", s.data.intelligence[0]); | ||||
|       writefln("  willpower: ", s.data.willpower[0]); | ||||
|       writefln("  agility: ", s.data.agility[0]); | ||||
|       writefln("  speed: ", s.data.speed[0]); | ||||
|       writefln("  endurance: ", s.data.endurance[0]); | ||||
|       writefln("  personality: ", s.data.personality[0]); | ||||
|       writefln("  luck: ", s.data.luck[0]); | ||||
|       writefln("  height: ", s.data.height[0]); | ||||
|       writefln("  weight: ", s.data.weight[0]); | ||||
|     } | ||||
|   */ | ||||
| 
 | ||||
|   // This isn't necessary but it's here for testing purposes.
 | ||||
|   cellList.release(cd); | ||||
|  |  | |||
							
								
								
									
										116
									
								
								terrain/cpp_baseland.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								terrain/cpp_baseland.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,116 @@ | |||
| class BaseLand | ||||
| { | ||||
| public: | ||||
|   BaseLand(Ogre::SceneNode* s) | ||||
|     : mTerrainSceneNode(s) | ||||
|   { | ||||
|     createMaterial(); | ||||
|     createMesh(); | ||||
|   } | ||||
| 
 | ||||
|   ~BaseLand() | ||||
|   { | ||||
|     destroyMaterial(); | ||||
|     destroyMesh(); | ||||
|   } | ||||
| 
 | ||||
|   /**
 | ||||
|    * @brief repositions the mesh, and ensures that it is the right size | ||||
|    */ | ||||
|   void update() | ||||
|   { | ||||
|     Ogre::Real vd = mCamera->getFarClipDistance(); | ||||
|     if ( vd > mMeshDistance  ) { | ||||
|       destroyMaterial(); | ||||
|       destroyMesh(); | ||||
|       createMaterial(); | ||||
|       createMesh(); | ||||
|     } | ||||
| 
 | ||||
|     Ogre::Vector3 p = mCamera->getDerivedPosition(); | ||||
|     p.x -= ((int)p.x % 8192); | ||||
|     p.z -= ((int)p.z % 8192); | ||||
| 
 | ||||
|     float h = p.y + 2048; | ||||
|     h = pow(h/8192*2,2); | ||||
|     if ( h < 0 ) h = 0; | ||||
| 
 | ||||
|     mNode->setPosition(p.x, -32 - h, p.z); | ||||
|   } | ||||
| 
 | ||||
| private: | ||||
|   void createMesh() | ||||
|   { | ||||
|     mObject = mSceneMgr->createManualObject("BaseLand"); | ||||
|     mObject->begin("BaseLandMat", Ogre::RenderOperation::OT_TRIANGLE_LIST); | ||||
| 
 | ||||
|     Ogre::Real vd = mCamera->getFarClipDistance(); | ||||
|     vd += 8192 - ((int)vd % 8192); | ||||
| 
 | ||||
|     mMeshDistance = vd; | ||||
| 
 | ||||
| 
 | ||||
|     mObject->position(-vd,-2048, vd); | ||||
|     mObject->textureCoord(0, 1); | ||||
| 
 | ||||
|     mObject->position(vd,-2048, vd); | ||||
|     mObject->textureCoord(1, 1); | ||||
| 
 | ||||
|     mObject->position(vd,-2048, -vd); | ||||
|     mObject->textureCoord(1, 0); | ||||
| 
 | ||||
|     mObject->position(-vd,-2048, -vd); | ||||
|     mObject->textureCoord(0, 0); | ||||
| 
 | ||||
|     mObject->quad(0,1,2,3); | ||||
| 
 | ||||
|     mObject->end(); | ||||
| 
 | ||||
| 
 | ||||
|     mNode = mTerrainSceneNode->createChildSceneNode(); | ||||
|     mNode->attachObject(mObject); | ||||
|   } | ||||
| 
 | ||||
|   void destroyMesh() | ||||
|   { | ||||
|     mNode->detachAllObjects(); | ||||
|     mSceneMgr->destroyManualObject(mObject); | ||||
|     mNode->getParentSceneNode()->removeAndDestroyChild(mNode->getName()); | ||||
|   } | ||||
| 
 | ||||
|   void createMaterial() | ||||
|   { | ||||
|     float vd = mCamera->getFarClipDistance(); | ||||
|     vd += 8192 - ((int)vd % 8192); | ||||
|     vd = vd/8192 * 2; | ||||
| 
 | ||||
|     mMat = Ogre::MaterialManager::getSingleton(). | ||||
|       create(std::string("BaseLandMat"), | ||||
|              Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); | ||||
| 
 | ||||
|     Ogre::TextureUnitState* us = mMat->getTechnique(0)->getPass(0)->createTextureUnitState("_land_default.dds"); | ||||
|     us->setTextureScale(0.1f/vd,0.1f/vd); | ||||
| 
 | ||||
|     mMat->getTechnique(0)->getPass(0)->setDepthBias(-1); | ||||
|   } | ||||
| 
 | ||||
|   void destroyMaterial() | ||||
|   { | ||||
|     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; | ||||
| 
 | ||||
|   Ogre::SceneNode* mTerrainSceneNode; | ||||
| }; | ||||
							
								
								
									
										169
									
								
								terrain/cpp_esm.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										169
									
								
								terrain/cpp_esm.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,169 @@ | |||
| /*
 | ||||
|   Copyright (c) Jacob Essex 2009 | ||||
| 
 | ||||
|   This file is part of MWLand. | ||||
| 
 | ||||
|   MWLand is free software: you can redistribute it and/or modify it | ||||
|   under the terms of the GNU General Public License as published by | ||||
|   the Free Software Foundation, either version 3 of the License, or | ||||
|   (at your option) any later version. | ||||
| 
 | ||||
|   MWLand 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 | ||||
|   along with MWLand.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| 
 | ||||
| ///generic subrecord
 | ||||
| struct SubRecord | ||||
| { | ||||
|   SubRecord(){} | ||||
|   SubRecord(const std::string& n, const std::string& d) : subName(n), subData(d){} | ||||
|   std::string subName; | ||||
|   std::string subData; | ||||
| }; | ||||
| 
 | ||||
| ///generic record
 | ||||
| class Record { | ||||
| public: | ||||
|   Record(const std::string& type) | ||||
|     : mType(type) {} | ||||
| 
 | ||||
|   inline void addSubRecord(const std::string& id, | ||||
|                            const SubRecord& subRecord) | ||||
|   {mSubRecords[id] = subRecord; } | ||||
| 
 | ||||
|   std::string getSubRecordData(const std::string& recordName) | ||||
|   { | ||||
|     SubRecordMapItr itr = mSubRecords.find(recordName ); | ||||
|     if ( itr != mSubRecords.end() ) | ||||
|       return itr->second.subData; | ||||
|     return std::string(""); | ||||
|   } | ||||
| 
 | ||||
|   bool hasSubRecord(const std::string& recordName) | ||||
|   { | ||||
|     if ( mSubRecords.find(recordName ) != mSubRecords.end() ) | ||||
|       return true; | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   inline const std::string& getID() { return mId; } | ||||
|   inline void setID( const std::string& id) { mId = id;} | ||||
|   const std::string& getType(){return mType;} | ||||
| 
 | ||||
| private: | ||||
|   typedef std::map<std::string, SubRecord> SubRecordMap; | ||||
|   typedef SubRecordMap::iterator SubRecordMapItr; | ||||
|   SubRecordMap mSubRecords; | ||||
| 
 | ||||
|   std::string mType; | ||||
|   std::string mId; | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| typedef boost::shared_ptr<Record> RecordPtr; | ||||
| typedef std::list<RecordPtr> RecordList; | ||||
| typedef RecordList::iterator RecordListItr; | ||||
| typedef boost::shared_ptr<RecordList> RecordListPtr; | ||||
| 
 | ||||
| typedef std::map<std::string, RecordPtr> RecordMap; | ||||
| typedef RecordMap::iterator RecordMapItr; | ||||
| 
 | ||||
| ///top level class for loading and saving esp files.
 | ||||
| class ESM | ||||
| { | ||||
| private: | ||||
|   /// types of records to load
 | ||||
|   std::map<std::string, std::string> mLoadTypes; | ||||
| 
 | ||||
|   /// map<id, record> of the record
 | ||||
|   RecordMap mRecords; | ||||
| 
 | ||||
|   ///checks if the given type should be loaded
 | ||||
|   inline bool loadType(const std::string& t) | ||||
|   { | ||||
|     return ( mLoadTypes.find(t) != mLoadTypes.end() ); | ||||
|   } | ||||
| 
 | ||||
| public: | ||||
|   inline void addRecordType(const std::string& t, | ||||
|                             const std::string& i = "NAME") | ||||
|   { mLoadTypes[t] = i;  } | ||||
| 
 | ||||
|   bool loadFile(const std::string& file) | ||||
|   { | ||||
|     std::ifstream esp (file.c_str(), std::ios::in | std::ios::binary); | ||||
| 
 | ||||
|     if ( !esp.is_open() ) return false; //check open
 | ||||
| 
 | ||||
|     esp.seekg(4); | ||||
| 
 | ||||
|     long hdrSize; //get offset for start of data
 | ||||
|     esp.read ((char *)&hdrSize, sizeof(long)); | ||||
| 
 | ||||
|     //get num records
 | ||||
|     esp.seekg(16 + 8 + 296); | ||||
|     long numRecords; | ||||
|     esp.read ((char *)&numRecords, sizeof(long)); | ||||
| 
 | ||||
|     esp.seekg(hdrSize + 16); //go to end of header
 | ||||
| 
 | ||||
|     for ( long i = 0; i < numRecords; i++ ){ | ||||
| 
 | ||||
|       char type[5]; | ||||
|       esp.get(type, 5); | ||||
|       type[4] = '\0'; | ||||
| 
 | ||||
|       long recordSize; | ||||
|       esp.read ((char *)&recordSize, 4); | ||||
|       esp.seekg(8, std::ofstream::cur); | ||||
|       long endPos = recordSize +  esp.tellg(); | ||||
| 
 | ||||
|       if ( loadType(type) ) { | ||||
|         RecordPtr record = RecordPtr(new Record(type)); | ||||
| 
 | ||||
|         //load all subrecords
 | ||||
|         while ( esp.tellg() < endPos ) { | ||||
|           char subType[5]; | ||||
|           esp.get(subType, 5); | ||||
| 
 | ||||
|           long subRecLength; | ||||
|           esp.read ((char *)&subRecLength, 4); | ||||
| 
 | ||||
|           long subRecEnd = subRecLength + esp.tellg(); | ||||
|           char* subRecData = new char[subRecLength]; | ||||
|           esp.read(subRecData, subRecLength); | ||||
| 
 | ||||
|           record->addSubRecord(subType, SubRecord(subType,std::string(subRecData, subRecLength))); | ||||
|           delete [] subRecData; | ||||
| 
 | ||||
|           assert(subRecEnd==esp.tellg()); | ||||
|         } | ||||
|         record->setID(record->getSubRecordData(mLoadTypes[type])); | ||||
|         mRecords[record->getSubRecordData(mLoadTypes[type])] = record; | ||||
|       }else{ | ||||
|         esp.seekg(endPos); | ||||
|       } | ||||
|       assert(endPos==esp.tellg()); | ||||
| 
 | ||||
|     } | ||||
|     esp.close(); | ||||
| 
 | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
|   inline RecordPtr getRecord(const std::string& id){ return mRecords[id]; } | ||||
| 
 | ||||
|   RecordListPtr getRecordsByType(const std::string& t) | ||||
|   { | ||||
|     RecordListPtr r = RecordListPtr(new RecordList); //need pointer....
 | ||||
|     for ( RecordMapItr iter = mRecords.begin(); iter != mRecords.end(); ++iter) | ||||
|       if ( t == iter->second->getType() ) | ||||
|         r->push_back(iter->second); | ||||
|     return r; | ||||
|   } | ||||
| }; | ||||
							
								
								
									
										58
									
								
								terrain/cpp_framelistener.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								terrain/cpp_framelistener.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,58 @@ | |||
| class TerrainFrameListener : public FrameListener | ||||
| { | ||||
| protected: | ||||
|   Terrain* mTerrain; | ||||
|   MWHeightmap* mHeights; | ||||
| 
 | ||||
|   /**
 | ||||
|    * Updates the quad tree | ||||
|    */ | ||||
|   bool frameEnded(const FrameEvent& evt) | ||||
|   { | ||||
|     mTerrain->update(evt.timeSinceLastFrame); | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
| public: | ||||
|   void setup() | ||||
|   { | ||||
|     // Add the frame listener
 | ||||
|     mRoot->addFrameListener(this); | ||||
| 
 | ||||
|     //our derived heightmap
 | ||||
|     mHeights = new MWHeightmap(); | ||||
|     mHeights->load(TERRAIN_OUTPUT); | ||||
| 
 | ||||
|     //setup terrain
 | ||||
|     mTerrain = new Terrain( mHeights, //heightmap
 | ||||
|                             mSceneMgr->getRootSceneNode()->createChildSceneNode("TERRAIN_ROOT"));  //root scene node
 | ||||
| 
 | ||||
|     //fix settings
 | ||||
|     mTerrain->setMorphingEnabled(false); | ||||
|     mTerrain->setTextureFadingEnabled(false); | ||||
| 
 | ||||
|     //create the quad node
 | ||||
|     mTerrain->create(); | ||||
|   } | ||||
| 
 | ||||
|   /* KILLME
 | ||||
|   void drawLine(std::string name, Ogre::Vector3 start, Ogre::Vector3 end) | ||||
|   { | ||||
|     Ogre::ManualObject* mo =  mSceneMgr->createManualObject( name); | ||||
|     Ogre::SceneNode* node = mSceneMgr->getRootSceneNode()->createChildSceneNode( name+"node"); | ||||
| 
 | ||||
|     Ogre::MaterialPtr mat = Ogre::MaterialManager::getSingleton().create( name+"Material","debugger"); | ||||
|     mat->setReceiveShadows(false); | ||||
|     mat->getTechnique(0)->setLightingEnabled(true); | ||||
|     mat->getTechnique(0)->getPass(0)->setDiffuse(0,0,1,0); | ||||
|     mat->getTechnique(0)->getPass(0)->setAmbient(0,0,1); | ||||
|     mat->getTechnique(0)->getPass(0)->setSelfIllumination(0,0,1); | ||||
| 
 | ||||
|     mo->begin(name+"Material", Ogre::RenderOperation::OT_LINE_LIST); | ||||
|     mo->position(start); | ||||
|     mo->position(end); | ||||
|     mo->end(); | ||||
|     node->attachObject(mo); | ||||
|   } | ||||
|   */ | ||||
| }; | ||||
							
								
								
									
										441
									
								
								terrain/cpp_generator.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										441
									
								
								terrain/cpp_generator.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,441 @@ | |||
| /*
 | ||||
|   OpenMW - The completely unofficial reimplementation of Morrowind | ||||
|   Copyright (C) 2009  Jacob Essex | ||||
|   WWW: http://openmw.sourceforge.net/
 | ||||
| 
 | ||||
|   This file (cpp_generator.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/ .
 | ||||
| 
 | ||||
| */ | ||||
| 
 | ||||
| class Generator | ||||
| { | ||||
| public: | ||||
|   Generator(const std::string& baseFileName) : | ||||
|     mBaseFileName(baseFileName) | ||||
|   { | ||||
|     mDataO.open(std::string(baseFileName+".data").c_str(), std::ios::binary); | ||||
|   } | ||||
| 
 | ||||
|   inline void addLandData(RecordPtr record, const std::string& source) | ||||
|   { mMWLand.addLandData(record, source); } | ||||
| 
 | ||||
|   inline void addLandTextureData(RecordPtr record, const std::string& source) | ||||
|   { mMWLand.addLandTextureData(record, source); } | ||||
| 
 | ||||
|   void beginGeneration() | ||||
|   { | ||||
|     //maxiumum distance form 0, 0 in any direction
 | ||||
|     int max = 0; | ||||
|     max = std::max<int>(mMWLand.getMaxX(), max); | ||||
|     max = std::max<int>(mMWLand.getMaxY(), max); | ||||
|     max = std::max<int>(-mMWLand.getMinX(), max); | ||||
|     max = std::max<int>(-mMWLand.getMinY(), max); | ||||
| 
 | ||||
|     //round up to nearest binary number. makes some stuff easier iirc
 | ||||
|     //FIXME
 | ||||
|     for ( int i = 1;;i++ ) { | ||||
|       if ( max < pow((float)2, i) ) | ||||
|         { | ||||
|           max = pow((float)2, i); | ||||
|           break; | ||||
|         } | ||||
|       assert(i<=8); //don't go too high. Used for debug
 | ||||
|     } | ||||
| 
 | ||||
|     int maxDepth = 0; //temp var used below
 | ||||
|     // FIXME: make 8192 a constant
 | ||||
|     mIndex.setRootSideLength(max*2*8192); //given that 8192 is the length of a cell
 | ||||
| 
 | ||||
|     //Keep deviding the root side length by 2 (thereby simulating a
 | ||||
|     //split) until we reach the width of the base cell (or the
 | ||||
|     //smallest quad)
 | ||||
|     for (long i = mIndex.getRootSideLength(); i > 8192; i/=2 ) | ||||
|       maxDepth++; | ||||
|     mIndex.setMaxDepth(maxDepth); | ||||
|   } | ||||
| 
 | ||||
|   void generateLODLevel(int level, bool createTexture, int textureSize) | ||||
|   { | ||||
|     std::cout << "Generating Level " << level << "\n"; | ||||
| 
 | ||||
|     assert(level <= mIndex.getMaxDepth()); | ||||
|     assert(level > 0 ); | ||||
|     assert(textureSize>2); | ||||
|     assert(textureSize<=4096); //change to gpu max if pos
 | ||||
| 
 | ||||
|     const int initialLevel = level; | ||||
| 
 | ||||
|     // FIXME: Should probably use another name than 'level' here
 | ||||
|     level = pow((float)2, level); //gap between verts that we want
 | ||||
|     const int halfLevel = level/2; | ||||
|     assert(halfLevel > 0 ); | ||||
| 
 | ||||
|     // FIXME. Just search for all pow() calls, really
 | ||||
|     int cellDist = pow((float)2, mIndex.getMaxDepth()); | ||||
| 
 | ||||
|     // Temporary storage
 | ||||
|     MWQuadData qd(0); | ||||
|     qd.setVertexSeperation(128*halfLevel); //dist between two verts
 | ||||
| 
 | ||||
|     std::vector<float>& gh = qd.getHeightsRef(); //ref to the data storage in the quad
 | ||||
|     std::vector<char>& gn  = qd.getNormalsRef(); | ||||
|     gh.resize(LAND_NUM_VERTS); //allocate memory for mesh functions
 | ||||
|     gn.resize(LAND_NUM_VERTS*3); | ||||
| 
 | ||||
|     //the 16*16 array used for holding the LTEX records (what texure is splatted where)
 | ||||
|     std::vector<int>& gl = qd.getTextureIndexRef(); | ||||
|     gl.resize((LAND_LTEX_WIDTH+2)*(LAND_LTEX_WIDTH+2)); | ||||
| 
 | ||||
|     const std::string stringLevel(Ogre::StringConverter::toString(level)); //cache this
 | ||||
|     const std::string defaultTexture(stringLevel + "_default.png"); | ||||
|     bool hasUsedDefault = false; | ||||
| 
 | ||||
|     // loops over the start of each quad we want to get
 | ||||
|     for(   int y = -(cellDist/2); y < (cellDist/2); y+=halfLevel ) | ||||
|       for( int x = -(cellDist/2); x < (cellDist/2); x+=halfLevel ) | ||||
|         { | ||||
| 
 | ||||
|           qd.setParentTexture(""); | ||||
|           bool usingDefaultTexture = false; | ||||
| 
 | ||||
|           if ( initialLevel == 1 ) | ||||
|             generateLTEXData(x, y, gl); | ||||
|           else if ( createTexture ) | ||||
|             { | ||||
|               //this is the name of the file that OGRE will
 | ||||
|               //look for
 | ||||
|               std::string name =	stringLevel + "_" + | ||||
|                 Ogre::StringConverter::toString(x)  + "_" + | ||||
|                 Ogre::StringConverter::toString(y) + ".png"; | ||||
| 
 | ||||
|               //where as the arg1 here is the file name to save it.
 | ||||
|               bool hasGen = generateTexture(std::string(TEXTURE_OUTPUT) + name, textureSize, x, y, halfLevel); | ||||
| 
 | ||||
|               if ( hasGen ) qd.setTexture(name); | ||||
|               else | ||||
|                 { | ||||
|                   qd.setTexture(defaultTexture); | ||||
|                   hasUsedDefault = true; | ||||
|                   usingDefaultTexture = true; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|           //calculate parent texture
 | ||||
|           if ( initialLevel != mIndex.getMaxDepth() ) | ||||
|             { | ||||
|               //calcualte the level one higher
 | ||||
|               int higherLevel = pow((float)2, (initialLevel+1)); | ||||
|               int highHalfLevel = higherLevel/2; | ||||
| 
 | ||||
|               int higherX = x; | ||||
|               if ( (higherX-halfLevel) % highHalfLevel == 0 ) | ||||
|                 higherX -= halfLevel; | ||||
| 
 | ||||
| 
 | ||||
|               int higherY = y; | ||||
|               if ( (higherY-halfLevel) % highHalfLevel  == 0 ) | ||||
|                 higherY -= halfLevel; | ||||
| 
 | ||||
|               std::string higherName = Ogre::StringConverter::toString(higherLevel) + "_" + | ||||
|                 Ogre::StringConverter::toString(higherX)  + "_" + | ||||
|                 Ogre::StringConverter::toString(higherY) + ".png"; | ||||
| 
 | ||||
|               //check file exists without needing boost filesystenm libs
 | ||||
|               FILE* fp = fopen((std::string(TEXTURE_OUTPUT) + higherName).c_str(), "r"); | ||||
|               if ( fp ) | ||||
|                 { | ||||
|                   qd.setParentTexture(higherName); | ||||
|                   fclose(fp); | ||||
|                 } | ||||
|               else | ||||
|                 qd.setParentTexture(""); | ||||
|             } | ||||
|           generateMesh(gh, gn, x, y, halfLevel ); | ||||
| 
 | ||||
|           bool isEmptyQuad = true; | ||||
|           if ( usingDefaultTexture ) | ||||
|             { | ||||
|               for ( int i = 0; i < LAND_NUM_VERTS; i++ ){ | ||||
|                 if ( gh.at(i) != LAND_DEFAULT_HEIGHT ){ | ||||
|                   isEmptyQuad = false; | ||||
|                   break; | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|           else isEmptyQuad = false; | ||||
| 
 | ||||
|           if ( isEmptyQuad ) | ||||
|             continue; | ||||
| 
 | ||||
|           //save data
 | ||||
|           //the data is the position of the generated quad
 | ||||
|           mIndex.setOffset(x*8192+halfLevel*8192/2, | ||||
|                            y*8192+halfLevel*8192/2, | ||||
|                            mDataO.tellp()); | ||||
|           boost::archive::binary_oarchive oa(mDataO); //using boost fs to write the quad
 | ||||
|           oa << qd; | ||||
|         } | ||||
| 
 | ||||
|     //check if we have used a default texture
 | ||||
|     if ( hasUsedDefault ) | ||||
|       { | ||||
|         std::vector<int> ltex; | ||||
|         ltex.resize(halfLevel*LAND_LTEX_WIDTH*halfLevel*LAND_LTEX_WIDTH, mPalette.getOrAddIndex("_land_default.dds")); | ||||
|         renderTexture(std::string(TEXTURE_OUTPUT) + defaultTexture, ltex, textureSize, halfLevel*LAND_LTEX_WIDTH); | ||||
|       } | ||||
|   } | ||||
| 
 | ||||
|   void endGeneration() | ||||
|   { | ||||
|     // FIXME: Just write one file?
 | ||||
|     std::ofstream ofi(std::string(mBaseFileName + ".index").c_str(), std::ios::binary); | ||||
|     std::ofstream ofp(std::string(mBaseFileName + ".palette").c_str(), std::ios::binary); | ||||
|     boost::archive::binary_oarchive oai(ofi); | ||||
|     boost::archive::binary_oarchive oap(ofp); | ||||
|     oai << mIndex; | ||||
|     oap << mPalette; | ||||
|   } | ||||
| 
 | ||||
| private: | ||||
| 
 | ||||
|   void generateLTEXData(int x, int y, std::vector<int>& index) | ||||
|   { | ||||
|     for ( int texY = 0; texY < LAND_LTEX_WIDTH+2; texY++ ) | ||||
|       for ( int texX = 0; texX < LAND_LTEX_WIDTH+2; texX++ ) | ||||
|         { | ||||
|           int tempX = x; | ||||
|           int tempY = y; | ||||
| 
 | ||||
|           int sourceX = texX - 1; | ||||
|           int sourceY = texY - 1; | ||||
| 
 | ||||
|           if ( sourceX == -1 ) | ||||
|             { | ||||
|               tempX--; | ||||
|               sourceX = LAND_LTEX_WIDTH-1; | ||||
|             } | ||||
|           else if ( sourceX == LAND_LTEX_WIDTH) | ||||
|             { | ||||
|               tempX++; | ||||
|               sourceX = 0; | ||||
|             } | ||||
| 
 | ||||
|           if ( sourceY == -1 ) | ||||
|             { | ||||
|               tempY--; | ||||
|               sourceY = LAND_LTEX_WIDTH-1; | ||||
|             } | ||||
|           else if ( sourceY == LAND_LTEX_WIDTH ) | ||||
|             { | ||||
|               tempY++; | ||||
|               sourceY = 0; | ||||
|             } | ||||
| 
 | ||||
|           std::string source; | ||||
|           short texID = 0; | ||||
| 
 | ||||
|           if ( mMWLand.hasData(tempX, tempY) ) | ||||
|             { | ||||
|               source = mMWLand.getSource(tempX, tempY); | ||||
|               texID = mMWLand.getLTEXIndex(tempX,tempY, sourceX, sourceY); | ||||
|             } | ||||
| 
 | ||||
|           std::string texturePath = "_land_default.dds"; | ||||
|           if ( texID != 0 && mMWLand.hasLTEXRecord(source,--texID) ) | ||||
|             texturePath = mMWLand.getLTEXRecord(source,texID); | ||||
| 
 | ||||
|           // textures given as tga, when they are a dds. I hate that "feature"
 | ||||
|           // FIXME: do we handle this already?
 | ||||
|           size_t d = texturePath.find_last_of(".") + 1; | ||||
|           texturePath = texturePath.substr(0, d) + "dds"; | ||||
|           std::transform(texturePath.begin(), texturePath.end(), texturePath.begin(), tolower); | ||||
| 
 | ||||
|           index[texY*(LAND_LTEX_WIDTH+2)+texX] = mPalette.getOrAddIndex(texturePath); | ||||
|         } | ||||
|   } | ||||
| 
 | ||||
|   bool generateTexture(const std::string& name, int size, | ||||
|                        int blockX, int blockY, int halfLevel) | ||||
|   { | ||||
|     int width = size/(halfLevel*LAND_LTEX_WIDTH); | ||||
|     assert(width>=1); | ||||
| 
 | ||||
|     std::vector<int> ltex; //prealloc, as we know exactly what size it needs to be
 | ||||
|     ltex.resize(halfLevel*LAND_LTEX_WIDTH*halfLevel*LAND_LTEX_WIDTH, 0); | ||||
| 
 | ||||
|     //for each cell
 | ||||
|     for ( int y = 0; y < halfLevel; y++ ) | ||||
|       for ( int x = 0; x < halfLevel; x++ ) | ||||
|         //for each texture in the cell
 | ||||
|         for ( int texY = 0; texY < LAND_LTEX_WIDTH; texY++ ) | ||||
|           for ( int texX = 0; texX < LAND_LTEX_WIDTH; texX++ ) | ||||
|             { | ||||
|               std::string source; | ||||
|               short texID = 0; | ||||
| 
 | ||||
|               if ( mMWLand.hasData(x + blockX, y + blockY) ) | ||||
|                 { | ||||
|                   source = mMWLand.getSource(x + blockX, y + blockY); | ||||
|                   texID = mMWLand.getLTEXIndex(x + blockX, y + blockY, texX, texY); | ||||
|                 } | ||||
| 
 | ||||
|               std::string texturePath = "_land_default.dds"; | ||||
|               if ( texID != 0 && mMWLand.hasLTEXRecord(source,--texID) ) | ||||
|                 texturePath = mMWLand.getLTEXRecord(source,texID); | ||||
| 
 | ||||
|               //textures given as tga, when they are a dds. I hate that "feature"
 | ||||
|               //FIXME: ditto earlier comment
 | ||||
|               size_t d = texturePath.find_last_of(".") + 1; | ||||
|               texturePath = texturePath.substr(0, d) + "dds"; | ||||
|               //FIXME: BTW, didn't we make the BSA reader case insensitive?
 | ||||
|               std::transform(texturePath.begin(), texturePath.end(), texturePath.begin(), tolower); | ||||
|               const int index = (y*LAND_LTEX_WIDTH+texY)*halfLevel*LAND_LTEX_WIDTH + x*LAND_LTEX_WIDTH+texX; | ||||
|               ltex[index] = mPalette.getOrAddIndex(texturePath); | ||||
|             } | ||||
| 
 | ||||
|     //see if we have used anything at all
 | ||||
|     // FIXME: Now, I KNOW this isn't needed :)
 | ||||
|     int sum = 0; | ||||
|     for ( std::vector<int>::iterator i = ltex.begin(); i != ltex.end(); ++i ) | ||||
|       sum += *i; | ||||
| 
 | ||||
|     if ( sum == 0 ) //not used any textures
 | ||||
|       return false; | ||||
| 
 | ||||
|     renderTexture(name, ltex, size, halfLevel*LAND_LTEX_WIDTH); | ||||
| 
 | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
|   // FIXME: renderTexture and getRenderedTexture don't strike me as
 | ||||
|   // the optimal ways of doing this. For one it's hardware/driver
 | ||||
|   // dependent, when it should be a simple computational exercise. But
 | ||||
|   // understand what it actually does, before you change anything.
 | ||||
| 
 | ||||
|   void renderTexture(const std::string& outputName, std::vector<int>& ltex, | ||||
|                      int texSize, int alphaSize) | ||||
|   { | ||||
|     std::cout << "  Creating " << outputName << "\n"; | ||||
| 
 | ||||
|     assert(Ogre::Math::Sqrt(ltex.size())==alphaSize); | ||||
|     std::list<Ogre::ResourcePtr> createdResources; | ||||
| 
 | ||||
|     MaterialGenerator mg; | ||||
|     mg.setTexturePaths(mPalette.getPalette()); | ||||
| 
 | ||||
|     const int scaleDiv = alphaSize/LAND_LTEX_WIDTH; | ||||
| 
 | ||||
|     //genetate material/aplahas
 | ||||
|     Ogre::MaterialPtr mp = mg.getAlphaMat("Rtt_Alpha1", ltex, alphaSize, 0, scaleDiv,createdResources); | ||||
|     Ogre::TexturePtr tex1 = getRenderedTexture(mp, "RTT_TEX_1",texSize, Ogre::PF_R8G8B8); | ||||
|     tex1->getBuffer()->getRenderTarget()->writeContentsToFile(outputName); | ||||
|     Ogre::MaterialManager::getSingleton().remove(mp->getHandle()); | ||||
| 
 | ||||
|     //remove the texture we have just written to the fs
 | ||||
|     Ogre::TextureManager::getSingleton().remove(tex1->getHandle()); | ||||
| 
 | ||||
|     //remove all the materials
 | ||||
|     const std::list<Ogre::ResourcePtr>::const_iterator iend = createdResources.end(); | ||||
|     for ( std::list<Ogre::ResourcePtr>::const_iterator itr = createdResources.begin(); | ||||
|           itr != iend; | ||||
|           ++itr) { | ||||
|       (*itr)->getCreator()->remove((*itr)->getHandle()); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   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); | ||||
| 
 | ||||
|     //output pass
 | ||||
|     Ogre::CompositionTargetPass* ctp = cp->createTechnique()->getOutputTargetPass(); | ||||
|     Ogre::CompositionPass* cpass = ctp->createPass(); | ||||
|     cpass->setType(Ogre::CompositionPass::PT_RENDERQUAD); | ||||
|     cpass->setMaterial(mp); | ||||
| 
 | ||||
|     //create a texture to write the texture to...
 | ||||
|     Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton(). | ||||
|       createManual( | ||||
|                    name, | ||||
|                    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(); | ||||
| 
 | ||||
|     //required for some reason?
 | ||||
|     //Not implementing resulted in a black tex using openGL, Linux, and a nvidia 6150 in 1.4.?
 | ||||
|     Ogre::Root::getSingleton().renderOneFrame(); | ||||
| 
 | ||||
|     Ogre::CompositorManager::getSingleton().removeCompositor(vp, "Rtt_Comp"); | ||||
|     Ogre::CompositorManager::getSingleton().remove(cp->getHandle()); | ||||
| 
 | ||||
|     renderTexture->removeAllViewports(); //needed?
 | ||||
| 
 | ||||
|     return texture; | ||||
|   } | ||||
| 
 | ||||
|   void generateMesh(std::vector<float>& gh, std::vector<char>& gn, int blockX, | ||||
|                     int blockY, int halfLevel) | ||||
|   { | ||||
|     int gnc = 0; | ||||
|     int ghc = 0; | ||||
|     for ( int y = 0; y < LAND_VERT_WIDTH; y++ ) | ||||
|       for ( int x = 0; x < LAND_VERT_WIDTH; x++ ) | ||||
|         { | ||||
|           //FIXME: Eh, what?
 | ||||
|           int cellY = floor((float)y/LAND_VERT_WIDTH*halfLevel) + blockY; | ||||
|           int cellX = floor((float)x/LAND_VERT_WIDTH*halfLevel) + blockX; | ||||
| 
 | ||||
|           std::vector<float>& ch = mMWLand.getHeights(cellX,cellY); | ||||
|           std::vector<char>& cn =  mMWLand.getNormals(cellX,cellY); | ||||
| 
 | ||||
|           int vertY = (((float)y/LAND_VERT_WIDTH*halfLevel) - | ||||
|                        (float)floor((float)y/LAND_VERT_WIDTH*halfLevel)) * LAND_VERT_WIDTH; | ||||
|           int vertX = (((float)x/LAND_VERT_WIDTH*halfLevel) - | ||||
|                        (float)floor((float)x/LAND_VERT_WIDTH*halfLevel)) * LAND_VERT_WIDTH; | ||||
| 
 | ||||
|           assert(vertY < LAND_VERT_WIDTH && vertX < LAND_VERT_WIDTH); | ||||
| 
 | ||||
|           //store data
 | ||||
|           gh[ghc++] = ch[vertY*LAND_VERT_WIDTH+vertX]; | ||||
| 
 | ||||
|           for ( int z = 0; z < 3; z++ ) | ||||
|             gn[gnc++] = cn[(vertY*LAND_VERT_WIDTH+vertX)*3+z]; | ||||
|         } | ||||
|   } | ||||
| 
 | ||||
|   std::string mBaseFileName; ///base file name so we gen mBaseFileName + ".index" etc
 | ||||
|   std::ofstream mDataO; | ||||
| 
 | ||||
|   Index mIndex; ///the index of the data. holds offsets and max depth, rsl etc
 | ||||
|   TexturePalette mPalette; ///all the textures from all mods are merge into a single palette
 | ||||
|   MWLand mMWLand; ///deals with MW land stuff
 | ||||
| }; | ||||
							
								
								
									
										91
									
								
								terrain/cpp_index.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								terrain/cpp_index.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,91 @@ | |||
| /**
 | ||||
| * @brief holds data about positions of data and general header info | ||||
| */ | ||||
| class Index { | ||||
| public: | ||||
|     ///saves my fingers :P
 | ||||
|     typedef std::map<long, std::map<long, long> >::iterator OffsetItr; | ||||
|     typedef std::map<long, std::map<long, long> >::const_iterator OffsetConstItr; | ||||
| 
 | ||||
|     /**
 | ||||
|     * @brief sets the root quads side length in gu | ||||
|     * @param l the side length | ||||
|     * | ||||
|     * This is used for working out the locations of quad children. | ||||
|     * I am assuming a long is enough... | ||||
|     */ | ||||
|     inline void setRootSideLength(long l) { | ||||
|         mRootSideLength = l; | ||||
|     } | ||||
|     /**
 | ||||
|     * @return the side length of the root quad. | ||||
|     */ | ||||
|     inline long getRootSideLength() const { | ||||
|         return mRootSideLength; | ||||
|     } | ||||
| 
 | ||||
|     inline void setMaxDepth(int d) { | ||||
|         mMaxDepth = d; | ||||
|     } | ||||
|     inline int getMaxDepth() const { | ||||
|         return mMaxDepth; | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|     * @return -1 is returned if there is no offset | ||||
|     * @param x, y the position of the quad in gu | ||||
|     * | ||||
|     * Slightly faster using hasOffset to check if it exists | ||||
|     * Shouldn't be noticable diffrence. | ||||
|     */ | ||||
|     inline long getOffset(long x, long y) const { //inline?
 | ||||
|         OffsetConstItr itr1 = mQuadOffsets.find(x); | ||||
|         if ( itr1 == mQuadOffsets.end() ) return -1; | ||||
|         std::map<long, long>::const_iterator itr2 = itr1->second.find(y); | ||||
|         if ( itr2 == itr1->second.end() ) return -1; | ||||
|         return itr2->second; | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|     * @brief checks if a quad for the given position exists | ||||
|     * @return true/false | ||||
|     * @param x, y the position of the quad in gu | ||||
|     * | ||||
|     * @todo Would it be worth merging this with getOffset? | ||||
|     */ | ||||
|     inline bool hasOffset(long x, long y) const { | ||||
|         OffsetConstItr itr = mQuadOffsets.find(x); | ||||
|         if ( itr == mQuadOffsets.end() ) return false; | ||||
|         return (itr->second.find(y) != itr->second.end()); | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|     * @brief sets an offset of a quad | ||||
|     * @param x, y the position in gu of the quad | ||||
|     * @param o the offset within the file of the records for this quad | ||||
|     */ | ||||
|     inline void setOffset(long x, long y, long o) { | ||||
|         mQuadOffsets[x][y] = o; | ||||
|     } | ||||
| 
 | ||||
| protected: | ||||
|     std::map<long, std::map<long, long> > mQuadOffsets; | ||||
|     long mRootSideLength; ///length in gu of the root quad
 | ||||
|     int mMaxDepth; ///maximum depth assuming root quad depth = 0
 | ||||
| 
 | ||||
|     friend class boost::serialization::access; | ||||
|     /**
 | ||||
|     * Saves the data for the max depth, the root side legnth, and the quad offsets | ||||
|     */ | ||||
|     template<class Archive> | ||||
|     inline void serialize(Archive& ar, const unsigned int version){ | ||||
| 
 | ||||
|     ar &mMaxDepth; | ||||
|     ar &mRootSideLength; | ||||
|     ar &mQuadOffsets; | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| BOOST_CLASS_TRACKING(Index, boost::serialization::track_never); | ||||
							
								
								
									
										209
									
								
								terrain/cpp_landdata.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										209
									
								
								terrain/cpp_landdata.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,209 @@ | |||
| class MWLand | ||||
| { | ||||
| public: | ||||
|   MWLand() | ||||
|   { | ||||
|     mMaxX = mMaxY = mMinX = mMinY = 0; | ||||
|   } | ||||
| 
 | ||||
|   void addLandTextureData(RecordPtr record, const std::string& source) | ||||
|   { | ||||
|     LandTexture l; | ||||
|     l.name = record->getSubRecordData("NAME"); | ||||
|     l.data = record->getSubRecordData("DATA"); | ||||
|     l.intv = *((short*) record->getSubRecordData("INTV").c_str()); | ||||
|     mLTEXRecords[source][l.intv] = l; | ||||
|   } | ||||
| 
 | ||||
|   void addLandData(RecordPtr record, const std::string& source) | ||||
|   { | ||||
|     if ( !record->hasSubRecord("VHGT") || !record->hasSubRecord("VTEX") ) //ensure all records exist
 | ||||
|       return; | ||||
| 
 | ||||
|     //copy these, else we end up with invliad data
 | ||||
|     LAND::INTV intv = *(LAND::INTV*)record->getSubRecordData("INTV").c_str(); | ||||
|     LAND::VHGT vhgt = *(LAND::VHGT*)record->getSubRecordData("VHGT").c_str(); | ||||
|     LAND::VNML vnml = *(LAND::VNML*)record->getSubRecordData("VNML").c_str(); | ||||
|     LAND::VTEX vtex = *(LAND::VTEX*)record->getSubRecordData("VTEX").c_str(); | ||||
| 
 | ||||
|     //GridPosition gp(intv.x, intv.y);
 | ||||
|     mLandRecords[intv.x][intv.y].heights = parseHeights(&vhgt); //convert into a format we want
 | ||||
|     mLandRecords[intv.x][intv.y].normals = parseNormals(&vnml); | ||||
|     mLandRecords[intv.x][intv.y].textures = parseTextures(&vtex); | ||||
|     mLandRecords[intv.x][intv.y].source = source; | ||||
| 
 | ||||
|     mMaxX = std::max<int>(mMaxX, intv.x); | ||||
|     mMaxY = std::max<int>(mMaxY, intv.y); | ||||
|     mMinX = std::min<int>(mMinX, intv.x); | ||||
|     mMinY = std::min<int>(mMinY, intv.y); | ||||
|   } | ||||
| 
 | ||||
|   ///Maximum distance of a cell on the X plane from grid pos 0 in the positive direction
 | ||||
|   inline int getMaxX() const { return mMaxX; } | ||||
|   ///Maximum distance of a cell on the Y plane from grid pos 0 in the positvie direction
 | ||||
|   inline int getMaxY() const { return mMaxY; } | ||||
|   ///Maximum distance of a cell on the X plane from grid pos 0 in the negative direction
 | ||||
|   inline int getMinX() const { return mMinX; } | ||||
|   ///see others
 | ||||
|   inline int getMinY() const { return mMinY; } | ||||
| 
 | ||||
|   inline std::vector<float>& getHeights(int x, int y) | ||||
|   { | ||||
|     if ( hasData(x,y) ) | ||||
|       return mLandRecords[x][y].heights; | ||||
|     static std::vector<float> e(LAND_NUM_VERTS, LAND_DEFAULT_HEIGHT); | ||||
|     return e; | ||||
|   } | ||||
| 
 | ||||
|   inline std::vector<char>& getNormals(int x, int y) | ||||
|   { | ||||
|     if ( hasData(x,y) ) | ||||
|       return mLandRecords[x][y].normals; | ||||
|     static std::vector<char> e(LAND_NUM_VERTS*3,0); | ||||
|     return e; | ||||
|   } | ||||
| 
 | ||||
|   inline const std::string& getSource(int x, int y) | ||||
|   { | ||||
|     assert(hasData(x,y)); | ||||
|     return mLandRecords[x][y].source; | ||||
|   } | ||||
| 
 | ||||
|   inline bool hasData(int x, int y) const | ||||
|   { | ||||
|     std::map<int , std::map<int, LandData> >::const_iterator itr = mLandRecords.find(x); | ||||
|     if ( itr == mLandRecords.end() ) | ||||
|       return false; | ||||
|     if ( itr->second.find(y) == itr->second.end() ) | ||||
|       return false; | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
|   inline std::string& getLTEXRecord(const std::string& source, short i) | ||||
|   { | ||||
|     return mLTEXRecords[source][i].data; | ||||
|   } | ||||
| 
 | ||||
|   inline bool hasLTEXRecord(const std::string& source, short index) const | ||||
|   { | ||||
|     std::map<std::string, std::map<short, LandTexture> >::const_iterator itr = mLTEXRecords.find(source); | ||||
|     if ( itr == mLTEXRecords.end() ) | ||||
|       return false; | ||||
|     if ( itr->second.find(index) == itr->second.end() ) | ||||
|       return false; | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
|   inline short getLTEXIndex(int x, int y, int pos) | ||||
|   { | ||||
|     assert(hasData(x,y)); | ||||
|     return mLandRecords[x][y].textures[pos]; | ||||
|   } | ||||
| 
 | ||||
|   inline short getLTEXIndex(int x1, int y1, int x2, int y2) | ||||
|   { | ||||
|     return getLTEXIndex(x1, y1, y2*LAND_LTEX_WIDTH+x2); | ||||
|   } | ||||
| 
 | ||||
| private: | ||||
| 
 | ||||
|   ///the min/max size of cells
 | ||||
|   int mMaxY, mMinY; | ||||
|   int mMaxX, mMinX; | ||||
| 
 | ||||
|   // Land structure as held in the ESM
 | ||||
|   struct LAND { | ||||
|     struct INTV { /// x, y grid pos of the cell
 | ||||
|       long x; | ||||
|       long y; | ||||
|     }; | ||||
|     struct VNML { ///vertex normal data
 | ||||
|       struct NORMAL { | ||||
|         char x; | ||||
|         char y; | ||||
|         char z; | ||||
|       }; | ||||
|       NORMAL normals[LAND_NUM_VERTS]; | ||||
|     }; | ||||
|     struct VHGT { ///height data
 | ||||
|       float heightOffset; | ||||
|       char  heightData[LAND_NUM_VERTS]; | ||||
|       short unknown1; | ||||
|       char unknown2; | ||||
|     }; | ||||
|     struct VTEX { ///splat texture data
 | ||||
|       short index[LAND_NUM_LTEX]; | ||||
|     }; | ||||
|     INTV* intv; | ||||
|     VNML* vnml; | ||||
|     VHGT* vhgt; | ||||
|     VTEX* vtex; | ||||
|   }; | ||||
| 
 | ||||
|   std::vector<float> parseHeights(LAND::VHGT* vhgt) | ||||
|   { | ||||
|     std::vector<float> ph; | ||||
|     ph.resize(LAND_NUM_VERTS, LAND_DEFAULT_HEIGHT); | ||||
|     float offset = vhgt->heightOffset; | ||||
|     for (int y = 0; y < LAND_VERT_WIDTH; y++) { //code from MGE
 | ||||
|       offset += vhgt->heightData[y*LAND_VERT_WIDTH]; | ||||
|       ph[y*LAND_VERT_WIDTH] =+ (float)offset*8; | ||||
|       float pos = offset; | ||||
|       for (int x = 1; x < LAND_VERT_WIDTH; x++) { | ||||
|         pos += vhgt->heightData[y*LAND_VERT_WIDTH+x]; | ||||
|         ph[y*LAND_VERT_WIDTH+x] = pos*8; //flipped x
 | ||||
|       } | ||||
|     } | ||||
|     return ph; | ||||
|   } | ||||
| 
 | ||||
|   std::vector<char> parseNormals( LAND::VNML* vnml ) | ||||
|   { | ||||
|     std::vector<char> n; | ||||
|     n.resize(LAND_NUM_VERTS*3,0); | ||||
|     for ( int y = 0; y < LAND_VERT_WIDTH; y++ ) { //this could just be cast.
 | ||||
|       for ( int x = 0; x < LAND_VERT_WIDTH; x++ ) { //as a vector is a continus segment of mem...
 | ||||
|         n[(y*LAND_VERT_WIDTH+x)*3] = vnml->normals[y*LAND_VERT_WIDTH+x].x; | ||||
|         n[(y*LAND_VERT_WIDTH+x)*3+1] = vnml->normals[y*LAND_VERT_WIDTH+x].y; | ||||
|         n[(y*LAND_VERT_WIDTH+x)*3+2] = vnml->normals[y*LAND_VERT_WIDTH+x].z; | ||||
|       } | ||||
|     } | ||||
|     return n; | ||||
|   } | ||||
| 
 | ||||
|   std::vector<short> parseTextures( LAND::VTEX* vtex ) | ||||
|   { | ||||
|     std::vector<short> t; | ||||
|     t.resize(LAND_NUM_LTEX,0); | ||||
| 
 | ||||
|     //thanks to timeslip (MGE) for the code
 | ||||
|     int rpos = 0; //bit ugly, but it works
 | ||||
|     for ( int y1 = 0; y1 < 4; y1++ ) | ||||
|       for ( int x1 = 0; x1 < 4; x1++ ) | ||||
|         for ( int y2 = 0; y2 < 4; y2++) | ||||
|           for ( int x2 = 0; x2 < 4; x2++ ) | ||||
|             t[(y1*4+y2)*16+(x1*4+x2)]=vtex->index[rpos++]; | ||||
|     return t; | ||||
|   } | ||||
| 
 | ||||
|   /**
 | ||||
|    * Holds the representation of a cell in the way that is most usefull to me | ||||
|    */ | ||||
|   struct LandData | ||||
|   { | ||||
|     std::string source; //data file the data is from
 | ||||
|     std::vector<float> heights; | ||||
|     std::vector<char> normals; | ||||
|     std::vector<short> textures; | ||||
|   }; | ||||
| 
 | ||||
|   struct LandTexture | ||||
|   { | ||||
|     std::string name, data; | ||||
|     short intv; | ||||
|   }; | ||||
| 
 | ||||
|   std::map<std::string, std::map<short, LandTexture> > mLTEXRecords; | ||||
|   std::map<int, std::map<int,LandData> > mLandRecords; | ||||
| }; | ||||
							
								
								
									
										367
									
								
								terrain/cpp_materialgen.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										367
									
								
								terrain/cpp_materialgen.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,367 @@ | |||
| /**
 | ||||
|  * Handles the runtime generation of materials | ||||
|  * | ||||
|  */ | ||||
| class MaterialGenerator | ||||
| { | ||||
|   class TextureSplatter | ||||
|   { | ||||
|   public: | ||||
|     inline TextureSplatter(const std::vector<int>& ltex, | ||||
|                            int texSize, | ||||
|                            int ltexsize, | ||||
|                            int border) : | ||||
|       mLTEX(ltex), | ||||
|       mTexSize(texSize), //the root of the size of the texture
 | ||||
|       mLTEXSize(ltexsize), //the root of the ltex array
 | ||||
|       mBorder(border) {   //the size of the border of the ltex
 | ||||
|       mSizeDiff = texSize/(ltexsize-border*2); | ||||
|     } | ||||
| 
 | ||||
|     int getAlphaAtPixel(int x, int y, int tid) { | ||||
|       //offset for border
 | ||||
|       x += (mBorder*mSizeDiff); | ||||
|       y += (mBorder*mSizeDiff); | ||||
| 
 | ||||
|       if ( getTextureAtPixel(x,y) == tid ) { | ||||
|         return 255; | ||||
|       } else if ( mBorder > 0 ) { //hacky remove fix. perofrmance issues. skips if not ingame gen.
 | ||||
|         float amount = 0; | ||||
|         for ( int ty = y-1; ty <= y+1; ++ty ) { | ||||
|           for ( int tx = x-1; tx <= x+1; ++tx ) { | ||||
| 
 | ||||
|             if ( ty < -mBorder*mSizeDiff || | ||||
|                  tx < -mBorder*mSizeDiff || | ||||
|                  ty >= mTexSize+mBorder*mSizeDiff || | ||||
|                  tx >= mTexSize+mBorder*mSizeDiff  ) | ||||
|               continue; | ||||
| 
 | ||||
|             if ( getTextureAtPixel(tx,ty) == tid ) | ||||
|               amount += 0.18f; | ||||
|           } | ||||
|         } | ||||
|         if ( amount > 1 ) amount = 1; | ||||
|         assert(amount>=0&&amount<=1); | ||||
|         return amount*255; | ||||
|       } | ||||
|       return 0; | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|   private: | ||||
| 
 | ||||
|     int getTextureAtPixel(int x, int y) { | ||||
|       x = (x - x%mSizeDiff)/mSizeDiff; | ||||
|       y = (y - y%mSizeDiff)/mSizeDiff; | ||||
|       //y = floor(float(y)/float(mSizeDiff));
 | ||||
|       //x = floor(float(x)/float(mSizeDiff));
 | ||||
| 
 | ||||
|       return mLTEX[(y)*mLTEXSize+(x)]; | ||||
|     } | ||||
| 
 | ||||
|     const std::vector<int>& mLTEX; | ||||
|     const int mTexSize; | ||||
|     const int mLTEXSize; | ||||
|     const int mBorder; | ||||
|     int mSizeDiff; | ||||
|   }; | ||||
| 
 | ||||
| public: | ||||
| 
 | ||||
|   /**
 | ||||
|    * @brief generates a material for a quad using a single texture | ||||
|    */ | ||||
|   Ogre::MaterialPtr generateSingleTexture(const std::string& matname, const std::string& texName, std::list<Ogre::ResourcePtr> createdResources) | ||||
|   { | ||||
|     if ( !Ogre::MaterialManager::getSingleton().resourceExists(matname) ) | ||||
|       { | ||||
|         Ogre::MaterialPtr mat = Ogre::MaterialManager::getSingleton().create(matname,Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); | ||||
| 
 | ||||
|         Ogre::Pass* p = mat->getTechnique(0)->getPass(0); | ||||
|         p->setLightingEnabled(false); | ||||
|         p->createTextureUnitState(texName)->setTextureAddressingMode(TextureUnitState::TAM_CLAMP); | ||||
|         createdResources.push_back(mat); | ||||
| 
 | ||||
|         return mat; | ||||
|       } | ||||
|     return Ogre::MaterialPtr(); | ||||
|   } | ||||
| 
 | ||||
|   /**
 | ||||
|    * Currently doesn't work | ||||
|    */ | ||||
|   Ogre::MaterialPtr getShaderAlpha(const std::string& materialName, std::vector<int>& ltex, int size, std::list<Ogre::TexturePtr>& generatedAlphas) | ||||
|   { | ||||
|     Ogre::TexturePtr currentAlphaTexture; //ptr to the texture we are currnly writing to
 | ||||
|     Ogre::uint8* currentAlphaPtr = 0; //pointer to the data
 | ||||
|     Ogre::HardwarePixelBufferSharedPtr currentPixelBuffer; //pointer to the buffer for ctrling locks/unlicjks
 | ||||
|     int currentColour = 0; //current colour index. ARGB.
 | ||||
|     std::vector<std::string> texturesWritten; //a list of "done" textures. Order important
 | ||||
|     std::vector<std::string> alphasGenerated; //alphas generated
 | ||||
| 
 | ||||
|     std::set<short> tidDone; //holds the list of done textures
 | ||||
| 
 | ||||
|     const int rootSideLength = Ogre::Math::Sqrt(ltex.size()); //used for looping
 | ||||
| 
 | ||||
|     //loop over every splat possition
 | ||||
|     for ( int y1 = 0; y1 < rootSideLength; y1++ ) { | ||||
|       for ( int x1 = 0; x1 < rootSideLength; x1++ ) { | ||||
|         const short tid = ltex[y1*rootSideLength+x1]; | ||||
| 
 | ||||
|         //if already done.
 | ||||
|         if ( tidDone.find(tid) != tidDone.end())	continue; | ||||
| 
 | ||||
|         //insert it into the done list
 | ||||
|         tidDone.insert(tid); | ||||
| 
 | ||||
|         //get the textuyre path. If it is default we don't need to do anything, as it
 | ||||
|         //is done in the first pass. CHANGE? We end up using a whole pass for it??
 | ||||
|         const std::string tn(mTexturePaths[tid]); | ||||
|         if ( tn == "_land_default.dds" ) continue; | ||||
| 
 | ||||
|         texturesWritten.push_back(tn); | ||||
| 
 | ||||
|         //unqiue alpha name
 | ||||
|         const std::string alphaName(materialName + "_A_" + tn); | ||||
| 
 | ||||
| 
 | ||||
|         if ( Ogre::TextureManager::getSingleton().resourceExists(alphaName) ) | ||||
|           OGRE_EXCEPT(0, "ALPHA Already Exists", ""); //shouldn't happen.
 | ||||
| 
 | ||||
|         //check if we need to create a new apla
 | ||||
|         if ( currentColour == 4 || //we only have 4 colours per tex ofc
 | ||||
|              currentAlphaTexture.isNull() ) { //no texture assigned yet
 | ||||
| 
 | ||||
|           //unlock old buffer, if we have one
 | ||||
|           if ( !currentAlphaTexture.isNull() ) | ||||
|             currentPixelBuffer->unlock(); | ||||
| 
 | ||||
|           //new texture
 | ||||
|           currentAlphaTexture  = Ogre::TextureManager::getSingleton(). | ||||
|             createManual( | ||||
|                          alphaName, | ||||
|                          Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, | ||||
|                          Ogre::TEX_TYPE_2D, | ||||
|                          size,size, //size ofc
 | ||||
|                          1,	0, //depth, mipmaps
 | ||||
|                          Ogre::PF_A8R8G8B8, //4 channesl for 4 splats
 | ||||
|                          Ogre::TU_STATIC_WRITE_ONLY //we only need to write data
 | ||||
|                          ); | ||||
| 
 | ||||
|           generatedAlphas.push_back(currentAlphaTexture); //record
 | ||||
|           alphasGenerated.push_back(alphaName); | ||||
| 
 | ||||
| 
 | ||||
|           currentPixelBuffer = currentAlphaTexture->getBuffer(); | ||||
|           currentPixelBuffer->lock(Ogre::HardwareBuffer::HBL_DISCARD); | ||||
|           const Ogre::PixelBox& pixelBox = currentPixelBuffer->getCurrentLock(); | ||||
| 
 | ||||
|           currentAlphaPtr = static_cast<Ogre::uint8*>(pixelBox.data); | ||||
| 
 | ||||
|           //zero out all data
 | ||||
|           for ( int i = 0; i < size*size*4; i++ ) | ||||
|             currentAlphaPtr[i] = 0; | ||||
| 
 | ||||
| 
 | ||||
|           currentColour = 0; | ||||
|         } else { | ||||
|           currentColour++; //increase the colour we are working with by one
 | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         //for every splat that the texture is splatted,
 | ||||
|         //check if it is the same as the one in the current splat
 | ||||
|         for ( int y2 = 0; y2 < rootSideLength; y2++ ) { | ||||
|           for ( int x2 = 0; x2 < rootSideLength; x2++ ) { | ||||
|             if ( ltex[y2*rootSideLength+x2] == tid ) { | ||||
| 
 | ||||
|               //add splat to current alpha map
 | ||||
|               const int splatSize = size/rootSideLength; | ||||
| 
 | ||||
|               for ( int ys = 0; ys < splatSize ; ys++ ) { | ||||
|                 for ( int xs = 0; xs < splatSize; xs++ ) { | ||||
|                   //calc position on main texture
 | ||||
|                   const int pxpos = splatSize*x2+xs; | ||||
|                   const int pypos = splatSize*y2+ys; | ||||
| 
 | ||||
|                   //write splat to buffer
 | ||||
|                   const int index = (pypos*size*4)+(pxpos*4)+currentColour; | ||||
|                   currentAlphaPtr[index] = 255; | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         }//for ( int y2 = 0; y2 < rootSideLength; y2++ ){
 | ||||
| 
 | ||||
| 
 | ||||
|       } | ||||
|     }//for ( int y1 = 0; y1 < rootSideLength; y1++ ){
 | ||||
| 
 | ||||
|     //check to see if we need to unlock a buff again
 | ||||
|     if ( !currentAlphaTexture.isNull() ) | ||||
|       currentPixelBuffer->unlock(); | ||||
| 
 | ||||
|     //sort material
 | ||||
|     assert(Ogre::MaterialManager::getSingleton().getByName("AlphaSplatTerrain").getPointer()); | ||||
| 
 | ||||
|     Ogre::MaterialPtr material = ((Ogre::Material*)Ogre::MaterialManager::getSingleton().getByName("AlphaSplatTerrain").getPointer())->clone(materialName); | ||||
| 
 | ||||
|     Ogre::Pass* pass = material->getTechnique(0)->getPass(0); | ||||
| 
 | ||||
|     //write alphas
 | ||||
|     if ( alphasGenerated.size() > 0 ) | ||||
|       pass->getTextureUnitState(0)->setTextureName(alphasGenerated[0]); | ||||
|     if ( alphasGenerated.size() > 1 ) | ||||
|       pass->getTextureUnitState(1)->setTextureName(alphasGenerated[1]); | ||||
| 
 | ||||
|     //write 8 textures
 | ||||
|     int c = 1; | ||||
|     for ( std::vector<std::string>::iterator itr = texturesWritten.begin(); | ||||
|           itr != texturesWritten.end(); | ||||
|           ++itr ) { | ||||
|       if ( ++c > 8 ) break; | ||||
|       pass->getTextureUnitState(1+c)->setTextureName(*itr); | ||||
|       //ta["Splat" + Ogre::StringConverter::toString(c)] = *itr;
 | ||||
|     } | ||||
| 
 | ||||
|     //pass->applyTextureAliases(ta, true);
 | ||||
| 
 | ||||
|     return material; | ||||
| 
 | ||||
|   } | ||||
| 
 | ||||
|   /**
 | ||||
|    * gets the material for the alpha textures | ||||
|    */ | ||||
|   Ogre::MaterialPtr getAlphaMat(const std::string& materialName, | ||||
|                                 std::vector<int>& ltex, | ||||
|                                 int size, | ||||
|                                 int border, | ||||
|                                 float scaleDiv, | ||||
|                                 std::list<Ogre::ResourcePtr>& createdResources) | ||||
|   { | ||||
|     const int sizeDiff = 4; | ||||
|     size *= sizeDiff; | ||||
| 
 | ||||
|     const int rootSideLength = Ogre::Math::Sqrt(ltex.size())-(border*2); //used for looping
 | ||||
|     const int ltexWidth = Ogre::Math::Sqrt(ltex.size()); | ||||
|     assert(size%rootSideLength==0); | ||||
| 
 | ||||
|     Ogre::MaterialPtr material = Ogre::MaterialManager::getSingleton(). | ||||
|       create(materialName,Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); | ||||
|     createdResources.push_back(material); | ||||
| 
 | ||||
| 
 | ||||
|     Ogre::Pass* np = material->getTechnique(0)->getPass(0); | ||||
|     np->setLightingEnabled(false); | ||||
|     np->createTextureUnitState("_land_default.dds")->setTextureScale(0.1f/scaleDiv,0.1f/scaleDiv); | ||||
| 
 | ||||
|     std::set<int> textures; | ||||
|     for ( int y1 = 0; y1 < ltexWidth; y1++ ) { | ||||
|       for ( int x1 = 0; x1 < ltexWidth; x1++ ) { | ||||
|         textures.insert(ltex[(y1)*ltexWidth+x1]); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     for ( std::set<int>::iterator itr = textures.begin(); itr != textures.end(); ++itr ) | ||||
|       { | ||||
| 
 | ||||
|         int tid = *itr; | ||||
| 
 | ||||
|         const std::string tn(mTexturePaths[tid]); | ||||
|         if ( tn == "_land_default.dds" ) | ||||
|           continue; | ||||
| 
 | ||||
|         //std::cout << "  Generating texture " << tn << "\n";
 | ||||
| 
 | ||||
|         std::string alphaName(materialName + "_A_" + tn); | ||||
|         if ( Ogre::TextureManager::getSingleton().resourceExists(alphaName) ) | ||||
|           OGRE_EXCEPT(0, "ALPHA Already Exists", ""); | ||||
| 
 | ||||
|         //create alpha map
 | ||||
|         Ogre::TexturePtr texPtr  = Ogre::TextureManager::getSingleton(). | ||||
|           createManual( | ||||
|                        alphaName, // Name of texture
 | ||||
|                        Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, // Name of resource group in which the texture should be created
 | ||||
|                        Ogre::TEX_TYPE_2D, | ||||
|                        size,size, //size ofc
 | ||||
|                        1,	0, //depth, mipmaps
 | ||||
|                        Ogre::PF_A8, //we only need one channel to hold the splatting texture
 | ||||
|                        Ogre::TU_STATIC_WRITE_ONLY //best performace, we shopuldn't need the data again
 | ||||
|                        ); | ||||
| 
 | ||||
|         createdResources.push_back(texPtr); | ||||
| 
 | ||||
| 
 | ||||
|         Ogre::HardwarePixelBufferSharedPtr pixelBuffer = texPtr->getBuffer(); | ||||
|         pixelBuffer->lock(Ogre::HardwareBuffer::HBL_DISCARD); | ||||
|         const Ogre::PixelBox& pixelBox = pixelBuffer->getCurrentLock(); | ||||
| 
 | ||||
|         Ogre::uint8* pDest = static_cast<Ogre::uint8*>(pixelBox.data); | ||||
|         memset(pDest,0, sizeof(Ogre::uint8)*size*size); | ||||
| 
 | ||||
|         TextureSplatter ts(ltex, size, ltexWidth, border); | ||||
|         for ( int ty = 0; ty < size; ty++ ) { | ||||
|           for ( int tx = 0; tx < size; tx++ ) { | ||||
|             pDest[ty*size+tx] = ts.getAlphaAtPixel(tx,ty, tid); | ||||
|           } | ||||
|         } | ||||
| 
 | ||||
|         pixelBuffer->unlock(); | ||||
| 
 | ||||
|         np = material->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); | ||||
| 
 | ||||
|         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); | ||||
| 
 | ||||
| 
 | ||||
|         tus = np->createTextureUnitState(tn); | ||||
|         tus->setColourOperationEx(	Ogre::LBX_BLEND_DIFFUSE_ALPHA, | ||||
|                                         Ogre::LBS_TEXTURE, | ||||
|                                         Ogre::LBS_CURRENT); | ||||
|         tus->setTextureScale(0.1f/scaleDiv,0.1f/scaleDiv); | ||||
|       } | ||||
| 
 | ||||
|     return material; | ||||
|   } | ||||
| 
 | ||||
|   std::string generateAlphaMap(const std::string& name, | ||||
|                                std::vector<int>& ltex, | ||||
|                                int size, | ||||
|                                int border, | ||||
|                                float scaleDiv, | ||||
|                                std::list<Ogre::ResourcePtr>& createdResources) | ||||
|   { | ||||
|     //std::string materialName = "TEX_" + Ogre::StringConverter::toString(cp.x) + "_" + Ogre::StringConverter::toString(cp.y);
 | ||||
| 
 | ||||
|     if ( Ogre::MaterialManager::getSingleton().resourceExists(name) ) | ||||
|       assert(0); | ||||
|     //return materialName;
 | ||||
| 
 | ||||
|     getAlphaMat(name, ltex, size, border, scaleDiv, createdResources); | ||||
|     //getShaderAlpha(materialName, ltex, size, newTextures);
 | ||||
| 
 | ||||
|     return name; | ||||
|   } | ||||
| 
 | ||||
|   inline void setTexturePaths( std::map<int, std::string> r) { | ||||
|     mTexturePaths = r; | ||||
|   } | ||||
| private: | ||||
|   /**
 | ||||
|    * Merged records accross all mods for LTEX data | ||||
|    */ | ||||
|   std::map<int, std::string> mTexturePaths; | ||||
| }; | ||||
							
								
								
									
										110
									
								
								terrain/cpp_meshinterface.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								terrain/cpp_meshinterface.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,110 @@ | |||
| MeshInterface::MeshInterface(Quad* p, Terrain* t) : | ||||
|   mParentQuad(p), | ||||
|   mTerrain(t), | ||||
|   mMax(0), | ||||
|   mMin(0), | ||||
|   mSplitState(SS_NONE) { | ||||
| 
 | ||||
|   mQuadData = t->getTerrainData()->getData(mParentQuad); | ||||
| 
 | ||||
|   //the mesh is created a zero, so an offset is applied
 | ||||
|   const Ogre::Vector3 pos(mParentQuad->getPosition().x - mParentQuad->getSideLength()/2, | ||||
|                           0, | ||||
|                           mParentQuad->getPosition().y - mParentQuad->getSideLength()/2); | ||||
| 
 | ||||
|   mSceneNode = mTerrain->getTerrainSceneNode()->createChildSceneNode(pos); | ||||
| } | ||||
| 
 | ||||
| MeshInterface::~MeshInterface() { | ||||
|   for ( std::vector<TerrainObjectGroup*>::iterator itr = mTerrainObjects.begin(); | ||||
|         itr != mTerrainObjects.end(); | ||||
|         ++itr ) | ||||
|     delete *itr; | ||||
| 
 | ||||
|   mSceneNode->removeAndDestroyAllChildren(); | ||||
|   mSceneMgr->destroySceneNode(mSceneNode); | ||||
| 
 | ||||
|   mTerrain->getTerrainSceneNode()->detachAllObjects(); | ||||
| 
 | ||||
|   mTerrain->_quadDestroyed(mQuadData); | ||||
|   delete mQuadData; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| void MeshInterface::create() { | ||||
|   //LOG("Creating");
 | ||||
|   if ( mParentQuad->getDepth() == mTerrain->getMaxDepth() ) { | ||||
|     for ( int y = 0; y < 4; ++y ) { | ||||
|       for ( int x = 0; x < 4; ++x ) { | ||||
|         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
 | ||||
|       } | ||||
|     } | ||||
|   } else { | ||||
|     addNewObject(Ogre::Vector3(0,0,0), 65); | ||||
|   } | ||||
| 
 | ||||
|   getBounds(); | ||||
|   mTerrain->_quadCreated(mQuadData); | ||||
| } | ||||
| 
 | ||||
| void MeshInterface::addNewObject(const Ogre::Vector3& pos, | ||||
|                                  int terrainSize, | ||||
|                                  bool skirts /*= true*/, | ||||
|                                  float segmentSize /*= 1*/, | ||||
|                                  float startX /*= 0*/, | ||||
|                                  float startY /*= 0*/ ) { | ||||
| 
 | ||||
|   TerrainObjectGroup* to = new TerrainObjectGroup(); | ||||
| 
 | ||||
|   to->segment = new QuadSegment(mQuadData, segmentSize, startX, startY); | ||||
|   to->node = mSceneNode->createChildSceneNode(pos); | ||||
|   to->terrain = new TerrainRenderable(mTerrain, to->segment, terrainSize, mParentQuad->getDepth(), skirts); | ||||
|   to->terrain->create(to->node); | ||||
| 
 | ||||
|   mMax = std::max(to->terrain->getMax(), mMax); | ||||
|   mMin = std::max(to->terrain->getMin(), mMin); | ||||
| 
 | ||||
|   mTerrainObjects.push_back(to); | ||||
| } | ||||
| 
 | ||||
| void MeshInterface::update(Ogre::Real time) { | ||||
|   const Ogre::Vector3 cpos = mCamera->getDerivedPosition(); | ||||
|   Ogre::Vector3 diff(0, 0, 0); | ||||
| 
 | ||||
|   //copy?
 | ||||
|   Ogre::AxisAlignedBox worldBounds = mBounds; | ||||
|   worldBounds.transformAffine(mSceneNode->_getFullTransform()); | ||||
| 
 | ||||
|   diff.makeFloor(cpos - worldBounds.getMinimum() ); | ||||
|   diff.makeCeil(cpos - worldBounds.getMaximum() ); | ||||
|   const Ogre::Real camDist = diff.squaredLength(); | ||||
| 
 | ||||
|   mSplitState = SS_NONE; | ||||
|   if ( camDist < mSplitDistance )             mSplitState = SS_SPLIT; | ||||
|   else if ( camDist > mUnsplitDistance )      mSplitState = SS_UNSPLIT; | ||||
| 
 | ||||
| 
 | ||||
|   for ( std::vector<TerrainObjectGroup*>::iterator itr = mTerrainObjects.begin(); | ||||
|         itr != mTerrainObjects.end(); | ||||
|         ++itr ) | ||||
|     (*itr)->terrain->update(time, camDist, mUnsplitDistance, mMorphDistance); | ||||
| } | ||||
| 
 | ||||
| void MeshInterface::getBounds() { | ||||
|   mBounds.setExtents( 0, | ||||
|                       mMin, | ||||
|                       0, | ||||
|                       (65 - 1) * mQuadData->getVertexSeperation(), | ||||
|                       mMax, | ||||
|                       (65 - 1) * mQuadData->getVertexSeperation()); | ||||
| 
 | ||||
|   mBoundingRadius = (mBounds.getMaximum() - mBounds.getMinimum()).length() / 2; | ||||
| 
 | ||||
|   mSplitDistance = pow(mBoundingRadius * 0.5, 2); | ||||
|   mUnsplitDistance = pow(mBoundingRadius * 2.0, 2); | ||||
|   mMorphDistance = pow(mBoundingRadius * 1.5, 2); | ||||
| } | ||||
							
								
								
									
										117
									
								
								terrain/cpp_meshinterface.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								terrain/cpp_meshinterface.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,117 @@ | |||
| /**
 | ||||
| * Interface between the quad and the terrain renderble classes, to the | ||||
| * quad it looks like this rendereds a single mesh for the quad. This | ||||
| * may not be the case. | ||||
| * | ||||
| * It also could allow several optomizations (e.g. multiple splits) | ||||
| */ | ||||
| class MeshInterface | ||||
| { | ||||
|     /**
 | ||||
|     * | ||||
|     * @brief Holds a group of objects and destorys them in the | ||||
|     * destructor. Avoids the needs for 100s of vectors | ||||
|     */ | ||||
|     struct TerrainObjectGroup { | ||||
|         /**
 | ||||
|         * @brief inits all ptrs at 0 | ||||
|         */ | ||||
|         inline TerrainObjectGroup() : segment(0), terrain(0), node(0) {} | ||||
| 
 | ||||
|         /**
 | ||||
|         * @brief destorys all objects | ||||
|         */ | ||||
|         inline ~TerrainObjectGroup() { | ||||
|             if ( node ) { | ||||
|                 node->detachAllObjects(); | ||||
|                 node->getCreator()->destroySceneNode(node); | ||||
|             } | ||||
|             delete terrain; | ||||
|             delete segment; | ||||
|         } | ||||
|         QuadSegment* segment; | ||||
|         TerrainRenderable* terrain; | ||||
|         Ogre::SceneNode* node; | ||||
|     }; | ||||
| public: | ||||
|     enum SplitState { SS_NONE, SS_SPLIT, SS_UNSPLIT }; | ||||
| 
 | ||||
|     MeshInterface(Quad* p, Terrain* t); | ||||
|     ~MeshInterface() ; | ||||
| 
 | ||||
|     /**
 | ||||
|     * @brief creates all required meshes. If it is at the max depth, it creates 16, otherwise just one | ||||
|     */ | ||||
|     void create(); | ||||
| 
 | ||||
|     /**
 | ||||
|     * @brief updates all meshes. | ||||
|     * @remarks the camera distance is calculated here so that all terrain has the correct morph levels etc | ||||
|     */ | ||||
|     void update(Ogre::Real time); | ||||
| 
 | ||||
|     inline SplitState getSplitState() { | ||||
|         return mSplitState; | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|     * @brief propergates the just split through all terrain | ||||
|     */ | ||||
|     inline void justSplit() { | ||||
|         for ( std::vector<TerrainObjectGroup*>::iterator itr = mTerrainObjects.begin(); | ||||
|                 itr != mTerrainObjects.end(); | ||||
|                 ++itr ) | ||||
|             (*itr)->terrain->justSplit(); | ||||
|     } | ||||
|     /**
 | ||||
|     * @brief propergates the just unsplit through all terrain | ||||
|     */ | ||||
|     inline void justUnsplit() { | ||||
|         for ( std::vector<TerrainObjectGroup*>::iterator itr = mTerrainObjects.begin(); | ||||
|                 itr != mTerrainObjects.end(); | ||||
|                 ++itr ) | ||||
|             (*itr)->terrain->justUnsplit(); | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
| 
 | ||||
|     Quad* mParentQuad; | ||||
|     Terrain* mTerrain; | ||||
| 
 | ||||
|     ///Must be a ptr, else it destorys before we are ready
 | ||||
|     std::vector<TerrainObjectGroup*> mTerrainObjects; | ||||
| 
 | ||||
|     Ogre::SceneNode* mSceneNode; | ||||
| 
 | ||||
|     ///use for split distances
 | ||||
|     Ogre::Real mBoundingRadius; | ||||
|     Ogre::AxisAlignedBox mBounds; | ||||
| 
 | ||||
|     ///max and min heights
 | ||||
|     float mMax, mMin; | ||||
| 
 | ||||
|     Ogre::Real mSplitDistance,mUnsplitDistance,mMorphDistance; | ||||
| 
 | ||||
|     SplitState mSplitState; | ||||
|     QuadData* mQuadData; | ||||
| 
 | ||||
|     /**
 | ||||
|     * @brief sets the bounds and split radius of the object | ||||
|     */ | ||||
|     void getBounds(); | ||||
| 
 | ||||
|     /**
 | ||||
|     * @brief Adds a new mesh | ||||
|     * @param pos the position in relation to mSceneNode | ||||
|     * @param terrainSize the size of the terrain in verts. Should be n^2+1 | ||||
|     * @param skirts true if the terrain should have skirts | ||||
|     * @param segmentSize the size of the segment. So if splitting terrain into 4*4, it should be 0.25 | ||||
|     * @param startX, startY the start position of this segment (0 <= startX < 1) | ||||
|     */ | ||||
|     void addNewObject(const Ogre::Vector3& pos,  int terrainSize, | ||||
|                       bool skirts = true, float segmentSize = 1, | ||||
|                       float startX = 0, float startY = 0 ); | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| }; | ||||
							
								
								
									
										84
									
								
								terrain/cpp_mwheightmap.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								terrain/cpp_mwheightmap.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,84 @@ | |||
| /**
 | ||||
|  * @brief the class that deals with loading quads from the disk @todo a | ||||
|  * major improvment would be to store the data as a quad tree. It might | ||||
|  * improve lookup times. Then again. Might not | ||||
|  */ | ||||
| class MWHeightmap : public TerrainHeightmap | ||||
| { | ||||
| public: | ||||
|   QuadData* getData(Quad* q) | ||||
|   { | ||||
|     MWQuadData* data = loadQuad(q->getPosition().x,q->getPosition().y); | ||||
| 
 | ||||
|     if ( !data ) | ||||
|       assert(0); | ||||
| 
 | ||||
|     return data; | ||||
|   } | ||||
| 
 | ||||
|   inline bool hasData(Quad* q) { | ||||
|     return hasQuad(q->getPosition().x,q->getPosition().y); | ||||
|   } | ||||
| 
 | ||||
|   inline long getRootSideLength() { | ||||
|     return mIndex.getRootSideLength(); | ||||
|   } | ||||
|   inline int getMaxDepth() { | ||||
|     return mIndex.getMaxDepth(); | ||||
|   } | ||||
| 
 | ||||
|   /**
 | ||||
|    * Loads the index and palette | ||||
|    */ | ||||
|   bool load(const std::string& fn) | ||||
|   { | ||||
|     { | ||||
|       std::ifstream ifs(std::string(fn + ".index").c_str(), std::ios::binary); | ||||
|       boost::archive::binary_iarchive oa(ifs); | ||||
|       oa >> mIndex; | ||||
|     } | ||||
|     { | ||||
|       std::ifstream ifs(std::string(fn + ".palette").c_str(), std::ios::binary); | ||||
|       boost::archive::binary_iarchive oa(ifs); | ||||
|       oa >> mPalette; | ||||
|     } | ||||
|     mMaterialGen.setTexturePaths(mPalette.getPalette()); | ||||
| 
 | ||||
|     mMaterialGenerator = new MWQuadMaterialGen(&mMaterialGen); | ||||
| 
 | ||||
|     mDataIFS.open(std::string(fn + ".data").c_str(), std::ios::binary); | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
| private: | ||||
| 
 | ||||
|   inline long hasQuad(long x, long y) { | ||||
|     return (mIndex.getOffset(x,y) != -1 ) ; | ||||
|   } | ||||
| 
 | ||||
|   /**
 | ||||
|    * @brief loads the quad data from the disk | ||||
|    */ | ||||
|   MWQuadData* loadQuad(long x, long y) | ||||
|   { | ||||
|     long offset = mIndex.getOffset(x,y); | ||||
|     if ( offset == -1 ) //check we have xy
 | ||||
|       return 0; | ||||
| 
 | ||||
|     mDataIFS.seekg(offset); | ||||
| 
 | ||||
|     //load the quad from the file
 | ||||
|     MWQuadData* q = new MWQuadData(this); | ||||
|     boost::archive::binary_iarchive oa(mDataIFS); | ||||
|     oa >> *q; | ||||
|     return q; | ||||
|   } | ||||
| 
 | ||||
|   ///ifs for the data file. Opned on load
 | ||||
|   std::ifstream mDataIFS; | ||||
|   ///holds the offsets of the quads
 | ||||
|   Index mIndex; | ||||
|   TexturePalette mPalette; | ||||
|   ///material generator. gens a ogre::material from quad data
 | ||||
|   MaterialGenerator mMaterialGen; | ||||
| }; | ||||
							
								
								
									
										84
									
								
								terrain/cpp_mwquadmatgen.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								terrain/cpp_mwquadmatgen.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,84 @@ | |||
| 
 | ||||
| /**
 | ||||
|  * @todo mergre with matgen class | ||||
|  */ | ||||
| class MWQuadMaterialGen : public QuadMaterialGenerator | ||||
| { | ||||
| public: | ||||
|   MWQuadMaterialGen(MaterialGenerator* mg) : mMatGen(mg), mCount(0){} | ||||
| 
 | ||||
|   Ogre::MaterialPtr getMaterial(QuadData* qd){ | ||||
|     return _getMaterial((MWQuadData*)qd); | ||||
|   } | ||||
|   Ogre::MaterialPtr getMaterialSegment(QuadData* qd, QuadSegment* qs){ | ||||
|     return _getMaterialSegment((MWQuadData*)qd,qs); | ||||
|   } | ||||
| private: | ||||
| 
 | ||||
|   Ogre::MaterialPtr _getMaterial(MWQuadData* qd){ | ||||
|     assert(qd); | ||||
|     const std::string name("MAT" + Ogre::StringConverter::toString(mCount++)); | ||||
| 
 | ||||
|     if ( qd->getTexture().length() ){ | ||||
|       return mMatGen->generateSingleTexture(name, qd->getTexture(), qd->getUsedResourcesRef()); | ||||
| 
 | ||||
|     }else{ | ||||
|       assert(0); | ||||
|     } | ||||
| 
 | ||||
|     return Ogre::MaterialPtr(); | ||||
|   } | ||||
| 
 | ||||
|   Ogre::MaterialPtr _getMaterialSegment(MWQuadData* qd, QuadSegment* qs){ | ||||
|     const std::string name("MAT" + Ogre::StringConverter::toString(mCount++)); | ||||
| 
 | ||||
| 
 | ||||
|     if ( qd->getTexture().length() ) | ||||
|       assert(0&&"NOT IMPLEMENTED"); | ||||
| 
 | ||||
|     const std::vector<int>& tref = qd->getTextureIndexRef(); | ||||
|     const int indexSize = sqrt(tref.size()); | ||||
|     const int cellIndexSize = indexSize - 2; | ||||
| 
 | ||||
| 
 | ||||
|     //plus 1 to take into account border
 | ||||
|     const int xoff = float(cellIndexSize) * qs->getStartX(); | ||||
|     const int yoff = float(cellIndexSize) * qs->getStartY(); | ||||
|     const int size = float(cellIndexSize) * qs->getSegmentSize(); | ||||
| 
 | ||||
|     std::vector<int> ti; | ||||
| 
 | ||||
| 
 | ||||
|     ti.resize((size+2)*(size+2), -1); | ||||
| 
 | ||||
|     for ( int y = 0; y < size+2; ++y ){ | ||||
|       for ( int x = 0; x < size+2; ++x ){ | ||||
|         ti[(y)*(size+2)+(x)] = tref.at((y+yoff)*(indexSize)+(x+xoff)); | ||||
|       } | ||||
|     } | ||||
|     /*
 | ||||
|       ti.resize((size)*(size)); | ||||
| 
 | ||||
|       for ( int y = 1; y < size+1; ++y ){ | ||||
|       for ( int x = 1; x < size+1; ++x ){ | ||||
| 
 | ||||
|       if ( y+yoff >= indexSize ) assert(0); | ||||
|       if ( x+xoff >= indexSize ) assert(0); | ||||
| 
 | ||||
|       ti.at((y-1)*(size)+(x-1)) = tref.at((y+yoff)*(indexSize)+(x+xoff)); | ||||
|       } | ||||
|       } | ||||
|     */ | ||||
|     Ogre::MaterialPtr t = Ogre::MaterialManager::getSingleton(). | ||||
|       getByName(mMatGen->generateAlphaMap | ||||
|                 (name, | ||||
|                  ti,size, | ||||
|                  1, 1.0f/size, | ||||
|                  qd->getUsedResourcesRef())); | ||||
|     return t; | ||||
|   } | ||||
| 
 | ||||
|   MaterialGenerator* mMatGen; | ||||
|   unsigned int mCount; | ||||
| 
 | ||||
| }; | ||||
							
								
								
									
										75
									
								
								terrain/cpp_palette.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								terrain/cpp_palette.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,75 @@ | |||
| /**
 | ||||
| * @brief a class that holds a texture palette (basicly, an index accoicated with a texture) | ||||
| * | ||||
| * Unfortunaly, this uses a std::map class, which means that hasIndex is slow. | ||||
| * A fix would be to use a class that has fast lookups for both key/value (both are keys). | ||||
| * Or something better than that. Whatever. | ||||
| * | ||||
| * Yeah, so this is a bit like a std::map | ||||
| */ | ||||
| class TexturePalette { | ||||
| public: | ||||
|     inline bool hasTexture(int index) { | ||||
|         return (mPalette.find(index) != mPalette.end()); | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|     * Not a great function. Very slow :( | ||||
|     */ | ||||
|     inline bool hasIndex(const std::string& texture) { | ||||
|         for ( Palette::iterator i = mPalette.begin(); i != mPalette.end(); ++i) { | ||||
|             if ( i->second == texture ) | ||||
|                 return true; | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|     inline int getIndex(const std::string& texture) { | ||||
|         for ( Palette::iterator i = mPalette.begin(); i != mPalette.end(); ++i) { | ||||
|             if ( i->second == texture ) | ||||
|                 return i->first; | ||||
|         } | ||||
|         return -1; | ||||
|     } | ||||
|     inline int getOrAddIndex(const std::string& texture) { | ||||
|         if ( hasIndex(texture) ) | ||||
|             return getIndex(texture); | ||||
|         return addTexture(texture); | ||||
|     } | ||||
| 
 | ||||
|     inline const std::string& getTexture(int index) { | ||||
|         return mPalette[index]; | ||||
|     } | ||||
|     inline void setTexture(int index, std::string texture) { | ||||
|         mPalette[index] = texture; | ||||
|     } | ||||
|     /**
 | ||||
|     * @todo add proper error thing rather than assert(0) | ||||
|     */ | ||||
|     inline int addTexture(const std::string& texture) { | ||||
|         for ( int i = 0; i >= 0; i++ ) { //this loop is not infinate, as it will go to -2^31
 | ||||
|             if ( mPalette.find(i) != mPalette.end() ) | ||||
|                 continue; | ||||
|             mPalette[i] = texture; | ||||
|             return i; | ||||
|         } | ||||
|         assert(0); //this should never happen. Seeing as we can assign about 2^31 images
 | ||||
|     } | ||||
| 
 | ||||
|     inline std::map<int, std::string>& getPalette() { | ||||
|         return mPalette; | ||||
|     } | ||||
| private: | ||||
|     typedef std::map<int, std::string> Palette; | ||||
|     Palette mPalette; | ||||
| 
 | ||||
|     friend class boost::serialization::access; | ||||
|     /**
 | ||||
|     * @brief saves the palette | ||||
|     */ | ||||
|     template<class Archive> | ||||
|     inline void serialize(Archive& ar, const unsigned int version){ | ||||
|         ar &mPalette; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| BOOST_CLASS_TRACKING(TexturePalette, boost::serialization::track_never); | ||||
							
								
								
									
										52
									
								
								terrain/cpp_point2.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								terrain/cpp_point2.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,52 @@ | |||
| /*
 | ||||
|   Copyright (c) Jacob Essex 2009 | ||||
| 
 | ||||
|   This file is part of MWLand. | ||||
| 
 | ||||
|   MWLand is free software: you can redistribute it and/or modify it | ||||
|   under the terms of the GNU General Public License as published by | ||||
|   the Free Software Foundation, either version 3 of the License, or | ||||
|   (at your option) any later version. | ||||
| 
 | ||||
|   MWLand 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 | ||||
|   along with MWLand.  If not, see <http://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| 
 | ||||
| /**
 | ||||
| * @brief utility class for holding two values | ||||
| * | ||||
| * This is mainly used for querying the position of the quad. | ||||
| * Each quad has a center position, which we can use as a unique identifier | ||||
| */ | ||||
| template<class T> | ||||
| struct Point2 { | ||||
|     T x, y; //held values.
 | ||||
| 
 | ||||
|     inline Point2() {} | ||||
|     inline Point2(T ix, T iy) { | ||||
|         x = ix; | ||||
|         y = iy; | ||||
|     } | ||||
|     inline Point2(const Point2<T>& i) { | ||||
|         x = i.x; | ||||
|         y = i.y; | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|     * @brief comparison operator. Although not used directly, this | ||||
|     * class is used in std::map a lot, which used the < operator | ||||
|     */ | ||||
| 
 | ||||
|     inline bool operator<(const Point2<T>& rhs) const{ | ||||
|         return ( x < rhs.x || !( rhs.x < x) && y < rhs.y ); | ||||
|     } | ||||
| 
 | ||||
|     inline Point2 operator + (const Point2<T>& rhs) { | ||||
|         return Point2(x + rhs.x, y + rhs.y); | ||||
|     } | ||||
| }; | ||||
							
								
								
									
										327
									
								
								terrain/cpp_quad.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										327
									
								
								terrain/cpp_quad.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,327 @@ | |||
| /**
 | ||||
|  * @brief defines an area of Landscape | ||||
|  * | ||||
|  * A quad can either hold a mesh, or 4 other sub quads | ||||
|  * The functions split and unslip either break the current quad into smaller quads, or | ||||
|  * alternatively remove the lower quads and create the terrain mesh on the the current (now the lowest) level | ||||
|  * | ||||
|  * needUnsplit and needSplit query the state of the meshes to see if it needs spliting or unspliting | ||||
|  * | ||||
|  */ | ||||
| class Quad | ||||
| { | ||||
| public: | ||||
| 
 | ||||
|   /**
 | ||||
|    * when each quad is split, the children can be one of 4 places, | ||||
|    * topleft (NW) top right (NE) etc etc. The other position is the | ||||
|    * root which is the top quad.  The root quad doesn't have a mesh, | ||||
|    * and should always have 4 children. | ||||
|    */ | ||||
| 
 | ||||
|   // FIXME: There's probably a better way to do this
 | ||||
|   enum QuadLocation { QL_NW, QL_NE, QL_SW, QL_SE, QL_ROOT }; | ||||
| 
 | ||||
|   /**
 | ||||
|    * @param l the location of the quad | ||||
|    * @param p the parent quad. Leave 0 if it is root | ||||
|    * @param t the terrain object | ||||
|    * | ||||
|    * Constructor mainly sets up the position variables/depth etc | ||||
|    */ | ||||
|   Quad(QuadLocation l, Quad* p, Terrain* t) | ||||
|     : mParent(p), mTerrain(t), mTerrainMesh(0), mLocation(l) | ||||
|   { | ||||
|     //as mentioned elsewhere, the children should all be null.
 | ||||
|     memset(mChildren, NULL, sizeof(Quad*)*NUM_CHILDREN); | ||||
| 
 | ||||
|     //find the location of the quad
 | ||||
|     if ( l != Quad::QL_ROOT ) | ||||
|       { //if it isn't the root node
 | ||||
|         mDepth = p->getDepth() + 1; | ||||
| 
 | ||||
|         mPosition = p->getPosition(); | ||||
|         mSideLength = p->getSideLength()/2; | ||||
| 
 | ||||
|         //horrible bit of code
 | ||||
|         switch (l) { | ||||
|         case Quad::QL_NE: | ||||
|             mPosition.x += mSideLength/2; | ||||
|             mPosition.y += mSideLength/2; | ||||
|             break; | ||||
|         case Quad::QL_NW: | ||||
|             mPosition.x -= mSideLength/2; | ||||
|             mPosition.y += mSideLength/2; | ||||
|             break; | ||||
|         case Quad::QL_SE: | ||||
|             mPosition.x += mSideLength/2; | ||||
|             mPosition.y -= mSideLength/2; | ||||
|             break; | ||||
|         case Quad::QL_SW: | ||||
|             mPosition.x -= mSideLength/2; | ||||
|             mPosition.y -= mSideLength/2; | ||||
|             break; | ||||
|         default: | ||||
|             break;//get rid of warning
 | ||||
|         } | ||||
| 
 | ||||
|         //set after positions have been retrived
 | ||||
|         TerrainHeightmap* d = mTerrain->getTerrainData(); | ||||
|         mMaxDepth = d->getMaxDepth(); | ||||
|         mHasData = d->hasData(this); | ||||
| 
 | ||||
|         if ( needSplit() ) //need to "semi" build terrain
 | ||||
|             split(); | ||||
|         else if ( mHasData ) | ||||
|           { | ||||
|             buildTerrain(); | ||||
|             getMesh()->justSplit(); | ||||
|           } | ||||
| 
 | ||||
|       } | ||||
|     else | ||||
|       { //assume it is root node, get data and possition
 | ||||
|         mDepth = 0; //root
 | ||||
|         mSideLength = mTerrain->getTerrainData()->getRootSideLength(); | ||||
|         mPosition = Point2<long>(0,0); //see Quad::getPosition as to why this is always 0
 | ||||
| 
 | ||||
|         mHasData = false; | ||||
| 
 | ||||
|         //always split, as this node never has data
 | ||||
|         split(); | ||||
|       } | ||||
|   } | ||||
| 
 | ||||
|   /**
 | ||||
|    * Destroyes the terrain mesh OR all children. | ||||
|    * If it needs to do both, there is a bug, as that would lead to a terrain mesh existing | ||||
|    * On more than one level in the same space. | ||||
|    * assert checks that there is only one or the other | ||||
|    */ | ||||
|   ~Quad() | ||||
|   { | ||||
|     destroyTerrain(); | ||||
|     for (size_t i = 0; i < NUM_CHILDREN; i++) | ||||
|       delete mChildren[i]; | ||||
|   } | ||||
| 
 | ||||
|   void update(Ogre::Real t) | ||||
|   { | ||||
|     if (  needSplit()  ) | ||||
|       { | ||||
|         split(); | ||||
|         return; | ||||
|       } | ||||
|     else if ( needUnsplit() ) | ||||
|       { | ||||
|         unsplit(); | ||||
|         return; | ||||
|       } | ||||
| 
 | ||||
|     //deal with updating the mesh.
 | ||||
|     if ( mTerrainMesh ) | ||||
|       mTerrainMesh->update(t); | ||||
|     else if ( hasChildren() ) | ||||
|       { | ||||
|         for (size_t i = 0; i < NUM_CHILDREN; ++i) { | ||||
|           assert( mChildren[i] ); | ||||
|           mChildren[i]->update(t); | ||||
|         } | ||||
|       } | ||||
|   } | ||||
| 
 | ||||
|   /**
 | ||||
|    * @return true if the node needs to be split. | ||||
|    * | ||||
|    * The issue with this is that at present this requires the mesh to | ||||
|    * be built to check. This is fine but it could lead to a lot more | ||||
|    * loading when teleporting | ||||
|    */ | ||||
|   bool needSplit() | ||||
|   { | ||||
|     if ( hasChildren() || | ||||
|          getDepth() == mMaxDepth || | ||||
|          !hasData() ) | ||||
|       return false; | ||||
|     return ( mTerrainMesh && mTerrainMesh->getSplitState() | ||||
|              == MeshInterface::SS_SPLIT ); | ||||
|   } | ||||
| 
 | ||||
|   /**
 | ||||
|    * The functions preforms work on the quad tree state. There is no | ||||
|    * requirement for this to be called every frame, but it is not | ||||
|    * thread safe due to interacting with OGRE The function checks if a | ||||
|    * split or unsplit is needed. It also calls update on all children | ||||
| 
 | ||||
|    * @param t the time since the last frame | ||||
|    */ | ||||
| 
 | ||||
|   /**
 | ||||
|    * Deletes the landscape, if there is any | ||||
|    *   Creates children, and either splits them, or creates landscape for them | ||||
|    */ | ||||
|   void split() | ||||
|   { | ||||
|     destroyTerrain(); | ||||
| 
 | ||||
|     //create a new terrain
 | ||||
|     for ( size_t i = 0; i < NUM_CHILDREN; ++i ) | ||||
|         mChildren[i] = new Quad((QuadLocation)i, this, mTerrain); | ||||
| 
 | ||||
|     assert(!needUnsplit()); | ||||
|   } | ||||
| 
 | ||||
|   /**
 | ||||
|    * @brief removes all children, and builds terrain on this level | ||||
|    */ | ||||
|   void unsplit() | ||||
|   { | ||||
|     //shouldn't unsplit 0 depth
 | ||||
|     assert(getDepth()); | ||||
| 
 | ||||
|     for ( size_t i = 0; i < NUM_CHILDREN; i++ )//{
 | ||||
|       delete mChildren[i]; | ||||
|     memset(mChildren, NULL, sizeof(Quad*)*NUM_CHILDREN); | ||||
| 
 | ||||
|     if ( mHasData ) { | ||||
|       buildTerrain(); | ||||
|       getMesh()->justUnsplit(); | ||||
|     } | ||||
|     assert(!needSplit()); | ||||
|   } | ||||
| 
 | ||||
|   /**
 | ||||
|    * @return true if the node needs to delete all its child nodes, and | ||||
|    * rebuild the terrain its level | ||||
|    */ | ||||
|   bool needUnsplit() | ||||
|   { | ||||
|     if ( hasChildren() && getDepth() ) { | ||||
|       for (size_t i=0;i< NUM_CHILDREN;i++) { | ||||
|         if ( mChildren[i]->hasData()  ) { | ||||
|           if ( !mChildren[i]->hasMesh() ) | ||||
|             return false; | ||||
|           else if ( mChildren[i]->getMesh()->getSplitState() != MeshInterface::SS_UNSPLIT) | ||||
|             return false; | ||||
|         } | ||||
|       } | ||||
|       return true; | ||||
|     } | ||||
| 
 | ||||
|     //get depth ensures the root doesn't try and unsplit
 | ||||
|     if ( getDepth() && !hasData() ) | ||||
|       return true; | ||||
| 
 | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   /**
 | ||||
|    * @brief constructs the terrain on this level. The terrain must on | ||||
|    * exist before hand | ||||
|    */ | ||||
|   void buildTerrain() | ||||
|   { | ||||
|     assert(hasData()); | ||||
|     assert(getMesh() == NULL); //the terrain sould not exist
 | ||||
|     mTerrainMesh = new MeshInterface(this, mTerrain); | ||||
|     mTerrainMesh->create(); | ||||
|   } | ||||
| 
 | ||||
|   /**
 | ||||
|    * @brief destroys the terrain. | ||||
|    */ | ||||
|   void destroyTerrain() | ||||
|   { | ||||
|     delete mTerrainMesh; | ||||
|     mTerrainMesh = NULL; | ||||
|   } | ||||
| 
 | ||||
|   /**
 | ||||
|    * @brief gets the position in relation to the root (always 0,0) | ||||
|    * @return the position as a long in a container holding the .x and .y vals | ||||
|    * | ||||
|    * This is called form the subnodes of this node, and the TerrainRenderable to work out what needs positiong where | ||||
|    *  This is a long (as opposed to a float) so it can be used in comparisons | ||||
|    * @todo typedef this, so it can be changed to int or long long (long64) | ||||
|    * | ||||
|    * The roots position is always 0. This is as the roots position is totally independent of the position | ||||
|    * that the terrain is rendered, as you can just move the root terrain node. | ||||
|    * In other words, it makes everything simpler | ||||
|    * | ||||
|    * The position of the quad is always taken from the center of the quad. Therefore, a top left quads location can be | ||||
|    * defined as: | ||||
|    * xpos = parent x pos - sidelength/2 | ||||
|    * ypos = parent y pos + sidelength/2 | ||||
|    * | ||||
|    * Where the side length is half the parents side length. | ||||
|    * The calcs are all handled in the consturctor (Quad::Quad) | ||||
|    */ | ||||
|   inline Point2<long> getPosition() const{ return mPosition; } | ||||
| 
 | ||||
|   /**
 | ||||
|    * @return the location of the quad. E.g. North West | ||||
|    */ | ||||
|   inline QuadLocation getLocation() const{ return mLocation; } | ||||
| 
 | ||||
|   /**
 | ||||
|    * @brief simply the length of one side of the current quad. | ||||
|    * @return the side length of the current quad | ||||
|    * | ||||
|    * As all quads are square, all sides are this length. | ||||
|    * Obviously this has to fit in sizeof(long) | ||||
|    */ | ||||
|   inline long getSideLength() const{ return mSideLength;} | ||||
| 
 | ||||
|   /**
 | ||||
|    * @brief The depth is how many splits have taken place since the root node. | ||||
|    * @return the depth of the current quad | ||||
|    * | ||||
|    * The root node has a depth 0. As this is the case, all of its children will have | ||||
|    * a depth of one. Simply depth = parent depth + 1 | ||||
|    * | ||||
|    * Set in the consturctor (Quad::Quad) | ||||
|    */ | ||||
|   inline int getDepth() const{return mDepth;} | ||||
| 
 | ||||
|   /**
 | ||||
|    * @return true if their is a terrain mesh alocated | ||||
|    */ | ||||
|   inline bool hasMesh() const{ return mTerrainMesh != 0; } | ||||
| 
 | ||||
|   /**
 | ||||
|    * @return true if there are any children | ||||
|    */ | ||||
|   inline bool hasChildren() const { return mChildren[0] != 0; } | ||||
| 
 | ||||
|   /**
 | ||||
|    * @return the mesh. 0 if there is no mesh | ||||
|    */ | ||||
|   MeshInterface* getMesh() | ||||
|   { | ||||
|     if ( mTerrainMesh == 0 ) return 0; | ||||
|     return mTerrainMesh; | ||||
|   } | ||||
| 
 | ||||
|   /**
 | ||||
|    * @brief checks if the quad has any data (i.e. a mesh avaible for rendering | ||||
|    */ | ||||
|   inline bool hasData() const{ return mHasData; } | ||||
| 
 | ||||
| private: | ||||
|   static const size_t NUM_CHILDREN = 4; | ||||
| 
 | ||||
|   Quad* mParent; /// this is the node above this. 0 if this is root
 | ||||
|   Quad* mChildren[4]; ///optionaly the children. Should be 0 if not exist
 | ||||
| 
 | ||||
|   Terrain* mTerrain; ///the pointer to the root terrain
 | ||||
|   MeshInterface* mTerrainMesh; ///the terrain mesh, only used if this is the bottom node
 | ||||
|   Quad::QuadLocation mLocation; ///the location within the quad (ne, se, nw, sw). See Quad::QuadLocation
 | ||||
|   Point2<long> mPosition; ///the center of the mesh. this is a long so can be used as comparison. See Quad::getPosition
 | ||||
|   long mSideLength; ///the length in units of one side of the quad. See Quad::getSideLength
 | ||||
|   int mDepth; ///depth of the node. See Quad::getDepth for more info
 | ||||
| 
 | ||||
|   bool mHasData; ///holds if there is terrain data about this quad
 | ||||
|   int mMaxDepth; ///the maxmium depth. Cached. This is not valid is mDepth == 0
 | ||||
| }; | ||||
| 
 | ||||
| //const size_t Quad::NUM_CHILDREN = 4;
 | ||||
							
								
								
									
										203
									
								
								terrain/cpp_quaddata.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										203
									
								
								terrain/cpp_quaddata.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,203 @@ | |||
| /**
 | ||||
|  * @brief gets a material for the quad data | ||||
|  */ | ||||
| class QuadMaterialGenerator | ||||
| { | ||||
| public: | ||||
|   virtual Ogre::MaterialPtr getMaterial(QuadData* qd) = 0; | ||||
|   /**
 | ||||
|    * @brief can't overload :( | ||||
|    */ | ||||
|   virtual Ogre::MaterialPtr getMaterialSegment(QuadData* qd,QuadSegment* qs) = 0; | ||||
| private: | ||||
|   /**
 | ||||
|    * @brief fix for gcc not putting the vtable anywhere unless there is a none inline, none virtual function | ||||
|    *   @remarks does absolutly nothing ofc | ||||
|    */ | ||||
|   void _vtablefix(); | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief abstract class used for getting LOD data. | ||||
|  * | ||||
|  * This class enables storing of data in whatever form is wanted | ||||
|  */ | ||||
| class TerrainHeightmap { | ||||
| public: | ||||
|   TerrainHeightmap() : mMaterialGenerator(0){ | ||||
|   } | ||||
|   virtual ~TerrainHeightmap() { | ||||
|     delete mMaterialGenerator; | ||||
|   } | ||||
| 
 | ||||
|   /**
 | ||||
|    * @brief called to load some data for the given quad | ||||
|    * @return NULL if the data doesn't exist | ||||
|    * | ||||
|    * The deleting of the memory is handled by TerrainMesh | ||||
|    */ | ||||
|   virtual QuadData* getData(Quad* q) = 0; | ||||
| 
 | ||||
|   /**
 | ||||
|    * @brief check if any data exists for this level | ||||
|    * @param q the quad that is asking for the data | ||||
|    */ | ||||
|   virtual bool hasData(Quad* q) = 0; | ||||
| 
 | ||||
|   /**
 | ||||
|    * @brief get the distance from one end of the map to the other | ||||
|    */ | ||||
|   virtual long getRootSideLength() = 0; | ||||
|   virtual int getMaxDepth() = 0; | ||||
| 
 | ||||
|   inline QuadMaterialGenerator* getMaterialGenerator(){ | ||||
|     assert(mMaterialGenerator); | ||||
|     return mMaterialGenerator; | ||||
|   } | ||||
| protected: | ||||
|   QuadMaterialGenerator* mMaterialGenerator; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief holds data that is passed to the mesh renderer. heights normals etc | ||||
|  * | ||||
|  * This needs a rework, as really the mesh renderer should accept just a set of verts | ||||
|  * Normals and indicies to allow us to pass optoizied meshes | ||||
|  */ | ||||
| class QuadData | ||||
| { | ||||
| public: | ||||
|   QuadData(TerrainHeightmap* p) | ||||
|     : mHeightmap(p) {} | ||||
| 
 | ||||
|   virtual ~QuadData() {} | ||||
| 
 | ||||
|   /**
 | ||||
|    * How many vertes wide the qd is. Usally 65. | ||||
|    * @todo cache? | ||||
|    */ | ||||
|   inline int getVertexWidth() { | ||||
|     return sqrt(getHeightsRef().size()); | ||||
|   } | ||||
| 
 | ||||
|   inline std::vector<float>& getHeightsRef() { | ||||
|     return mHeights; | ||||
|   } | ||||
|   inline float getVertex(int offset){ | ||||
|     return getHeightsRef().at(offset); | ||||
|   } | ||||
|   inline std::vector<char>& getNormalsRef() { | ||||
|     return mNormals; | ||||
|   } | ||||
|   inline float getNormal(int offset){ | ||||
|     return getNormalsRef().at(offset); | ||||
|   } | ||||
| 
 | ||||
|   /**
 | ||||
|    * @brief should be removed when we get around to developing optomized meshes | ||||
|    */ | ||||
|   inline int getVertexSeperation() { | ||||
|     return mVertexSeperation; | ||||
|   } | ||||
|   inline void setVertexSeperation(int vs) { | ||||
|     mVertexSeperation = vs; | ||||
|   } | ||||
| 
 | ||||
|   /**
 | ||||
|    * @brief lazy get function. Avoids creating materail until requested | ||||
|    */ | ||||
|   inline Ogre::MaterialPtr getMaterial() { | ||||
|     if ( mMaterial.isNull() ) | ||||
|       createMaterial(); | ||||
|     assert(!mMaterial.isNull()); | ||||
|     return mMaterial; | ||||
|   } | ||||
|   QuadMaterialGenerator* getMaterialGenerator() | ||||
|   { | ||||
|     return mHeightmap->getMaterialGenerator(); | ||||
|   } | ||||
| 
 | ||||
|   /**
 | ||||
|    * @brief gets the texture for the above quad | ||||
|    */ | ||||
|   inline const std::string& getParentTexture() const { | ||||
|     return mParentTexture; | ||||
|   } | ||||
|   inline bool hasParentTexture() const { | ||||
|     return (mParentTexture.length() > 0); | ||||
|   } | ||||
|   inline void setParentTexture(const std::string& c) { | ||||
|     mParentTexture = c; | ||||
|   } | ||||
| 
 | ||||
| protected: | ||||
|   void createMaterial() | ||||
|   { | ||||
|     mMaterial = getMaterialGenerator()->getMaterial(this); | ||||
|   } | ||||
|   TerrainHeightmap* mHeightmap; | ||||
|   Ogre::MaterialPtr mMaterial; | ||||
| 
 | ||||
|   std::string mParentTexture; | ||||
| 
 | ||||
|   int mVertexSeperation; | ||||
|   std::vector<float> mHeights; | ||||
|   std::vector<char> mNormals; | ||||
| }; | ||||
| 
 | ||||
| class MWQuadData : public QuadData | ||||
| { | ||||
| public: | ||||
|   typedef std::list<Ogre::ResourcePtr> ResourceList; | ||||
|   typedef std::list<Ogre::ResourcePtr>::const_iterator ResourceListCItr; | ||||
| 
 | ||||
|   MWQuadData(TerrainHeightmap* thm) : QuadData(thm) {} | ||||
| 
 | ||||
|   ~MWQuadData () | ||||
|   { | ||||
|     const ResourceListCItr end = mResources.end(); | ||||
|     for ( ResourceListCItr itr = mResources.begin(); itr != end; ++itr ) | ||||
|       (*itr)->getCreator()->remove((*itr)->getHandle()); | ||||
|   } | ||||
| 
 | ||||
|   /**
 | ||||
|    * @return a ref to the list of used resourcs | ||||
|    */ | ||||
|   inline ResourceList& getUsedResourcesRef() | ||||
|   { return mResources; } | ||||
| 
 | ||||
|   inline void setTexture(const std::string& t) | ||||
|   { mTexture = t; } | ||||
| 
 | ||||
|   inline std::string& getTexture() | ||||
|   { return mTexture; } | ||||
| 
 | ||||
|   inline std::vector<int>& getTextureIndexRef() | ||||
|   { return mTextureIndex; } | ||||
| 
 | ||||
| private: | ||||
|   ///Holds the resources used by the quad
 | ||||
|   ResourceList mResources; | ||||
| 
 | ||||
|   std::vector<int> mTextureIndex; ///holds index that corespond the the palette
 | ||||
|   std::string mTexture; ///The land texture. Mutally exclusive with the above
 | ||||
| 
 | ||||
|   friend class boost::serialization::access; | ||||
| 
 | ||||
|   /**
 | ||||
|    * Saves the data for the heights, noramals and texture indexies. | ||||
|    * Texture as well | ||||
|    */ | ||||
|   template<class Archive> | ||||
|   inline void serialize(Archive& ar, const unsigned int version){ | ||||
|     ar &mVertexSeperation; | ||||
|     ar &mHeights; | ||||
|     ar &mNormals; | ||||
|     ar &mParentTexture; | ||||
| 
 | ||||
|     ar &mTextureIndex; | ||||
|     ar &mTexture; | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| BOOST_CLASS_TRACKING(MWQuadData, boost::serialization::track_never); | ||||
							
								
								
									
										145
									
								
								terrain/cpp_quadsegment.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								terrain/cpp_quadsegment.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,145 @@ | |||
| /* Represents a segment of a quad. It can also represent a large segment
 | ||||
|  * | ||||
|  * this is needed for experimenting with splitting the lowest | ||||
|  * quad level into small segments for better culling.  Due to the | ||||
|  * intented design of using optomized meshes, this should not work on | ||||
|  * optomized meshes, as there is no easy way to segment the data | ||||
|  * | ||||
|  * This uses floating point numbers to define the areas of the | ||||
|  * quads. There may be issues due to accuracy if we go to small but it | ||||
|  * should be caught in debug mode | ||||
|  */ | ||||
| #define QSDEBUG | ||||
| 
 | ||||
| class QuadSegment | ||||
| { | ||||
| public: | ||||
|   /**
 | ||||
|    * @param qd the parent quad data | ||||
|    * @param size the proportion of the total size that the segment is. Must be greater than 0, but less than or equal to 1 | ||||
|    * @param x,y the start positions of the segment | ||||
|    */ | ||||
|   QuadSegment(QuadData* qd, float size = 1, float x = 0, float y = 0) | ||||
|     : mQuadData(qd), | ||||
|       mSegmentSize(size), | ||||
|       mX(x), | ||||
|       mY(y) | ||||
|   { | ||||
|     assert(qd); | ||||
|     assert(size>0&&size<=1); | ||||
|     assert(mY>=0&&mY<=1); | ||||
|     assert(mX>=0&&mY<=1); | ||||
| 
 | ||||
| #ifdef QSDEBUG | ||||
|     { | ||||
|       //check sizes
 | ||||
|       const float qw = mQuadData->getVertexWidth()-1; | ||||
|       const float fsw = qw*size; | ||||
|       const int isw = (int)fsw; | ||||
|       assert(fsw==isw); | ||||
|     } | ||||
| #endif | ||||
| 
 | ||||
|     //precalc offsets, as getVertex/getNormal get called a lot (1000s of times)
 | ||||
|     computeOffsets(); | ||||
|   } | ||||
| 
 | ||||
|   /**
 | ||||
|    * Gets how many vertexes wide this quad segment is. Should always be 2^n+1 | ||||
|    * @return the vertex width of this quad segment | ||||
|    */ | ||||
|   int getVertexWidth() | ||||
|   { | ||||
|     return (mQuadData->getVertexWidth()-1)*mSegmentSize+1; | ||||
|   } | ||||
| 
 | ||||
|   /**
 | ||||
|    * @brief gets a vertex assuming that x = 0, y = 0 addresses the start of the quad | ||||
|    */ | ||||
|   float getVertex(int x, int y) | ||||
|   { | ||||
| #ifdef QSDEBUG | ||||
|     { | ||||
|       const int vw = getVertexWidth(); | ||||
|       assert(x<vw); | ||||
|     } | ||||
| #endif | ||||
|     return mQuadData->getVertex((mYOffset + y)*mQuadData->getVertexWidth()+(mXOffset+x)); | ||||
|   } | ||||
| 
 | ||||
|   float getNormal(int x, int y, int z) | ||||
|   { | ||||
|     assert(z>=0&&z<3); | ||||
|     return mQuadData->getNormal(((mYOffset + y)*mQuadData->getVertexWidth()+(mXOffset+x))*3+z); | ||||
|   } | ||||
| 
 | ||||
|   /**
 | ||||
|    * @brief lazy function for getting a material | ||||
|    */ | ||||
|   Ogre::MaterialPtr getMaterial() | ||||
|   { | ||||
|     if ( mMaterial.isNull() ) | ||||
|       createMaterial(); | ||||
|     assert(!mMaterial.isNull()); | ||||
|     return mMaterial; | ||||
|   } | ||||
| 
 | ||||
|   void createMaterial() | ||||
|   { | ||||
|     assert(mSegmentSize>0); | ||||
|     if ( mSegmentSize == 1 ) //we can use the top level material
 | ||||
|       mMaterial = mQuadData->getMaterial(); | ||||
|     else //generate a material spesificly for this
 | ||||
|       mMaterial = mQuadData->getMaterialGenerator()-> | ||||
|         getMaterialSegment(mQuadData,this); | ||||
|     assert(!mMaterial.isNull()); | ||||
|   } | ||||
| 
 | ||||
|   inline bool hasParentTexture() const{ | ||||
|     return mQuadData->hasParentTexture(); | ||||
|   } | ||||
|   inline const std::string& getParentTexture() const{ | ||||
|     return mQuadData->getParentTexture(); | ||||
|   } | ||||
|   inline int getVertexSeperation(){ | ||||
|     return mQuadData->getVertexSeperation(); | ||||
|   } | ||||
| 
 | ||||
|   inline float getSegmentSize(){ | ||||
|     return mSegmentSize; | ||||
|   } | ||||
|   inline float getStartX(){ | ||||
|     return mX; | ||||
|   } | ||||
|   inline float getStartY(){ | ||||
|     return mY; | ||||
|   } | ||||
| 
 | ||||
| private: | ||||
|   int computeOffset(float x) | ||||
|   { | ||||
| #ifdef QSDEBUG | ||||
|     { | ||||
|       //check it is a valid position
 | ||||
|       const int start = (mQuadData->getVertexWidth()-1)*x; | ||||
|       const int vw = getVertexWidth()-1; | ||||
|       assert(vw>0); | ||||
|       assert((start%vw)==0); | ||||
|     } | ||||
| #endif | ||||
|     return float((mQuadData->getVertexWidth()-1))*x; | ||||
|   } | ||||
| 
 | ||||
|   void computeOffsets() | ||||
|   { | ||||
|     mXOffset = computeOffset(mX); | ||||
|     mYOffset = computeOffset(mY); | ||||
|   } | ||||
| 
 | ||||
|   QuadData* mQuadData; | ||||
|   float mSegmentSize; | ||||
|   float mX, mY; | ||||
|   Ogre::MaterialPtr mMaterial; | ||||
| 
 | ||||
|   int mXOffset, mYOffset; | ||||
| }; | ||||
							
								
								
									
										151
									
								
								terrain/cpp_terrain.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								terrain/cpp_terrain.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,151 @@ | |||
| /*
 | ||||
|   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/ .
 | ||||
| 
 | ||||
| */ | ||||
| 
 | ||||
| // Shader names
 | ||||
| #define MORPH_VERTEX_PROGRAM "mw_terrain_VS" | ||||
| #define FADE_FRAGMENT_PROGRAM "mw_terrain_texfade_FP" | ||||
| 
 | ||||
| // Directories
 | ||||
| #define TEXTURE_OUTPUT "cache/terrain/" | ||||
| #define TERRAIN_OUTPUT "cache/terrain/landscape" | ||||
| 
 | ||||
| ///no texture assigned
 | ||||
| const int LAND_LTEX_NONE = 0; | ||||
| 
 | ||||
| ///the default land height that it defaults to in the TESCS
 | ||||
| const int LAND_DEFAULT_HEIGHT = -2048; | ||||
| 
 | ||||
| ///how many verts wide (and long) the cell is
 | ||||
| const int LAND_VERT_WIDTH = 65; | ||||
| 
 | ||||
| ///Number of verts that make up a cell
 | ||||
| const int LAND_NUM_VERTS = LAND_VERT_WIDTH*LAND_VERT_WIDTH; | ||||
| 
 | ||||
| const int LAND_LTEX_WIDTH = 16; | ||||
| const int LAND_NUM_LTEX = LAND_LTEX_WIDTH*LAND_LTEX_WIDTH; | ||||
| 
 | ||||
| //stops it crashing, now it leaks.
 | ||||
| #define ENABLED_CRASHING 0 | ||||
| 
 | ||||
| class Quad; | ||||
| class QuadData; | ||||
| class QuadSegment; | ||||
| class Terrain; | ||||
| 
 | ||||
| // Prerequisites
 | ||||
| #include <vector> | ||||
| #include <map> | ||||
| #include <fstream> | ||||
| #include <string> | ||||
| #include <list> | ||||
| #include <algorithm> | ||||
| #include <boost/shared_ptr.hpp> | ||||
| #include <boost/archive/binary_iarchive.hpp> | ||||
| #include <boost/archive/binary_oarchive.hpp> | ||||
| #include <boost/serialization/string.hpp> | ||||
| #include <boost/serialization/vector.hpp> | ||||
| #include <boost/serialization/map.hpp> | ||||
| 
 | ||||
| // For generation
 | ||||
| #include "cpp_esm.cpp" | ||||
| #include "cpp_landdata.cpp" | ||||
| #include "cpp_quaddata.cpp" | ||||
| #include "cpp_point2.cpp" | ||||
| #include "cpp_materialgen.cpp" | ||||
| #include "cpp_index.cpp" | ||||
| #include "cpp_palette.cpp" | ||||
| #include "cpp_generator.cpp" | ||||
| 
 | ||||
| // For rendering
 | ||||
| #include "cpp_quadsegment.cpp" | ||||
| #include "cpp_baseland.cpp" | ||||
| #include "cpp_mwquadmatgen.cpp" | ||||
| 
 | ||||
| // These depend on each other, so our usual hackery won't work. We
 | ||||
| // need the header files first.
 | ||||
| #include "cpp_terrainmesh.h" | ||||
| #include "cpp_meshinterface.h" | ||||
| #include "cpp_terraincls.h" | ||||
| 
 | ||||
| #include "cpp_quad.cpp" | ||||
| 
 | ||||
| #include "cpp_terraincls.cpp" | ||||
| #include "cpp_terrainmesh.cpp" | ||||
| #include "cpp_meshinterface.cpp" | ||||
| 
 | ||||
| #include "cpp_mwheightmap.cpp" | ||||
| #include "cpp_framelistener.cpp" | ||||
| 
 | ||||
| TerrainFrameListener terrainListener; | ||||
| 
 | ||||
| extern "C" void d_superman(); | ||||
| 
 | ||||
| // Set up the rendering system
 | ||||
| extern "C" void terr_setupRendering() | ||||
| { | ||||
|   // Add the terrain directory
 | ||||
|   ResourceGroupManager::getSingleton(). | ||||
|     addResourceLocation(TEXTURE_OUTPUT, "FileSystem", "General"); | ||||
| 
 | ||||
|   // Set up the terrain frame listener
 | ||||
|   terrainListener.setup(); | ||||
| 
 | ||||
|   // Enter superman mode
 | ||||
|   mCamera->setFarClipDistance(32*8192); | ||||
|   d_superman(); | ||||
| } | ||||
| 
 | ||||
| // Generate all cached data.
 | ||||
| extern "C" void terr_genData() | ||||
| { | ||||
|   Ogre::Root::getSingleton().renderOneFrame(); | ||||
| 
 | ||||
|   Generator mhm(TERRAIN_OUTPUT); | ||||
|   { | ||||
|     ESM esm; | ||||
| 
 | ||||
|     const std::string fn("data/Morrowind.esm"); | ||||
| 
 | ||||
|     esm.addRecordType("LAND", "INTV"); | ||||
|     esm.addRecordType("LTEX", "INTV"); | ||||
| 
 | ||||
|     esm.loadFile(fn); | ||||
|     RecordListPtr land = esm.getRecordsByType("LAND"); | ||||
|     for ( RecordListItr itr = land->begin(); itr != land->end(); ++itr ) | ||||
|       mhm.addLandData(*itr, fn); | ||||
| 
 | ||||
|     RecordListPtr ltex = esm.getRecordsByType("LTEX"); | ||||
|     for ( RecordListItr itr = ltex->begin(); itr != ltex->end(); ++itr ) | ||||
|       mhm.addLandTextureData(*itr, fn); | ||||
|   } | ||||
| 
 | ||||
|   mhm.beginGeneration(); | ||||
| 
 | ||||
|   mhm.generateLODLevel(6, true, 1024); | ||||
|   mhm.generateLODLevel(5, true, 512); | ||||
|   mhm.generateLODLevel(4, true, 256); | ||||
|   mhm.generateLODLevel(3, true, 256); | ||||
|   mhm.generateLODLevel(2, true, 256); | ||||
|   mhm.generateLODLevel(1, false, 128); | ||||
| 
 | ||||
|   mhm.endGeneration(); | ||||
| } | ||||
							
								
								
									
										45
									
								
								terrain/cpp_terraincls.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								terrain/cpp_terraincls.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,45 @@ | |||
| Terrain::Terrain(TerrainHeightmap* d, | ||||
|                  Ogre::SceneNode* r) | ||||
|   :  mTerrainData(d), | ||||
|      mTerrainSceneNode(r), | ||||
|      mQuadRoot(0), | ||||
|      mQuadCreateFunction(0), | ||||
|      mQuadDestroyFunction(0), | ||||
|      mMorphingEnabled(true), | ||||
|      mTextureFadingEnabled(true), | ||||
|      mBaseLand(r) | ||||
| { | ||||
| 
 | ||||
| } | ||||
| //----------------------------------------------
 | ||||
| Terrain::~Terrain(){ | ||||
|   delete mQuadRoot; | ||||
| } | ||||
| //----------------------------------------------
 | ||||
| void Terrain::create(){ | ||||
|   mQuadRoot = new Quad(Quad::QL_ROOT, 0, this); //cleaned in Terrain::~Terrain
 | ||||
| } | ||||
| //----------------------------------------------
 | ||||
| void Terrain::update(Ogre::Real t){ | ||||
|   assert(mQuadRoot); | ||||
|   mQuadRoot->update(t); | ||||
|   mBaseLand.update(); | ||||
| } | ||||
| //----------------------------------------------
 | ||||
| void Terrain::_quadCreated(QuadData* qd){ | ||||
|   if ( mQuadCreateFunction ) (*mQuadCreateFunction)(qd); | ||||
| } | ||||
| //----------------------------------------------
 | ||||
| void Terrain::_quadDestroyed(QuadData* qd){ | ||||
|   if ( mQuadDestroyFunction ) (*mQuadDestroyFunction)(qd); | ||||
| } | ||||
| //----------------------------------------------
 | ||||
| int Terrain::getMaxDepth(){ | ||||
|   return mTerrainData->getMaxDepth(); | ||||
| } | ||||
| //----------------------------------------------
 | ||||
| void Terrain::reload(){ | ||||
|   delete mQuadRoot; | ||||
|   mQuadRoot = new Quad(Quad::QL_ROOT, 0, this); | ||||
| } | ||||
| //----------------------------------------------
 | ||||
							
								
								
									
										152
									
								
								terrain/cpp_terraincls.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								terrain/cpp_terraincls.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,152 @@ | |||
| class Terrain | ||||
| { | ||||
| public: | ||||
|   /**
 | ||||
|    * @brief inits vars and creates quad tree | ||||
|    * @param d the heightmap that contains the data | ||||
|    * @param s the current scne manager | ||||
|    * @param r the root scene node that all terrain nodes are barsed off | ||||
|    * @param c the camera that the poistion data is taken from | ||||
|    * | ||||
|    * The terrain is create in the constructor. | ||||
|    * @todo change quad root creation to create/destroy funcs? | ||||
|    */ | ||||
|   explicit Terrain(TerrainHeightmap* d, Ogre::SceneNode* r); | ||||
|   /**
 | ||||
|    * @brief deletes the quad tree | ||||
|    */ | ||||
|   ~Terrain(); | ||||
| 
 | ||||
|   /**
 | ||||
|    * @brief creates the quad tree | ||||
|    * @remarks This must be called before any call to upate(). We cannot call it from the constructor as options | ||||
|    *   may not have been set yet | ||||
|    */ | ||||
|   void create(); | ||||
| 
 | ||||
|   /**
 | ||||
|    * @brief recreates the quad tree by destroying everything and starting again. | ||||
|    * @remarks this is very slow, as it drops all created alpha maps and meshes. | ||||
|    * @todo check this works | ||||
|    */ | ||||
|   void reload(); | ||||
| 
 | ||||
|   /**
 | ||||
|    * @brief sets the scene node that all of the terrain nodes are based off | ||||
|    * @param r the scene node to use for terrain | ||||
|    */ | ||||
|   inline void setTerrainSceneNode(Ogre::SceneNode* r){ mTerrainSceneNode = r; } | ||||
| 
 | ||||
|   /**
 | ||||
|    * @return the scene node that should be used as the root of the terrain | ||||
|    * | ||||
|    * This is mainly used by the quad tree | ||||
|    */ | ||||
|   inline Ogre::SceneNode* getTerrainSceneNode(){return mTerrainSceneNode;} | ||||
| 
 | ||||
|   /**
 | ||||
|    * @brief updates the quad tree, splittig and unsplitting where needed. | ||||
|    * | ||||
|    * There is no requirement for this to be called every frame | ||||
|    */ | ||||
|   void update(Ogre::Real t); | ||||
| 
 | ||||
|   /**
 | ||||
|    * @return the heightmap data | ||||
|    */ | ||||
|   inline TerrainHeightmap* getTerrainData(){ return mTerrainData; } | ||||
| 
 | ||||
|   /**
 | ||||
|    * @brief handles the actions to take on the creation of a terrain mesh | ||||
|    * @param qd the quad data. It is valid until the same variable is passed to _quadDestroyed | ||||
|    */ | ||||
|   void _quadCreated(QuadData* qd); | ||||
| 
 | ||||
|   /**
 | ||||
|    * @brief The quad as defined by the quad data qd has been destroyed | ||||
|    * @param qd the quad that has been destroyed. This is only valid for this function. Is is deleted just after | ||||
|    */ | ||||
|   void _quadDestroyed(QuadData* qd); | ||||
| 
 | ||||
|   /**
 | ||||
|    * @brief sets the function to be used in the callback when a quad is created | ||||
|    * @param f the function to use. Set to null to disable callbacks | ||||
|    */ | ||||
|   inline void setQuadCreateFunction(void (*f)(QuadData*)){ | ||||
|     mQuadCreateFunction = f; | ||||
|   } | ||||
|   /**
 | ||||
|    * @brief sets the function to be used in the callback when a quad is destroyed | ||||
|    * @param f the function to use. Set to null to disable callbacks | ||||
|    */ | ||||
|   inline void setQuadDestroyFunction(void (*f)(QuadData*)){ | ||||
|     mQuadDestroyFunction = f; | ||||
|   } | ||||
| 
 | ||||
|   /**
 | ||||
|    * @brief time in seconds to morph to full detail after an unsplit. | ||||
|    */ | ||||
|   inline Ogre::Real getMorphSpeed(){return 1.0f;} | ||||
|   /**
 | ||||
|    * @brief the time in seconds it takes for the texture to fade to the higher detail | ||||
|    */ | ||||
|   inline Ogre::Real getTextureFadeSpeed(){ return 2.0f;} | ||||
| 
 | ||||
|   /**
 | ||||
|    * @return the maximum depth of the quad tree | ||||
|    * @remarks this is used by the terrain renderable for adding morhping to textures. It doesn't want to happen | ||||
|    *   on the lowest detail | ||||
|    */ | ||||
|   int getMaxDepth(); | ||||
| 
 | ||||
|   /**
 | ||||
|    * @brief disables/enables morphing | ||||
|    * @remarks This will not happen instantly unless reload() is called | ||||
|    */ | ||||
| 
 | ||||
|   inline void setMorphingEnabled(bool enabled){ | ||||
|     mMorphingEnabled = enabled; | ||||
| 
 | ||||
|   } | ||||
|   /**
 | ||||
|    * @return true if morphing is enabled | ||||
|    */ | ||||
|   inline bool isMorhpingEnabled() const{ | ||||
|     return mMorphingEnabled; | ||||
|   } | ||||
| 
 | ||||
|   /**
 | ||||
|    * @brief disables/enables texture fading. | ||||
|    * @remarks This will not happen instantly unless reload() is called. Morphing is required atm | ||||
|    */ | ||||
|   inline void setTextureFadingEnabled(bool enabled){ | ||||
|     if ( enabled && !mMorphingEnabled ) | ||||
|       OGRE_EXCEPT(Ogre::Exception::ERR_NOT_IMPLEMENTED, "Cannot have fading but not morphing active", "Terrain::setTextureFadingEnabled"); | ||||
|     mTextureFadingEnabled = enabled; | ||||
|   } | ||||
|   /**
 | ||||
|    * @return true if texture fading is enabled | ||||
|    */ | ||||
|   inline bool isTextureFadingEnabled() const{ | ||||
|     return mTextureFadingEnabled; | ||||
|   } | ||||
| protected: | ||||
|   TerrainHeightmap* mTerrainData; | ||||
| 
 | ||||
|   /// the scenenode that every other node is decended from. This
 | ||||
|   /// should be surplied by the user
 | ||||
|   Ogre::SceneNode* mTerrainSceneNode; | ||||
| 
 | ||||
|   ///the root node for all the quads.
 | ||||
|   Quad* mQuadRoot; | ||||
| 
 | ||||
|   ///quad callback function
 | ||||
|   void (*mQuadCreateFunction)(QuadData*); | ||||
|   ///quad callback function
 | ||||
|   void (*mQuadDestroyFunction)(QuadData*); | ||||
| 
 | ||||
|   bool mMorphingEnabled; | ||||
|   bool mTextureFadingEnabled; | ||||
| 
 | ||||
|   BaseLand mBaseLand; | ||||
| }; | ||||
							
								
								
									
										478
									
								
								terrain/cpp_terrainmesh.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										478
									
								
								terrain/cpp_terrainmesh.cpp
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,478 @@ | |||
| //----------------------------------------------
 | ||||
| TerrainRenderable::TerrainRenderable(Terrain* t, QuadSegment* qs,int width, int depth, bool skirts) : | ||||
|         Ogre::Renderable(), | ||||
|         Ogre::MovableObject(), | ||||
|         mWidth(width), | ||||
|         mUseSkirts(skirts), | ||||
|         mBuilt(false), | ||||
|         mDepth(depth), | ||||
|         mSegment(qs), | ||||
|         mTerrain(t), | ||||
|         mVertexes(0), | ||||
|         mIndices(0), | ||||
|         mLODMorphFactor(0), | ||||
|         mTextureFadeFactor(0), | ||||
|         mMin(30000), | ||||
|         mMax(-30000), | ||||
|         mExtraMorphAmount(0), | ||||
|         mHasFadePass(false) {} | ||||
| //----------------------------------------------
 | ||||
| TerrainRenderable::~TerrainRenderable() { | ||||
|     destroy(); | ||||
| } | ||||
| //----------------------------------------------
 | ||||
| void TerrainRenderable::create(Ogre::SceneNode* sn) { | ||||
|     using namespace Ogre; | ||||
| 
 | ||||
|     if ( mBuilt ) return; | ||||
| 
 | ||||
|     createVertexBuffer(); | ||||
|     calculateVetexValues(); | ||||
|     calculateIndexValues(); | ||||
|     setBounds(); | ||||
| 
 | ||||
|     sn->attachObject(this); | ||||
| 
 | ||||
|     mMaterial = mSegment->getMaterial(); | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     if ( mTerrain->isMorhpingEnabled() && mDepth != mTerrain->getMaxDepth() ) { | ||||
|         Ogre::Technique* tech = getMaterial()->getTechnique(0); | ||||
|         for ( size_t i = 0; i < tech->getNumPasses(); ++i ) { | ||||
|             assert(mTerrain->isMorhpingEnabled()); | ||||
|             tech->getPass(i)->setVertexProgram(MORPH_VERTEX_PROGRAM); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if ( mTerrain->isMorhpingEnabled() ) | ||||
|         calculateDeltaValues(); | ||||
| 
 | ||||
|     mBuilt = true; | ||||
| } | ||||
| //----------------------------------------------
 | ||||
| void TerrainRenderable::destroy() { | ||||
|     if ( !mBuilt ) return; | ||||
| 
 | ||||
|     //deleting null values is fine iirc
 | ||||
|     delete mIndices; | ||||
| 
 | ||||
| #   if ENABLED_CRASHING == 1 | ||||
|     { | ||||
|         delete mVertexes; | ||||
|     } | ||||
| #   else | ||||
|     { | ||||
|         if ( mDepth != mTerrain->getMaxDepth() ){ | ||||
|            delete mVertexes; | ||||
|         } | ||||
|     } | ||||
| #   endif | ||||
| 
 | ||||
|     mBuilt = false; | ||||
| } | ||||
| //----------------------------------------------
 | ||||
| void TerrainRenderable::update(Ogre::Real time, Ogre::Real camDist, Ogre::Real usplitDist, Ogre::Real morphDist) { | ||||
|     //if ( USE_MORPH ){
 | ||||
| 
 | ||||
|     //as aprocesh mUnsplitDistance, lower detail
 | ||||
|     if ( camDist > morphDist && mDepth > 1 ) { | ||||
|         mLODMorphFactor = 1 - (usplitDist - camDist)/(usplitDist-morphDist); | ||||
|     } else | ||||
|         mLODMorphFactor = 0; | ||||
|     mTextureFadeFactor = mLODMorphFactor; | ||||
| 
 | ||||
| 
 | ||||
|     //on an split, it sets the extra morph amount to 1, we then ensure this ends up at 0... slowly
 | ||||
|     if ( mExtraMorphAmount > 0 ) { | ||||
|         mLODMorphFactor += mExtraMorphAmount; | ||||
|         mExtraMorphAmount -= (time/mTerrain->getMorphSpeed()); //decrease slowly
 | ||||
|     } | ||||
|     if ( mExtraFadeAmount > 0 ) { | ||||
|         mTextureFadeFactor += mExtraFadeAmount; | ||||
|         mExtraFadeAmount -= (time/mTerrain->getTextureFadeSpeed()); | ||||
|     } | ||||
| 
 | ||||
|     //Ensure within valid bounds
 | ||||
|     if ( mLODMorphFactor > 1 ) | ||||
|         mLODMorphFactor = 1; | ||||
|     else if ( mLODMorphFactor < 0 ) | ||||
|         mLODMorphFactor = 0; | ||||
| 
 | ||||
|     if ( mTextureFadeFactor > 1 ) | ||||
|         mTextureFadeFactor = 1; | ||||
|     else if ( mTextureFadeFactor < 0 ) | ||||
|         mTextureFadeFactor = 0; | ||||
| 
 | ||||
|     //}
 | ||||
| 
 | ||||
| 
 | ||||
|     //remove pass. Keep outside in case terrain fading is removed while it is active
 | ||||
|     if ( mHasFadePass && mTextureFadeFactor == 0 ) { | ||||
|         removeFadePass(); | ||||
|     } else if ( mTerrain->isTextureFadingEnabled() && | ||||
|                 !mHasFadePass && | ||||
|                 mTextureFadeFactor > 0 && | ||||
|                 mSegment->hasParentTexture() ) { | ||||
|         addFadePass(); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| //----------------------------------------------
 | ||||
| void TerrainRenderable::addFadePass() { | ||||
|     assert(mHasFadePass==false); | ||||
| 
 | ||||
|     if ( mDepth == mTerrain->getMaxDepth() ) return; | ||||
| 
 | ||||
| 
 | ||||
|     mHasFadePass = true; | ||||
|     Ogre::MaterialPtr mat = getMaterial(); | ||||
|     Ogre::Pass* newPass = mat->getTechnique(0)->createPass(); | ||||
|     newPass->setSceneBlending(Ogre::SBF_SOURCE_ALPHA, Ogre::SBF_ONE_MINUS_SOURCE_ALPHA); | ||||
| 
 | ||||
|     //set fragment program
 | ||||
|     assert(mTerrain->isTextureFadingEnabled()); | ||||
|     newPass->setFragmentProgram(FADE_FRAGMENT_PROGRAM); | ||||
| 
 | ||||
|     if ( mTerrain->isMorhpingEnabled() && mDepth != mTerrain->getMaxDepth() ) { | ||||
|         assert(mTerrain->isMorhpingEnabled()); | ||||
|         newPass->setVertexProgram(MORPH_VERTEX_PROGRAM); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     //set texture to be used
 | ||||
|     newPass->createTextureUnitState(mSegment->getParentTexture(), 1); | ||||
| } | ||||
| //----------------------------------------------
 | ||||
| void TerrainRenderable::removeFadePass() { | ||||
|     assert(mHasFadePass==true); | ||||
|     mHasFadePass = false; | ||||
|     Ogre::MaterialPtr mat = getMaterial(); | ||||
|     Ogre::Technique* tech = mat->getTechnique(0); | ||||
| 
 | ||||
|     tech->removePass(tech->getNumPasses()-1); | ||||
| } | ||||
| //----------------------------------------------
 | ||||
| void TerrainRenderable::justSplit() { | ||||
|     mExtraMorphAmount = 1; | ||||
|     mLODMorphFactor = 1; | ||||
|     mTextureFadeFactor = 1; | ||||
|     mExtraFadeAmount = 1; | ||||
| 
 | ||||
|     if ( mTerrain->isTextureFadingEnabled() && mSegment->hasParentTexture() ) | ||||
|         addFadePass(); | ||||
| } | ||||
| 
 | ||||
| //----------------------------------------------
 | ||||
| void TerrainRenderable::_updateCustomGpuParameter( | ||||
|     const GpuProgramParameters::AutoConstantEntry& constantEntry, | ||||
|     GpuProgramParameters* params) const { | ||||
|     using namespace Ogre; | ||||
|     if (constantEntry.data == MORPH_CUSTOM_PARAM_ID) | ||||
|         params->_writeRawConstant(constantEntry.physicalIndex, mLODMorphFactor); | ||||
|     else if ( constantEntry.data == FADE_CUSTOM_PARAM_ID ) | ||||
|         params->_writeRawConstant(constantEntry.physicalIndex, mTextureFadeFactor); | ||||
|     else | ||||
|         Renderable::_updateCustomGpuParameter(constantEntry, params); | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| //----------------------------------------------
 | ||||
| float TerrainRenderable::getVertexHeight(int x, int y) { | ||||
|     return mSegment->getVertex(x,y); | ||||
| } | ||||
| //----------------------------------------------
 | ||||
| Ogre::Vector3 TerrainRenderable::getVertexPosition(int x, int y) { | ||||
|     return Ogre::Vector3(x*mSegment->getVertexSeperation(), getVertexHeight(x,y) , y*mSegment->getVertexSeperation()); | ||||
| } | ||||
| //----------------------------------------------
 | ||||
| void TerrainRenderable::calculateVetexValues() { | ||||
|     using namespace Ogre; | ||||
| 
 | ||||
|     //get the texture offsets for the higher uv
 | ||||
|     float xUVOffset = 0; | ||||
|     float yUVOffset = 0; | ||||
| 
 | ||||
|     if ( mTerrain->isTextureFadingEnabled() ) { | ||||
|         assert(0); | ||||
|     } | ||||
|     /*
 | ||||
|         switch (mInterface->getLocation()) { | ||||
|         case Quad::QL_NW : | ||||
|             yUVOffset = 32.0f/64.0f; | ||||
|             break; | ||||
|         case Quad::QL_NE: | ||||
|             yUVOffset = 32.0f/64.0f; | ||||
|             xUVOffset = 32.0f/64.0f; | ||||
|             break; | ||||
|         case Quad::QL_SE: | ||||
|             xUVOffset = 32.0f/64.0f; | ||||
|             break; | ||||
|         default: | ||||
|             break; | ||||
|         } | ||||
|         */ | ||||
| 
 | ||||
|     int start = 0; | ||||
|     int end = mWidth; | ||||
| 
 | ||||
|     if ( mUseSkirts ) { | ||||
|         --start; | ||||
|         ++end; | ||||
|     } | ||||
| 
 | ||||
|     float* verts = static_cast<float*>(mMainBuffer->lock(HardwareBuffer::HBL_DISCARD)); | ||||
|     for ( int y = start; y < end; y++ ) { | ||||
|         for ( int x = start; x < end; x++ ) { | ||||
| 
 | ||||
|             //the skirts
 | ||||
|             if ( y < 0 || y > (mWidth-1) || x < 0 || x > (mWidth-1) ) { | ||||
| 
 | ||||
|                 if ( x < 0 )		*verts++ = 0; | ||||
|                 else if ( x > (mWidth-1) )	*verts++ = (mWidth-1)*mSegment->getVertexSeperation(); | ||||
|                 else				*verts++ = x*mSegment->getVertexSeperation(); | ||||
| 
 | ||||
|                 *verts++ = -4096; //2048 below base sea floor height
 | ||||
| 
 | ||||
|                 if ( y < 0 )		        *verts++ = 0; | ||||
|                 else if ( y > (mWidth-1) )	*verts++ = (mWidth-1)*mSegment->getVertexSeperation(); | ||||
|                 else        				*verts++ = y*mSegment->getVertexSeperation(); | ||||
| 
 | ||||
| 
 | ||||
|                 for ( Ogre::uint i = 0; i < 3; i++ ) | ||||
|                     *verts++ = 0; | ||||
| 
 | ||||
|                 float u = (float)(x) / (mWidth-1); | ||||
|                 float v = (float)(y) / (mWidth-1); | ||||
| 
 | ||||
|                 //clamped, so shouldn't matter if > 1
 | ||||
| 
 | ||||
|                 *verts++ = u; | ||||
|                 *verts++ = v; | ||||
| 
 | ||||
|                 if ( mTerrain->isTextureFadingEnabled() ) { | ||||
|                     *verts++ = u; | ||||
|                     *verts++ = v; | ||||
|                 } | ||||
|             } else { | ||||
| 
 | ||||
|                 assert(y*mWidth+x>=0&&y*mWidth+x<mWidth*mWidth); | ||||
| 
 | ||||
|                 //verts
 | ||||
|                 *verts++ = x*mSegment->getVertexSeperation(); | ||||
|                 *verts++ = getVertexHeight(x,y); | ||||
|                 *verts++ = y*mSegment->getVertexSeperation(); | ||||
| 
 | ||||
|                 mMax = std::max(mMax, getVertexHeight(x,y)); | ||||
|                 mMin = std::min(mMin, getVertexHeight(x,y)); | ||||
| 
 | ||||
|                 //normals
 | ||||
|                 for ( Ogre::uint i = 0; i < 3; i++ ) | ||||
|                     *verts++ = mSegment->getNormal(x,y,i); | ||||
|                 //*verts++ = mInterface->getNormal((y*mWidth+x)*3+i);
 | ||||
| 
 | ||||
|                 const float u = (float)(x) / (mWidth-1); | ||||
|                 const float v = (float)(y) / (mWidth-1); | ||||
|                 assert(u>=0&&v>=0); | ||||
|                 assert(u<=1&&v<=1); | ||||
| 
 | ||||
|                 *verts++ = u; | ||||
|                 *verts++ = v; | ||||
| 
 | ||||
|                 if ( mTerrain->isTextureFadingEnabled() ) { | ||||
|                     *verts++ = u/2.0f + xUVOffset; | ||||
|                     *verts++ = v/2.0f + yUVOffset; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     mMainBuffer->unlock(); | ||||
| } | ||||
| //----------------------------------------------
 | ||||
| void TerrainRenderable::setBounds() { | ||||
|     mBounds.setExtents(0,mMin,0, | ||||
|                        (mWidth - 1) * mSegment->getVertexSeperation(), | ||||
|                        mMax, | ||||
|                        (mWidth - 1) * mSegment->getVertexSeperation()); | ||||
| 
 | ||||
|     mCenter = Ogre::Vector3( ( (mWidth  - 1) * mSegment->getVertexSeperation() ) / 2, | ||||
|                              ( mMin + mMax ) / 2, | ||||
|                              ( (mWidth - 1) * mSegment->getVertexSeperation() ) / 2); | ||||
| 
 | ||||
|     mBoundingRadius = (mBounds.getMaximum() - mBounds.getMinimum()).length() / 2; | ||||
| } | ||||
| //----------------------------------------------
 | ||||
| void TerrainRenderable::createVertexBuffer() { | ||||
|     using namespace Ogre; | ||||
| 
 | ||||
|     size_t vw = mWidth; | ||||
|     if ( mUseSkirts ) vw += 2; | ||||
| 
 | ||||
|     mVertexes = new VertexData(); | ||||
|     mVertexes->vertexStart = 0; | ||||
|     mVertexes->vertexCount = vw*vw;// VERTEX_WIDTH;
 | ||||
| 
 | ||||
|     VertexDeclaration* vertexDecl = mVertexes->vertexDeclaration; | ||||
|     size_t currOffset = 0; | ||||
| 
 | ||||
|     vertexDecl->addElement(MAIN_BINDING, currOffset, VET_FLOAT3, VES_POSITION); | ||||
|     currOffset += VertexElement::getTypeSize(VET_FLOAT3); | ||||
| 
 | ||||
|     vertexDecl->addElement(MAIN_BINDING, currOffset, VET_FLOAT3, VES_NORMAL); | ||||
|     currOffset += VertexElement::getTypeSize(VET_FLOAT3); | ||||
| 
 | ||||
| 
 | ||||
|     vertexDecl->addElement(MAIN_BINDING, currOffset, VET_FLOAT2, VES_TEXTURE_COORDINATES, 0); | ||||
|     currOffset += VertexElement::getTypeSize(VET_FLOAT2); | ||||
| 
 | ||||
|     if ( mTerrain->isTextureFadingEnabled() ) { | ||||
|         vertexDecl->addElement(MAIN_BINDING, currOffset, VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES, 1); | ||||
|         currOffset += VertexElement::getTypeSize(VET_FLOAT2); | ||||
|     } | ||||
| 
 | ||||
|     mMainBuffer = HardwareBufferManager::getSingleton().createVertexBuffer( | ||||
|                       vertexDecl->getVertexSize(0), // size of one whole vertex
 | ||||
|                       mVertexes->vertexCount, // number of vertices
 | ||||
|                       HardwareBuffer::HBU_STATIC_WRITE_ONLY, // usage
 | ||||
|                       false); // no shadow buffer
 | ||||
| 
 | ||||
|     mVertexes->vertexBufferBinding->setBinding(MAIN_BINDING, mMainBuffer); //bind the data
 | ||||
| 
 | ||||
|     if ( mTerrain->isMorhpingEnabled() ) | ||||
|         vertexDecl->addElement(DELTA_BINDING, 0, VET_FLOAT1, VES_BLEND_WEIGHTS); | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| Ogre::HardwareVertexBufferSharedPtr TerrainRenderable::createDeltaBuffer( ) { | ||||
|     size_t vw = mWidth; | ||||
|     if ( mUseSkirts ) vw += 2; | ||||
| 
 | ||||
|     const size_t totalVerts = (vw * vw); | ||||
|     return Ogre::HardwareBufferManager::getSingleton().createVertexBuffer( | ||||
|                Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT1), | ||||
|                totalVerts, | ||||
|                Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY, | ||||
|                false); //no shadow buff
 | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| //----------------------------------------------
 | ||||
| #define SET_DELTA_AT(x, y, v)                       \ | ||||
| if ( mUseSkirts )   pDeltas[( y + 1)*vw+ x + 1] = v; \ | ||||
| else                pDeltas[( y)*vw+ x] = v; | ||||
| void TerrainRenderable::calculateDeltaValues() { | ||||
| 
 | ||||
|     using namespace Ogre; | ||||
|     size_t vw = mWidth; | ||||
|     if ( mUseSkirts ) vw += 2; | ||||
| 
 | ||||
|     //must be using morphing to use this function
 | ||||
|     assert(mTerrain->isMorhpingEnabled()); | ||||
| 
 | ||||
|     const size_t step = 2; | ||||
| 
 | ||||
|     // Create a set of delta values
 | ||||
|     mDeltaBuffer = createDeltaBuffer(); | ||||
|     float* pDeltas = static_cast<float*>(mDeltaBuffer->lock(HardwareBuffer::HBL_DISCARD)); | ||||
|     memset(pDeltas, 0, (vw)*(vw) * sizeof(float)); | ||||
| 
 | ||||
|     return; | ||||
| 
 | ||||
|     bool flag=false; | ||||
|     for ( size_t y = 0; y < mWidth-step; y += step ) { | ||||
|         for ( size_t x = 0; x < mWidth-step; x += step ) { | ||||
|             //create the diffrence between the full vertex if the vertex wasn't their
 | ||||
| 
 | ||||
|             float bottom_left = getVertexHeight(x,y); | ||||
|             float bottom_right = getVertexHeight(x+step,y); | ||||
| 
 | ||||
|             float top_left = getVertexHeight(x,y+step); | ||||
|             float top_right = getVertexHeight(x+step,y+step); | ||||
| 
 | ||||
|             //const int vw = mWidth+2;
 | ||||
|             SET_DELTA_AT(x, y+1, (bottom_left+top_left)/2 - getVertexHeight(x, y+1)) //left
 | ||||
|             SET_DELTA_AT(x+2, y+1, (bottom_right+top_right)/2 - getVertexHeight(x+2, y+1)) //right
 | ||||
| 
 | ||||
|             SET_DELTA_AT(x+1, y+2, (top_right+top_left)/2 - getVertexHeight(x+1, y+2)) //top
 | ||||
|             SET_DELTA_AT(x+1, y, (bottom_right+bottom_left)/2 - getVertexHeight(x+1, y)) //bottom
 | ||||
| 
 | ||||
|             //this might not be correct
 | ||||
|             if ( !flag ) | ||||
|                 SET_DELTA_AT(x+1, y+1, (bottom_left+top_right)/2 - getVertexHeight(x+1, y+1)) //center
 | ||||
|                 else | ||||
|                     SET_DELTA_AT(x+1, y+1, (bottom_right+top_left)/2 - getVertexHeight(x+1, y+1)) //center
 | ||||
| 
 | ||||
|                     flag = !flag; | ||||
|         } | ||||
|         flag = !flag; //flip tries for next row
 | ||||
|     } | ||||
| 
 | ||||
|     mDeltaBuffer->unlock(); | ||||
|     mVertexes->vertexBufferBinding->setBinding(DELTA_BINDING,mDeltaBuffer); | ||||
| 
 | ||||
| } | ||||
| #undef SET_DELTA_AT | ||||
| 
 | ||||
| //----------------------------------------------
 | ||||
| void TerrainRenderable::calculateIndexValues() { | ||||
|     using namespace Ogre; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     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); | ||||
| 
 | ||||
|     mIndices = new IndexData(); | ||||
|     mIndices->indexCount = indexCount; | ||||
|     mIndices->indexBuffer = HardwareBufferManager::getSingleton().createIndexBuffer( | ||||
|                                 HardwareIndexBuffer::IT_16BIT, | ||||
|                                 indexCount, HardwareBuffer::HBU_STATIC_WRITE_ONLY, false); | ||||
| 
 | ||||
|     unsigned short* indices = static_cast<unsigned short*>(mIndices->indexBuffer->lock(0, | ||||
|                               mIndices->indexBuffer->getSizeInBytes(), | ||||
|                               HardwareBuffer::HBL_DISCARD)); | ||||
| 
 | ||||
|     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); | ||||
|     mIndices->indexBuffer->unlock(); | ||||
|     //return mIndices;
 | ||||
| } | ||||
							
								
								
									
										229
									
								
								terrain/cpp_terrainmesh.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										229
									
								
								terrain/cpp_terrainmesh.h
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,229 @@ | |||
| /**
 | ||||
| * @brief Terrain for one cell. Handles building, destroying and LOD morphing | ||||
| */ | ||||
| class TerrainRenderable : public Ogre::Renderable, public Ogre::MovableObject { | ||||
| public: | ||||
| 
 | ||||
|     explicit TerrainRenderable(Terrain* t, QuadSegment* qs,  int width, int depth, bool skirts); | ||||
| 
 | ||||
|     /**
 | ||||
|     * Calls destroy(); | ||||
|     */ | ||||
|     ~TerrainRenderable(); | ||||
| 
 | ||||
|     /**
 | ||||
|     * Creates the actual mesh, it has to be called manually, as it is not called in the constructor | ||||
|     */ | ||||
|     void create(Ogre::SceneNode* sn) ; | ||||
| 
 | ||||
|     /**
 | ||||
|     * Can be called manually and can be called from the destuctor. This destroyes the mesh | ||||
|     */ | ||||
|     void destroy(); | ||||
| 
 | ||||
|     /**
 | ||||
|     * @brief Checks if it needs to be split or unsplit and deals with the morph factor | ||||
|     * @brief time seconds since last frame | ||||
|     */ | ||||
|     void update(Ogre::Real time, Ogre::Real camDist, Ogre::Real usplitDist, Ogre::Real morphDist); | ||||
| 
 | ||||
|     /**
 | ||||
|     * @todo Needs to work out what this does (if it does what it is meant to) | ||||
|     */ | ||||
|     void visitRenderables(Renderable::Visitor* visitor, | ||||
|                           bool debugRenderables = false) { | ||||
|         visitor->visit(this, 0, false); | ||||
|     } | ||||
| 
 | ||||
|     virtual const Ogre::MaterialPtr& getMaterial( void ) const { | ||||
|         return mMaterial; | ||||
|     } | ||||
|     //-----------------------------------------------------------------------
 | ||||
|     void getRenderOperation( Ogre::RenderOperation& op ) { | ||||
|         op.useIndexes = true; | ||||
|         op.operationType = Ogre::RenderOperation::OT_TRIANGLE_LIST; | ||||
|         op.vertexData = mVertexes; | ||||
|         op.indexData = mIndices; | ||||
|     } | ||||
| 
 | ||||
|     //-----------------------------------------------------------------------
 | ||||
|     void getWorldTransforms( Ogre::Matrix4* xform ) const { | ||||
|         *xform = mParentNode->_getFullTransform(); | ||||
|     } | ||||
|     //-----------------------------------------------------------------------
 | ||||
|     const Ogre::Quaternion& getWorldOrientation(void) const { | ||||
|         return mParentNode->_getDerivedOrientation(); | ||||
|     } | ||||
|     //-----------------------------------------------------------------------
 | ||||
|     const Ogre::Vector3& getWorldPosition(void) const { | ||||
|         return mParentNode->_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, this->getBoundingRadius(), 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; | ||||
|     } | ||||
|     //-----------------------------------------------------------------------
 | ||||
| 
 | ||||
|     /**
 | ||||
|     * @brief passes the morph factor to the custom vertex program | ||||
|     */ | ||||
|     void _updateCustomGpuParameter(const Ogre::GpuProgramParameters::AutoConstantEntry& constantEntry, | ||||
|                                     Ogre::GpuProgramParameters* params) const; | ||||
| 
 | ||||
|     /**
 | ||||
|     * @brief sets the mExtraMorphAmount so it slowly regains detail from the lowest morph factor | ||||
|     */ | ||||
|     void justSplit(); | ||||
|     /**
 | ||||
|     * @brief Does nothing | ||||
|     */ | ||||
|     inline void justUnsplit(){ | ||||
|     } | ||||
| 
 | ||||
|     inline float getMax(){ | ||||
|         return mMax; | ||||
|     } | ||||
|     inline float getMin(){ | ||||
|         return mMin; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
| 
 | ||||
|     /**
 | ||||
|     * @brief Adds another pass to the material to fade in/out the material from a higher level | ||||
|     */ | ||||
|     void addFadePass(); | ||||
|     /**
 | ||||
|     * @brief removes the last pass from the material. Assumed to be the fade pass | ||||
|     */ | ||||
|     void removeFadePass(); | ||||
| 
 | ||||
|     /**
 | ||||
|     * @return the height at the given vertex | ||||
|     */ | ||||
|     float getVertexHeight(int x, int y); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Inits the vertex stuff | ||||
|      */ | ||||
|     void createVertexBuffer(); | ||||
| 
 | ||||
|     /**
 | ||||
|     * @brief fills the vertex buffer with data | ||||
|     * @todo I don't think tex co-ords are right | ||||
|     */ | ||||
|     void calculateVetexValues(); | ||||
| 
 | ||||
|     /**
 | ||||
|     * @brief returns a a new Vertex Buffer ready for input | ||||
|     * @remarks Unlike other terrain libs, this isn't 0s when it is returend | ||||
|     */ | ||||
|     Ogre::HardwareVertexBufferSharedPtr createDeltaBuffer( ); | ||||
| 
 | ||||
|     /**
 | ||||
|     * @brief DOESN'T WORK FULLY | ||||
|     * @todo fix | ||||
|     */ | ||||
|     void calculateDeltaValues(); | ||||
|     /**
 | ||||
|     * @brief gets the position of a vertex. It will not interpolate | ||||
|     */ | ||||
|     Ogre::Vector3 getVertexPosition(int x, int y); | ||||
| 
 | ||||
|     /**
 | ||||
|     * @brief gets the indicies for the vertex data. | ||||
|     */ | ||||
|     void calculateIndexValues(); | ||||
| 
 | ||||
|     /**
 | ||||
|     * @brief sets the bounds on the renderable. This requires that mMin/mMax have been set. They are set in createVertexData | ||||
|     * @remarks this may look as though it is done twice, as it is also done in MeshInterface, however, there is no guarentee that | ||||
|     *           the mesh sizes are the same | ||||
|     */ | ||||
|     void setBounds(); | ||||
| 
 | ||||
| 
 | ||||
|     const int mWidth; | ||||
|     const bool mUseSkirts; | ||||
| 
 | ||||
|     ///true if the land has been built
 | ||||
|     bool mBuilt; | ||||
| 
 | ||||
|     int mDepth; | ||||
|     QuadSegment* mSegment; | ||||
|     Terrain* mTerrain; | ||||
| 
 | ||||
|     Ogre::MaterialPtr mMaterial; | ||||
| 
 | ||||
|     Ogre::ManualObject* mObject; | ||||
|     Ogre::SceneNode* mSceneNode; | ||||
| 
 | ||||
| 
 | ||||
|     Ogre::VertexData* mVertexes; | ||||
|     Ogre::IndexData* mIndices; | ||||
|     Ogre::HardwareVertexBufferSharedPtr mMainBuffer; | ||||
|     Ogre::HardwareVertexBufferSharedPtr mDeltaBuffer; | ||||
| 
 | ||||
|     mutable bool mLightListDirty; | ||||
| 
 | ||||
| 
 | ||||
|     Ogre::Real mBoundingRadius; | ||||
|     Ogre::AxisAlignedBox mBounds; | ||||
|     Ogre::Vector3 mCenter; | ||||
| 
 | ||||
|     Ogre::Real mLODMorphFactor, mTextureFadeFactor; | ||||
| 
 | ||||
|     /** Max and min heights of the mesh */ | ||||
|     float mMin, mMax; | ||||
| 
 | ||||
| 
 | ||||
|     /**
 | ||||
|     * @brief holds the amount to morph by, this decreases to 0 over a periord of time | ||||
|     * It is changed and used in the update() function | ||||
|     */ | ||||
|     Ogre::Real mExtraMorphAmount, mExtraFadeAmount; | ||||
| 
 | ||||
|     /**
 | ||||
|     * True if the fade pass has been added to the material. | ||||
|     */ | ||||
|     bool mHasFadePass; | ||||
| 
 | ||||
|     //Custom params used in terrian shaders.
 | ||||
|     static const size_t MORPH_CUSTOM_PARAM_ID = 77; | ||||
|     static const size_t FADE_CUSTOM_PARAM_ID = 78; | ||||
| 
 | ||||
|     //Terrain bindings
 | ||||
|     static const int MAIN_BINDING = 0; | ||||
|     static const int DELTA_BINDING = 1; | ||||
| }; | ||||
							
								
								
									
										68
									
								
								terrain/terrain.d
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								terrain/terrain.d
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,68 @@ | |||
| /* | ||||
|   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 std.stdio; | ||||
| import std.file; | ||||
| import monster.util.string; | ||||
| 
 | ||||
| void fail(char[] msg) | ||||
| { | ||||
|   throw new Exception(msg); | ||||
| } | ||||
| 
 | ||||
| // Move elsewhere, make part of the general cache system later
 | ||||
| void makeDir(char[] pt) | ||||
| { | ||||
|   if(exists(pt)) | ||||
|     { | ||||
|       if(!isdir(pt)) | ||||
|         fail(pt ~ " is not a directory"); | ||||
|     } | ||||
|   else | ||||
|     mkdir(pt); | ||||
| } | ||||
| 
 | ||||
| void makePath(char[] pt) | ||||
| { | ||||
|   assert(!pt.begins("/")); | ||||
|   foreach(int i, char c; pt) | ||||
|     if(c == '/') | ||||
|       makeDir(pt[0..i]); | ||||
| 
 | ||||
|   if(!pt.ends("/")) | ||||
|     makeDir(pt); | ||||
| } | ||||
| 
 | ||||
| void initTerrain() | ||||
| { | ||||
|   makePath("cache/terrain"); | ||||
| 
 | ||||
|   //terr_genData();
 | ||||
|   terr_setupRendering(); | ||||
| } | ||||
| 
 | ||||
| extern(C): | ||||
| void terr_genData(); | ||||
| void terr_setupRendering(); | ||||
		Loading…
	
		Reference in a new issue