mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-10-29 03:26: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
 | # Some extra flags for niftool and bsatool
 | ||||||
| NIFFLAGS=-debug=warnstd -debug=check -debug=statecheck -debug=strict -debug=verbose | 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
 | # 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) | OGCC=$(CXX) $(CXXFLAGS) $(CF_OIS) | ||||||
| 
 | 
 | ||||||
| # Compiler settings for ffmpeg.
 | # Compiler settings for ffmpeg.
 | ||||||
|  | @ -23,10 +27,15 @@ BGCC=$(CXX) $(CXXFLAGS) $(CF_BULLET) | ||||||
| # passed to the compiler, the rest are dependencies.
 | # passed to the compiler, the rest are dependencies.
 | ||||||
| ogre_cpp=ogre framelistener interface bsaarchive | ogre_cpp=ogre framelistener interface bsaarchive | ||||||
| 
 | 
 | ||||||
| # MyGUI C++ files, gui/cpp_X.cpp. These are currently included into
 | # MyGUI C++ files, gui/cpp_X.cpp. These are currently included
 | ||||||
| # with cpp_ogre.cpp.
 | # cpp_ogre.o with cpp_ogre.cpp.
 | ||||||
| mygui_cpp=mygui console | 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.
 | # FFmpeg files, in the form sound/cpp_X.cpp.
 | ||||||
| avcodec_cpp=avcodec | avcodec_cpp=avcodec | ||||||
| 
 | 
 | ||||||
|  | @ -35,7 +44,10 @@ bullet_cpp=bullet player scale | ||||||
| 
 | 
 | ||||||
| #### No modifications should be required below this line. ####
 | #### 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) | avcodec_cpp_files=$(avcodec_cpp:%=sound/cpp_%.cpp) | ||||||
| bullet_cpp_files=$(bullet_cpp:%=bullet/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) \
 | src := $(wildcard bsa/*.d) $(wildcard bullet/*.d) $(wildcard core/*.d) \
 | ||||||
| $(wildcard esm/*.d) $(wildcard input/*.d) $(wildcard nif/*.d) $(wildcard ogre/*.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) | $(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 \
 | src := $(src) monster/monster.d \
 | ||||||
| $(wildcard monster/vm/*.d) \ | $(wildcard monster/vm/*.d) \ | ||||||
| $(wildcard monster/compiler/*.d) \ | $(wildcard monster/compiler/*.d) \ | ||||||
|  | @ -85,10 +97,10 @@ nifobjs/%.o: %.d | ||||||
| 	$(DMD) $(NIFFLAGS) -c $< -of$@ | 	$(DMD) $(NIFFLAGS) -c $< -of$@ | ||||||
| 
 | 
 | ||||||
| openmw: openmw.d cpp_ogre.o cpp_avcodec.o cpp_bullet.o $(obj) | 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) | 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) | niftool: niftool.d $(obj_nif) | ||||||
| 	$(DMD) $^ -of$@ | 	$(DMD) $^ -of$@ | ||||||
|  |  | ||||||
|  | @ -32,6 +32,7 @@ typedef void* WidgetPtr; | ||||||
| void gui_setupGUI(int debugOut); | void gui_setupGUI(int debugOut); | ||||||
| void gui_toggleGui(); | void gui_toggleGui(); | ||||||
| void gui_setCellName(char *str); | void gui_setCellName(char *str); | ||||||
|  | void gui_showHUD(); | ||||||
| 
 | 
 | ||||||
| // Console stuff
 | // Console stuff
 | ||||||
| void gui_toggleConsole(); | void gui_toggleConsole(); | ||||||
|  |  | ||||||
|  | @ -450,6 +450,12 @@ extern "C" void gui_toggleGui() | ||||||
| 
 | 
 | ||||||
| extern "C" int32_t* gui_getGuiModePtr() { return &guiMode; } | 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) | extern "C" void gui_setupGUI(int32_t debugOut) | ||||||
| { | { | ||||||
|   ResourceGroupManager::getSingleton(). |   ResourceGroupManager::getSingleton(). | ||||||
|  | @ -480,7 +486,6 @@ extern "C" void gui_setupGUI(int32_t debugOut) | ||||||
|   state.X.abs = mWidth / 2; |   state.X.abs = mWidth / 2; | ||||||
|   state.Y.abs = mHeight / 2; |   state.Y.abs = mHeight / 2; | ||||||
| 
 | 
 | ||||||
|   //*
 |  | ||||||
|   // Set up the HUD
 |   // Set up the HUD
 | ||||||
|   hud = new HUD(); |   hud = new HUD(); | ||||||
| 
 | 
 | ||||||
|  | @ -494,7 +499,8 @@ extern "C" void gui_setupGUI(int32_t debugOut) | ||||||
|   hud->setSpellStatus(65, 100); |   hud->setSpellStatus(65, 100); | ||||||
| 
 | 
 | ||||||
|   hud->setEffect("icons\\s\\tx_s_chameleon.dds"); |   hud->setEffect("icons\\s\\tx_s_chameleon.dds"); | ||||||
|   //*/
 | 
 | ||||||
|  |   hud->setVisible(false); | ||||||
| 
 | 
 | ||||||
|   //new MainMenu();
 |   //new MainMenu();
 | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										18
									
								
								gui/gui.d
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								gui/gui.d
									
									
									
									
									
								
							|  | @ -232,6 +232,24 @@ void getHeight() | ||||||
|   stack.pushInt(gui_getHeight(null)); |   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() | void setupGUIScripts() | ||||||
| { | { | ||||||
|   vm.addPath("mscripts/guiscripts/"); |   vm.addPath("mscripts/guiscripts/"); | ||||||
|  |  | ||||||
|  | @ -214,10 +214,14 @@ const float sndRefresh = 0.17; | ||||||
| // Refresh rate for music fadeing, seconds.
 | // Refresh rate for music fadeing, seconds.
 | ||||||
| const float musRefresh = 0.05; | const float musRefresh = 0.05; | ||||||
| 
 | 
 | ||||||
|  | // Walking / floating speed, in points per second.
 | ||||||
|  | float speed = 300; | ||||||
|  | 
 | ||||||
| float sndCumTime = 0; | float sndCumTime = 0; | ||||||
| float musCumTime = 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
 |   // Move the player into place. TODO: This isn't really input-related
 | ||||||
|   // at all, and should be moved.
 |   // at all, and should be moved.
 | ||||||
|  | @ -228,6 +232,11 @@ void initializeInput() | ||||||
| 
 | 
 | ||||||
|       bullet_movePlayer(position[0], position[1], position[2]); |       bullet_movePlayer(position[0], position[1], position[2]); | ||||||
|     } |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void initializeInput() | ||||||
|  | { | ||||||
|  |   movePlayer(); | ||||||
| 
 | 
 | ||||||
|   // TODO/FIXME: This should have been in config, but DMD's module
 |   // 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
 |   // system is on the brink of collapsing, and it won't compile if I
 | ||||||
|  | @ -250,6 +259,22 @@ bool isPressed(Keys key) | ||||||
|   return false; |   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) | extern(C) int d_frameStarted(float time) | ||||||
| { | { | ||||||
|   if(doExit) return 0; |   if(doExit) return 0; | ||||||
|  | @ -270,9 +295,6 @@ extern(C) int d_frameStarted(float time) | ||||||
|   // The rest is ignored in pause or GUI mode
 |   // The rest is ignored in pause or GUI mode
 | ||||||
|   if(pause || *guiMode > 0) return 1; |   if(pause || *guiMode > 0) return 1; | ||||||
| 
 | 
 | ||||||
|   // Walking / floating speed, in points per second.
 |  | ||||||
|   const float speed = 300; |  | ||||||
| 
 |  | ||||||
|   // Check if the movement keys are pressed
 |   // Check if the movement keys are pressed
 | ||||||
|   float moveX = 0, moveY = 0, moveZ = 0; |   float moveX = 0, moveY = 0, moveZ = 0; | ||||||
|   float x, y, z, ox, oy, oz; |   float x, y, z, ox, oy, oz; | ||||||
|  |  | ||||||
|  | @ -54,17 +54,6 @@ void initMonsterScripts() | ||||||
|   setupGUIScripts(); |   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:
 | // This should probably not be here:
 | ||||||
| import monster.vm.dbg; | import monster.vm.dbg; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -59,34 +59,9 @@ extern "C" int32_t ogre_configure( | ||||||
| 
 | 
 | ||||||
|   mRoot = new Root(plugincfg, "ogre.cfg", ""); |   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 ); |   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(). |   ResourceGroupManager::getSingleton(). | ||||||
|     addResourceLocation("internal", "BSA", "General"); |     addResourceLocation("internal", "BSA", "General"); | ||||||
| 
 | 
 | ||||||
|  | @ -328,10 +303,7 @@ void cloneNode(SceneNode *from, SceneNode *to, char* name) | ||||||
|   SceneNode::ObjectIterator it = from->getAttachedObjectIterator(); |   SceneNode::ObjectIterator it = from->getAttachedObjectIterator(); | ||||||
|   while(it.hasMoreElements()) |   while(it.hasMoreElements()) | ||||||
|     { |     { | ||||||
|       // We can't handle non-entities. To be honest I have no idea
 |       // We can't handle non-entities.
 | ||||||
|       // 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.
 |  | ||||||
|       Entity *e = dynamic_cast<Entity*> (it.getNext()); |       Entity *e = dynamic_cast<Entity*> (it.getNext()); | ||||||
|       if(e) |       if(e) | ||||||
|         { |         { | ||||||
|  |  | ||||||
|  | @ -70,8 +70,11 @@ int32_t guiMode = 0; | ||||||
| // Morrowind.
 | // Morrowind.
 | ||||||
| SceneNode *root; | 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 "../gui/cpp_mygui.cpp" | ||||||
| #include "cpp_framelistener.cpp" | #include "cpp_framelistener.cpp" | ||||||
| #include "cpp_bsaarchive.cpp" | #include "cpp_bsaarchive.cpp" | ||||||
| #include "cpp_interface.cpp" | #include "cpp_interface.cpp" | ||||||
|  | #include "../terrain/cpp_terrain.cpp" | ||||||
|  |  | ||||||
|  | @ -119,12 +119,6 @@ void setupOgre(bool debugOut) | ||||||
|   // exterior cells differently, etc.
 |   // exterior cells differently, etc.
 | ||||||
|   ogre_makeScene(); |   ogre_makeScene(); | ||||||
| 
 | 
 | ||||||
|   // Load the GUI system
 |  | ||||||
|   gui_setupGUI(debugOut); |  | ||||||
| 
 |  | ||||||
|   // Run the GUI scripts
 |  | ||||||
|   runGUIScripts(); |  | ||||||
| 
 |  | ||||||
|   ogreSetup = true; |   ogreSetup = true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										278
									
								
								openmw.d
									
									
									
									
									
								
							
							
						
						
									
										278
									
								
								openmw.d
									
									
									
									
									
								
							|  | @ -1,6 +1,6 @@ | ||||||
| /* | /* | ||||||
|   OpenMW - The completely unofficial reimplementation of Morrowind |   OpenMW - The completely unofficial reimplementation of Morrowind | ||||||
|   Copyright (C) 2008  Nicolay Korslund |   Copyright (C) 2008-2009  Nicolay Korslund | ||||||
|   Email: < korslund@gmail.com > |   Email: < korslund@gmail.com > | ||||||
|   WWW: http://openmw.snaptoad.com/
 |   WWW: http://openmw.snaptoad.com/
 | ||||||
| 
 | 
 | ||||||
|  | @ -31,6 +31,7 @@ import std.file; | ||||||
| import ogre.ogre; | import ogre.ogre; | ||||||
| import ogre.bindings; | import ogre.bindings; | ||||||
| import gui.bindings; | import gui.bindings; | ||||||
|  | import gui.gui; | ||||||
| 
 | 
 | ||||||
| import bullet.bullet; | import bullet.bullet; | ||||||
| 
 | 
 | ||||||
|  | @ -51,6 +52,8 @@ import sound.audio; | ||||||
| 
 | 
 | ||||||
| import input.events; | import input.events; | ||||||
| 
 | 
 | ||||||
|  | import terrain.terrain; | ||||||
|  | 
 | ||||||
| //*
 | //*
 | ||||||
| import std.gc; | import std.gc; | ||||||
| import gcstats; | import gcstats; | ||||||
|  | @ -72,6 +75,7 @@ void main(char[][] args) | ||||||
|   bool showOgreFlag = false; |   bool showOgreFlag = false; | ||||||
|   bool noSound = false; |   bool noSound = false; | ||||||
|   bool debugOut = false; |   bool debugOut = false; | ||||||
|  |   bool extTest = false; | ||||||
| 
 | 
 | ||||||
|   // Some examples to try:
 |   // Some examples to try:
 | ||||||
|   //
 |   //
 | ||||||
|  | @ -88,16 +92,10 @@ void main(char[][] args) | ||||||
| 
 | 
 | ||||||
|   // Cells to load
 |   // Cells to load
 | ||||||
|   char[][] cells; |   char[][] cells; | ||||||
|   int[] eCells; |  | ||||||
| 
 | 
 | ||||||
|   foreach(char[] a; args[1..$]) |   foreach(char[] a; args[1..$]) | ||||||
|     if(a == "-n") render = false; |     if(a == "-n") render = false; | ||||||
|     else if(a.begins("-e")) |     else if(a == "-ex") extTest = true; | ||||||
|       { |  | ||||||
| 	int i = find(a,','); |  | ||||||
| 	eCells ~= atoi(a[2..i]); |  | ||||||
| 	eCells ~= atoi(a[i+1..$]); |  | ||||||
|       } |  | ||||||
|     else if(a == "-h") help=true; |     else if(a == "-h") help=true; | ||||||
|     else if(a == "-rk") resetKeys = true; |     else if(a == "-rk") resetKeys = true; | ||||||
|     else if(a == "-oc") showOgreFlag = true; |     else if(a == "-oc") showOgreFlag = true; | ||||||
|  | @ -112,7 +110,7 @@ void main(char[][] args) | ||||||
|       } |       } | ||||||
|     else cells ~= a; |     else cells ~= a; | ||||||
| 
 | 
 | ||||||
|   if(cells.length + eCells.length/2 > 1 ) |   if(cells.length > 1) | ||||||
|     { |     { | ||||||
|       writefln("More than one cell specified, rendering disabled"); |       writefln("More than one cell specified, rendering disabled"); | ||||||
|       render=false; |       render=false; | ||||||
|  | @ -123,7 +121,7 @@ void main(char[][] args) | ||||||
|       writefln("Syntax: %s [options] cell-name [cell-name]", args[0]); |       writefln("Syntax: %s [options] cell-name [cell-name]", args[0]); | ||||||
|       writefln("  Options:"); |       writefln("  Options:"); | ||||||
|       writefln("    -n            Only load, do not render"); |       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("    -rk           Reset key bindings to default"); | ||||||
|       writefln("    -oc           Show the Ogre config dialogue"); |       writefln("    -oc           Show the Ogre config dialogue"); | ||||||
|       writefln("    -ns           Completely disable sound"); |       writefln("    -ns           Completely disable sound"); | ||||||
|  | @ -163,14 +161,14 @@ void main(char[][] args) | ||||||
|   // setting.
 |   // setting.
 | ||||||
|   if(showOgreFlag) config.finalOgreConfig = true; |   if(showOgreFlag) config.finalOgreConfig = true; | ||||||
| 
 | 
 | ||||||
|   if(cells.length == 0 && eCells.length == 0) |   if(cells.length == 0) | ||||||
|     if(config.defaultCell.length) |     if(config.defaultCell.length) | ||||||
|       cells ~= config.defaultCell; |       cells ~= config.defaultCell; | ||||||
| 
 | 
 | ||||||
|   if(cells.length == 1) |   if(cells.length == 1) | ||||||
|     config.defaultCell = cells[0]; |     config.defaultCell = cells[0]; | ||||||
| 
 | 
 | ||||||
|   if(cells.length == 0 && eCells.length == 0) |   if(cells.length == 0) | ||||||
|     { |     { | ||||||
|       showHelp(); |       showHelp(); | ||||||
|       return; |       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
 |   // Simple safety hack
 | ||||||
|   NodePtr putObject(MeshIndex m, Placement *pos, float scale, |   NodePtr putObject(MeshIndex m, Placement *pos, float scale, | ||||||
|                     bool collide=false) |                     bool collide=false) | ||||||
|  | @ -250,21 +227,23 @@ Try specifying another cell name on the command line, or edit openmw.ini."); | ||||||
|       setupOgre(debugOut); |       setupOgre(debugOut); | ||||||
|       scope(exit) cleanupOgre(); |       scope(exit) cleanupOgre(); | ||||||
| 
 | 
 | ||||||
|  |       // Create the GUI system
 | ||||||
|  |       initGUI(debugOut); | ||||||
|  | 
 | ||||||
|       // Set up Bullet
 |       // Set up Bullet
 | ||||||
|       initBullet(); |       initBullet(); | ||||||
|       scope(exit) cleanupBullet(); |       scope(exit) cleanupBullet(); | ||||||
| 
 | 
 | ||||||
|       if(cd.inCell) |       // Initialize the internal input and event manager. The
 | ||||||
| 	{ |       // lower-level input system (OIS) is initialized by the
 | ||||||
| 	  setAmbient(cd.ambi.ambient, cd.ambi.sunlight, |       // setupOgre() call further up.
 | ||||||
| 		     cd.ambi.fog, cd.ambi.fogDensity); |       initializeInput(); | ||||||
| 
 | 
 | ||||||
| 	  // Not all interior cells have water
 |       // Play some old tunes
 | ||||||
| 	  if(cd.inCell.flags & CellFlags.HasWater) |       if(extTest) | ||||||
| 	    ogre_createWater(cd.water); |         { | ||||||
| 	} |           // Exterior cell
 | ||||||
|       else |           /* | ||||||
| 	{ |  | ||||||
| 	  Color c; | 	  Color c; | ||||||
| 	  c.red = 180; | 	  c.red = 180; | ||||||
| 	  c.green = 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
 | 	  // Create an ugly sky
 | ||||||
| 	  ogre_makeSky(); | 	  ogre_makeSky(); | ||||||
| 	} |           */ | ||||||
| 
 | 
 | ||||||
|       // Insert the meshes of statics into the scene
 |           initTerrain(); | ||||||
|       foreach(ref LiveStatic ls; cd.statics) |         } | ||||||
| 	putObject(ls.m.model, ls.getPos(), ls.getScale(), true); |       else | ||||||
|       // Inventory lights
 |         { | ||||||
|       foreach(ref LiveLight ls; cd.lights) |           // Interior cell
 | ||||||
| 	{ |           assert(cd.inCell !is null); | ||||||
| 	  NodePtr n = putObject(ls.m.model, ls.getPos(), ls.getScale()); | 	  setAmbient(cd.ambi.ambient, cd.ambi.sunlight, | ||||||
| 	  ls.lightNode = attachLight(n, ls.m.data.color, ls.m.data.radius); | 		     cd.ambi.fog, cd.ambi.fogDensity); | ||||||
| 	  if(!noSound) | 
 | ||||||
| 	  { | 	  // Not all interior cells have water
 | ||||||
|             Sound *s = ls.m.sound; | 	  if(cd.inCell.flags & CellFlags.HasWater) | ||||||
|             if(s) | 	    ogre_createWater(cd.water); | ||||||
| 	    { | 
 | ||||||
| 	      ls.loopSound = soundScene.insert(s, true); |           // Insert the meshes of statics into the scene
 | ||||||
| 	      if(ls.loopSound) |           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(); |                   Sound *s = ls.m.sound; | ||||||
|                   ls.loopSound.setPos(p.position[0], |                   if(s) | ||||||
|                                       p.position[1], |                     { | ||||||
|                                       p.position[2]); |                       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) | ||||||
|       // 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); | ||||||
| 	  NodePtr n = putObject(ls.m.model, ls.getPos(), ls.getScale(), true); |               if(!noSound) | ||||||
| 	  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) |  | ||||||
|                 { |                 { | ||||||
|                   auto p = ls.getPos(); |                   Sound *s = ls.m.sound; | ||||||
|                   ls.loopSound.setPos(p.position[0], |                   if(s) | ||||||
|                                       p.position[1], |                     { | ||||||
|                                       p.position[2]); |                       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) | ||||||
|       // Misc items
 |             putObject(ls.m.model, ls.getPos(), ls.getScale()); | ||||||
|       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) | ||||||
|       // NPCs (these are complicated, usually do not have normal meshes)
 |           putObject(ls.m.model, ls.getPos(), ls.getScale()); | ||||||
|       foreach(ref LiveNPC ls; cd.npcs) |           */ | ||||||
|       putObject(ls.m.model, ls.getPos(), ls.getScale()); |           // Containers
 | ||||||
|       */ |           foreach(ref LiveContainer ls; cd.containers) | ||||||
|       // Containers
 |             putObject(ls.m.model, ls.getPos(), ls.getScale(), true); | ||||||
|       foreach(ref LiveContainer ls; cd.containers) |           // Doors
 | ||||||
| 	putObject(ls.m.model, ls.getPos(), ls.getScale(), true); |           foreach(ref LiveDoor ls; cd.doors) | ||||||
|       // Doors
 |             putObject(ls.m.model, ls.getPos(), ls.getScale()); | ||||||
|       foreach(ref LiveDoor ls; cd.doors) |           // Activators (including beds etc)
 | ||||||
| 	putObject(ls.m.model, ls.getPos(), ls.getScale()); |           foreach(ref LiveActivator ls; cd.activators) | ||||||
|       // Activators (including beds etc)
 |             putObject(ls.m.model, ls.getPos(), ls.getScale(), true); | ||||||
|       foreach(ref LiveActivator ls; cd.activators) |           // Potions
 | ||||||
| 	putObject(ls.m.model, ls.getPos(), ls.getScale(), true); |           foreach(ref LivePotion ls; cd.potions) | ||||||
|       // Potions
 |             putObject(ls.m.model, ls.getPos(), ls.getScale()); | ||||||
|       foreach(ref LivePotion ls; cd.potions) |           // Apparatus
 | ||||||
| 	putObject(ls.m.model, ls.getPos(), ls.getScale()); |           foreach(ref LiveApparatus ls; cd.appas) | ||||||
|       // Apparatus
 |             putObject(ls.m.model, ls.getPos(), ls.getScale()); | ||||||
|       foreach(ref LiveApparatus ls; cd.appas) |           // Ingredients
 | ||||||
| 	putObject(ls.m.model, ls.getPos(), ls.getScale()); |           foreach(ref LiveIngredient ls; cd.ingredients) | ||||||
|       // Ingredients
 |             putObject(ls.m.model, ls.getPos(), ls.getScale()); | ||||||
|       foreach(ref LiveIngredient ls; cd.ingredients) |           // Armors
 | ||||||
| 	putObject(ls.m.model, ls.getPos(), ls.getScale()); |           foreach(ref LiveArmor ls; cd.armors) | ||||||
|       // Armors
 |             putObject(ls.m.model, ls.getPos(), ls.getScale()); | ||||||
|       foreach(ref LiveArmor ls; cd.armors) |           // Weapons
 | ||||||
| 	putObject(ls.m.model, ls.getPos(), ls.getScale()); |           foreach(ref LiveWeapon ls; cd.weapons) | ||||||
|       // Weapons
 |             putObject(ls.m.model, ls.getPos(), ls.getScale()); | ||||||
|       foreach(ref LiveWeapon ls; cd.weapons) |           // Books
 | ||||||
| 	putObject(ls.m.model, ls.getPos(), ls.getScale()); |           foreach(ref LiveBook ls; cd.books) | ||||||
|       // Books
 |             putObject(ls.m.model, ls.getPos(), ls.getScale()); | ||||||
|       foreach(ref LiveBook ls; cd.books) |           // Clothes
 | ||||||
| 	putObject(ls.m.model, ls.getPos(), ls.getScale()); |           foreach(ref LiveClothing ls; cd.clothes) | ||||||
|       // Clothes
 |             putObject(ls.m.model, ls.getPos(), ls.getScale()); | ||||||
|       foreach(ref LiveClothing ls; cd.clothes) |           // Tools
 | ||||||
| 	putObject(ls.m.model, ls.getPos(), ls.getScale()); |           foreach(ref LiveTool ls; cd.tools) | ||||||
|       // Tools
 |             putObject(ls.m.model, ls.getPos(), ls.getScale()); | ||||||
|       foreach(ref LiveTool ls; cd.tools) |           // Creatures (not displayed very well yet)
 | ||||||
| 	putObject(ls.m.model, ls.getPos(), ls.getScale()); |           foreach(ref LiveCreature ls; cd.creatures) | ||||||
|       // Creatures (not displayed very well yet)
 |             putObject(ls.m.model, ls.getPos(), ls.getScale()); | ||||||
|       foreach(ref LiveCreature ls; cd.creatures) |  | ||||||
| 	putObject(ls.m.model, ls.getPos(), ls.getScale()); |  | ||||||
| 
 | 
 | ||||||
|       // Initialize the internal input and event manager. The
 |           // End of interior cell
 | ||||||
|       // lower-level input system (OIS) is initialized by the
 |         } | ||||||
|       // setupOgre() call further up.
 |  | ||||||
|       initializeInput(); |  | ||||||
| 
 | 
 | ||||||
|       // Start swangin'
 |       // Run GUI system
 | ||||||
|  |       startGUI(); | ||||||
|  | 
 | ||||||
|  |       // Play some old tunes
 | ||||||
|       if(!noSound) |       if(!noSound) | ||||||
|         Music.play(); |         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("%d creatures", cd.creatures.length); | ||||||
|       writefln(); |       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.
 |   // This isn't necessary but it's here for testing purposes.
 | ||||||
|   cellList.release(cd); |   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