From dfa2d627b2c1ef24145d3a5e8695eca9dbc476cd Mon Sep 17 00:00:00 2001 From: engineer_apple Date: Tue, 27 Jan 2009 15:57:07 +0000 Subject: [PATCH] Changes in version 1.6, TA - FileSystem 2.0 SUPER MASTER MAJOR API CHANGE !!! The FileSystem is know build internally like for e.q the texture-, and the meshloaders. There exists a known list of ArchiveLoader, which know how to produce a Archive. The Loaders and the Archive can be attached/detached on runtime. The FileNames are now stored as core::string. where c16 is toggled between char/wchar with the #define flag _IRR_WCHAR_FILESYSTEM, to supported unicode backends (default:off) I replaced all (const c8* filename) to string references. Basically the FileSystem is divided into two regions. Native and Virtual. Native means using the backend OS. Virtual means only use currently attach IArchives. Browsing each FileSystem has it's own workdirectory and it's own methods to - create a FileTree - add/remove files & directory ( to be done ) Hint: store a savegame in a zip archive... basic browsing for all archives is implemented. Example 21. Quake3Explorer shows this TODO: - a file filter should be implemented. - The IArchive should have a function to create a filetree for now CFileList is used. Class Hiarchy: IArchiveLoader: is able to produce a IFileArchive - ZipLoader - PakLoader - MountPointReader ( formaly known as CUnzipReader ) IFileArchive: -ZipArchive -PakArchive -MountPoint (known as FolderFile) IFileSystem - addArchiveLoader - changed implementation of isALoadableFileExtension in all loaders to have consistent behavior - added a parameter to IFileList * createFileList setFileListSystem allows to query files in any of the game archives standard behavior listtype = SYSTEM ( default) - CLimitReadFile added multiple file random-access support. solved problems with mixed compressed & uncompressed files in a zip TODO: - Big Big Testing!! - Linux Version ( minor ) - remove all double loader interfaces where only the filename differs (IReadFile/const char *filename). This blows up the the interface - many loaders use their own private filesearching we should rework this - there are a lot of helper function ( getAbsolutePath, getFileDir ) which should be adapted to the virtual filesystem - IrrlichtDevice added: virtual bool setGammaRamp( f32 red, f32 green, f32 blue, f32 brightness, f32 contrast ) = 0; virtual bool getGammaRamp( f32 &red, f32 &green, f32 &blue ) = 0; and calculating methods to DeviceStub. implemented in Win32, TODO: other Devices - irrlicht.h changed exported irrlicht.dll routines createDevice, createDeviceEx, IdentityMatrix to extern "C" name mangling. for easier dynamically loading the irrlicht library and different versions - ParticleSystem removed the private (old?,wrong?) interface from the ParticleEffectors to match the parent class irr::io::IAttributeExchangingObject::deserializeAttributes TODO: please test if the serialization works! - Generic - vector3d& normalize() #if 0 f32 length = (f32)(X*X + Y*Y + Z*Z); if (core::equals(length, 0.f)) return *this; length = core::reciprocal_squareroot ( (f32)length ); #else const T length = core::reciprocal_squareroot ( (X*X + Y*Y + Z*Z) ); #endif Weak checking on zero?!?! just to avoid a sqrt?. mhm, maybe not;-) added reciprocal_squareroot for f64 - dimension2d added operator dimension2d& operator=(const dimension2d& other) to cast between different types - vector2d bugfix: vector2d& operator+=(const dimension2d& other) { X += other.Width; Y += other.Width; return *this; } to vector2d& operator+=(const dimension2d& other) { X += other.Width; Y += other.Height; return *this; } - C3DMeshLoader renamed chunks const u16 to a enum removing "variable declared but never used warning" - added a global const identity Material changed all references *((video::SMaterial*)0) to point to IdentityMaterial removed warning: "a NULL reference is not allowed" - modified IRRLICHT_MATH to not support reciprocal stuff but to use faster float-to-int conversion. gcc troubles may they are. i'm using intel-compiler..;-) - core::matrix4 USE_MATRIX_TEST i tried to optimize the identity-check ( in means of performance) i didn't succeed so well, so i made a define for the matrix isIdentity -check for now it's sometimes faster to always calculate versus identity-check but if there are a lot of scenenodes/ particles one can profit from the fast_inverse matrix, when no scaling is used. further approvement could be done on inverse for just tranlastion! ( many static scenenodes are not rotated, they are just placed somewhere in the world) one thing to take in account is that sizeof(matrix) is 64 byte and with the additional bool/u32 makes it 66 byte which is not really cache-friendly.. - added buildRotateFromTo Builds a matrix that rotates from one vector to another - irr::array. changed allocating routine in push_back okt, 2008. it's only allowed to alloc one element, if default constructor has to be called. removes existing crashes. ( MD3 Mesh ) and possible others ones. A new list template should be made. one with constructor/destructor calls ( safe_array ) and one without. like the array since the beginning of irrlicht. currently the array/string is extremly slow.. also a hint for the user has to be done, so that a struct T of array must have a copy constructor of type T ( const T&other ). i needed hours to track that down... added a new method setAllocStrategy, safe ( used + 1 ), double ( used * 2 + 1) better default strategies will be implemented - removed binary_search_const i added it quite a long time ago, but it doesnt make real sense a call to a sort method should happen always. i just wanted to safe a few cycles.. - added binary_search_multi searches for a multi-set ( more than 1 entry in the sorted array) returns start and end-index - changed some identity matrix settings to use core::IdentityMatrix - added deletePathFromFilename to generic string functions in coreutil.h and removed from CZipReader and CPakReader - s32 deserializeAttributes used instead of virtual void deserializeAttributes in ParticleSystem ( wrong virtual was used) - strings & Locale - started to add locale support - added verify to string - added some helper functions - XBOX i have access to a XBOX development machine now. I started to compile for the XBOX. Question: Who did the previous implementation?. There is no XBOX-Device inhere. maybe it's forbidden because of using the offical Microsoft XDK. I will implement a native or sdl device based on opendk. irrlicht compiles without errors on the xbox but can't be used. TODO: - native XBOX Device - Windows Mobile reworked a little. added the mobile example to the windows solution for cross development. added maximal 128x128 texture size for windows mobile ( memory issues ) - Collision Speed Up The Collision Speed Up greatly improves with many small static child-nodes - added COctTreeTriangleSelector::getTriangles for 3dline from user Piraaate - modified createOctTreeTriangleSelector and createTriangleSelector to allow node == 0, to be added to a meta selector - CSceneNodeAnimatorCollisionResponse has the same problem as CSceneNodeAnimatorFPS on first update: Problem. you start setting the map. (setWorld). First update cames 4000 ms later. The Animator applies the missing force... big problem... changed to react on first update like camera. - add Variable FirstUpdate. if set to true ( on all changes ) then position, lasttime, and falling are initialized -added #define OCTTREE_USE_HARDWARE in Octree.h if defined octtree uses internally a derived scene::MeshBuffer which has the possibility to use the Hardware Vertex Buffer for static vertices and dirty indices;-) if defined OCTTREE_USE_HARDWARE octree uses internally a derived scene::CMeshBuffer so it's not just a replacement inside the octree. It also in the OctTreeSceneNode. #if defined (OCTTREE_USE_HARDWARE) driver->drawMeshBuffer ( &LightMapMeshes[i] ); #else driver->drawIndexedTriangleList( &LightMapMeshes[i].Vertices[0], LightMapMeshes[i].Vertices.size(), d[i].Indices, d[i].CurrentSize / 3); #endif #define OCTTREE_PARENTTEST is also used. It's skip testing on fully outside and takes everything on fully inside - virtual void ISceneNode::updateAbsolutePosition() - changed inline CMatrix4 CMatrix4::operator*(const CMatrix4& m2) const all two matrices have to be checked by isIdentity() to let the isIdentity work always -changed inline bool CMatrix4::isIdentity() const on full identityCheck-> to look first on Translation, because this is the most challenging element which will likely not to be identity.. - virtual core::matrix4 getRelativeTransformation() const Hiarchy on Identity-Check 1) ->getRelativeTransform -> 9 floating point checks to be passed as Identity 2) ->isIdentity () -> 16 floating point checks to be passed as Identity - inline void CMatrix4::transformBoxEx(core::aabbox3d& box) const added isIdentity() check - changed CSceneNodeAnimatorCollisionResponse - added CSceneNodeAnimatorCollisionResponse::setGravity needed to set the differents Forces for the Animator. for eq. water.. - added CSceneNodeAnimatorCollisionResponse::setAnimateTarget - added CSceneNodeAnimatorCollisionResponse::getAnimateTarget - changed CSceneNodeAnimatorCollisionResponse::animateNode to react on FirstUpdate - changad Gravity to - TODO: set Gravity to Physically frame independent values.. current response uses an frame depdended acceleration vector. ~9.81 m/s^2 was achieved at around 50 fps with a setting of -0.03 may effect existing application.. - SceneNodes - CSkyDomeSceneNode moved radius ( default 1000 ) to constructor added Normals added DebugInfo added Material.ZBuffer, added SceneMaanager - CVolumeLightSceneNode: changed default blending OneTextureBlendgl_src_color gl_src_alpha to EMT_TRANSPARENT_ADD_COLOR ( gl_src_color gl_one ) which gives the same effect on non-transparent-materials. Following the unspoken guide-line, lowest effect as default - added LensFlareSceneNode (from forum user gammaray, modified to work ) showing in example special fx - changed SceneNode Skydome f64 to f32, - AnimatedMesh -Debug Data: mesh normals didn't rotate with the scenenode fixed ( matrix-multiplication order) - Camera SceneNode setPosition Camera now finally allow to change position and target and updates all effected animators.. a call to OnAnimate ( ) lastime < time or OnAnimate ( 0 ) will reset the camera and fr. the collision animator to a new position - Device: added the current mousebutton state to the Mouse Event so i need to get the current mouse state from the OS -a dded to CIrrDeviceWin32 TODO: - Linux and SDL Device - GUI - CGUIFont: - added virtual void setInvisibleCharacters( const wchar_t *s ) = 0; define which characters should not be drawn ( send to driver) by the font. for example " " would not draw any space which is usually blank in most fonts and saves rendering of ususally full blank alpha-sprites. This saves a lot of rendering... default: setInvisibleCharacters ( L" " ); - added MultiLine rendering should avoid to us CStaticText breaking text in future - CGUIListBox - changed Scrollbar LargeStepSize to ItemHeight which easy enables to scroll line by line - CGUIScrollBar bug: Create a Window and inside a listbox with a scrollbar or a windowed irrlicht application Click & hold Scrollbar Slider. move outside it's region. Release Mouse. Go Back to Scrollbar.. it's moving always... it's generally missing the event PRESSED_MOVED, which leads to problem when an element is dragging, has a focus, or position loose and gets focus back again. ( think of a drunken mouse sliding left&right during tracking ) so added the mouse Input Buttonstates on every mouse event IrrDeviceWin32: added event.MouseInput.ButtonStates = wParam & ( MK_LBUTTON | MK_RBUTTON | MK_MBUTTON ); TODO: Linux & SDL so now i can do this case irr::EMIE_MOUSE_MOVED: if ( !event.MouseInput.isLeftPressed () ) { Dragging = false; } - bug: Scrollbar notifyListBox notify when the scrollbar is clicked. - changed timed event in draw to OnPostRender Why the hell is a gui element firing a timed event in a draw routine!!!!!. This should be corrected for all gui-elements. - added GUI Image List from Reinhard Ostermeier, modified to work added GUI Tree View from Reinhard Ostermeier, modified to work shown in the Quake3MapShader Example TODO: Spritebanks - FileOpenDialog changed the static text for the filename to an edit box. - changed the interface for addEditBox to match with addStaticText - changed the interface for addSpinBox to match with addEditBox - added MouseWheel to Spinbox - changed CGUITable CLICK_AREA from 3 to 12 to enable clicking on the visible marker - CGUISpritebank removed some crashes with empty Sprite banks - IGUIScrollBar added SetMin before min was always 0 changed ScrollWheel Direction on horizontal to move right on wheel up, left on wheel down - IComboBox -added ItemData - removed IsVisbile check in IGUIElement::draw - Image Loaders - added TGA file type 2 ( grayscale uncompressed ) - added TGA file type (1) 8 Bit indexed color uncompressed ColorConverter: - added convert_B8G8R8toA8R8G8B8 - added convert_B8G8R8A8toA8R8G8B8 - Media Files - added missing shaders and textures to map-20kdm2. Taken from free implementation - ball.wav. adjusted DC-Offset, amplified to -4dB, trim cross-zero - impact.wav clip-restoration, trim cross-zero - added gun.md2, gun.pcx to media-files copyright issues!. i don't know from where this file came from... i hope this is not from original quake2.. - added new irrlicht logo irrlicht3.png i've taken the new layout. i should ask niko to use it. - added Skydome picture to media files (skydome2.jpg) half/sphere - OctTree -added #define OCTTREE_PARENTTEST ( default: disabled ) used to leave-out children test if the parent passed a complete frustum. plus: leaves out children test minus: all edges have to be checked - added MesBuffer Hardware Hint Vertex to octtree - CQuake3ShaderSceneNode: - removed function releaseMesh Shader doesn't copy the original mesh anymore ( saving memory ) so therefore this (for others often misleading ) function was removed - changed constructor to take a (shared) destination meshbuffer for rendering reducing vertex-memory to a half - don't copy the original vertices anymore - added deformvertexes autosprite - added deformvertexes move - added support for RTCW and Raven BSPs ( qmap2 ) - added polygonoffset (TODO: not perfect) - added added nomipmaps - added rgbgen const - added alphagen - added MesBuffer Hardware Hint Vertex/Index to Quake3: static geometry, dynamic indices - added Quake3Explorer examples - added wave noise - added tcmod transform - added whiteimage - added collision to Quake3Explorer - renamed SMD3QuaterionTag* to SMD3QuaternionTag* ( typo ) - updated quake3:blendfunc - added crouch to Quake3Explorer (modifying the ellipsiodRadius of the camera animator ) added crouch to CSceneNodeAnimatorCameraFPS still problems with stand up and collision - Quake3MapLoader modified memory allocation for faster loading - Quake3LoadParam added Parameter to the Mesh-Loader - added The still existing missing caulking of curved surfaces. using round in the coordinates doesn't solve the problem. but for the demo bsp mesh it solves the problem... (luck) so for now it's switchable. TJUNCTION_SOLVER_ROUND default:off - BurningVideo - pushed BurningsVideo to 0.40 - added blendfunc gl_one_minus_dst_alpha gl_one - added blendfunc gl_dst_color gl_zero - added blendfunc gl_dst_color src_alpha - modified AlphaChannel_Ref renderer to support alpha test lessequal - addded 32 Bit Index Buffer - added sourceRect/destRect check to 2D-Blitter ( slower, but resolves crash ) - added setTextureCreationFlag video::ETCF_ALLOW_NON_POWER_2 Burning checks this flag and when set, it bypasses the power2 size check, which is necessary on 3D but can be avoided on 2D. used on fonts automatically. - added Support for Destination Alpha - OpenGL - Fixed a bug in COpenGLExtensenionHandler where a glint was downcasted to u8!!!!!! MaxTextureSize=static_cast(num); - TODO: COpenGLMaterialRenderer_ONETEXTURE_BLEND to work as expected - Direct3D8 - compile and links again - added 32 Bit Index Buffer - D3DSAMP_MIPMAPLODBIAS doesnt compile!. it is d3d9 i think. - compile for XBOX - Direc3D9 - fixed crash on RTT Textures DepthBuffer freed twice. added deleteAllTextures to destuctor - NullDriver - removeallTextures. added setMaterial ( SMaterial() ) to clean pointers for freed textures git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@2148 dfc29bdd-3216-0410-991c-e03cc46cb475 --- ...loWorld for Windows Mobile on PC_v8.vcproj | 185 ++++++ include/IFileArchive.h | 109 ++++ include/IGUIImageList.h | 45 ++ include/IGUIListBox.h | 7 + include/IGUITreeView.h | 277 +++++++++ include/IQ3Shader.h | 541 +++++++++++++----- include/ISceneManager.h | 345 +++++------ include/IVideoDriver.h | 35 +- media/irrlichtlogo3.png | Bin 0 -> 8225 bytes media/skydome2.jpg | Bin 0 -> 62534 bytes media/smoke2.jpg | Bin 0 -> 22618 bytes media/smoke3.jpg | Bin 0 -> 23083 bytes 12 files changed, 1193 insertions(+), 351 deletions(-) create mode 100644 examples/17.HelloWorld_Mobile/17. HelloWorld for Windows Mobile on PC_v8.vcproj create mode 100644 include/IFileArchive.h create mode 100644 include/IGUIImageList.h create mode 100644 include/IGUITreeView.h create mode 100644 media/irrlichtlogo3.png create mode 100644 media/skydome2.jpg create mode 100644 media/smoke2.jpg create mode 100644 media/smoke3.jpg diff --git a/examples/17.HelloWorld_Mobile/17. HelloWorld for Windows Mobile on PC_v8.vcproj b/examples/17.HelloWorld_Mobile/17. HelloWorld for Windows Mobile on PC_v8.vcproj new file mode 100644 index 00000000..21a1905b --- /dev/null +++ b/examples/17.HelloWorld_Mobile/17. HelloWorld for Windows Mobile on PC_v8.vcproj @@ -0,0 +1,185 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/include/IFileArchive.h b/include/IFileArchive.h new file mode 100644 index 00000000..dc57bedd --- /dev/null +++ b/include/IFileArchive.h @@ -0,0 +1,109 @@ +// Copyright (C) 2002-2009 Nikolaus Gebhardt/ Thomas Alten +// This file is part of the "Irrlicht Engine". +// For conditions of distribution and use, see copyright notice in irrlicht.h + +#ifndef __I_FILE_ARCHIVE_H_INCLUDED__ +#define __I_FILE_ARCHIVE_H_INCLUDED__ + +#include "IReadFile.h" + +namespace irr +{ + +namespace io +{ + +//! FileSystemType: which Filesystem should be used for e.q browsing +enum eFileSystemType +{ + FILESYSTEM_NATIVE = 0, // Native OS FileSystem + FILESYSTEM_VIRTUAL, // Virtual FileSystem +}; + + +//! Base Info which all File archives must support on browsing +struct IFileArchiveEntry +{ + IFileArchiveEntry () {} + + core::string simpleFileName; + core::string path; + + bool operator < (const IFileArchiveEntry& other) const + { + return simpleFileName < other.simpleFileName; + } + + bool operator == (const IFileArchiveEntry& other) const + { + return simpleFileName == other.simpleFileName; + } +}; + +//! The FileArchive manages files and archives and provides access to them. +/** It manages where files are, so that modules which use the the IO do not +need to know where every file is located. A file could be in a .zip-Archive or +as file on disk, using the IFileSystem makes no difference to this. */ +struct IFileArchive : public virtual IReferenceCounted +{ + + //! return the id of the file Archive + virtual const core::string& getArchiveName () = 0; + + //! get the class Type + virtual const core::string& getArchiveType() = 0; + + //! opens a file by file name + virtual IReadFile* openFile(const core::string& filename) = 0; + + //! returns fileindex + virtual s32 findFile(const core::string& filename) = 0; + + //! Returns the amount of files in the filelist. + /** \return Amount of files and directories in the file list. */ + virtual u32 getFileCount() = 0; + + //! returns data of known file + virtual const IFileArchiveEntry* getFileInfo(u32 index) = 0; + +}; + +//! Class which is able to create an archive from a file. +/** If you want the Irrlicht Engine be able to load archives of +currently unsupported file formats (e.g .wad), then implement +this and add your new Archive loader with +IFileSystem::addArchiveLoader() to the engine. */ +struct IArchiveLoader : public virtual IReferenceCounted +{ + //! Check if the file might be loaded by this class + /** Check is based on the file extension (e.g. ".zip") + \param fileName Name of file to check. + \return True if file seems to be loadable. */ + virtual bool isALoadableFileFormat(const core::string& filename) const = 0; + + //! Creates an archive from the filename + /** \param file File handle to check. + \return Pointer to newly created archive, or 0 upon error. */ + virtual IFileArchive* createArchive(const core::string& filename, bool ignoreCase, bool ignorePaths) const = 0; + + //! Check if the file might be loaded by this class + /** Check might look into the file. + \param file File handle to check. + \return True if file seems to be loadable. */ + virtual bool isALoadableFileFormat(io::IReadFile* file) const = 0; + + //! Creates an archive from the file + /** \param file File handle to check. + \return Pointer to newly created archive, or 0 upon error. */ + virtual IFileArchive* createArchive(io::IReadFile* file, bool ignoreCase, bool ignorePaths) const = 0; + + +}; + + +} // end namespace io +} // end namespace irr + + +#endif + diff --git a/include/IGUIImageList.h b/include/IGUIImageList.h new file mode 100644 index 00000000..ee6ec879 --- /dev/null +++ b/include/IGUIImageList.h @@ -0,0 +1,45 @@ +// This file is part of the "Irrlicht Engine". +// written by Reinhard Ostermeier, reinhard@nospam.r-ostermeier.de + +#ifndef __I_GUI_IMAGE_LIST_H_INCLUDED__ +#define __I_GUI_IMAGE_LIST_H_INCLUDED__ + +#include "IGUIElement.h" +#include "rect.h" +#include "irrTypes.h" + +namespace irr +{ +namespace gui +{ + +//! Font interface. +class IGUIImageList : public virtual IReferenceCounted +{ +public: + + //! Destructor + virtual ~IGUIImageList() {}; + + //! Draws an image and clips it to the specified rectangle if wanted + //! \param index: Index of the image + //! \param destPos: Position of the image to draw + //! \param clip: Optional pointer to a rectalgle against which the text will be clipped. + //! If the pointer is null, no clipping will be done. + virtual void draw( s32 index, const core::position2d& destPos, + const core::rect* clip = 0 ) = 0; + + //! Returns the count of Images in the list. + //! \return Returns the count of Images in the list. + virtual s32 getImageCount() const = 0; + + //! Returns the size of the images in the list. + //! \return Returns the size of the images in the list. + virtual core::dimension2d getImageSize() const = 0; +}; + +} // end namespace gui +} // end namespace irr + +#endif + diff --git a/include/IGUIListBox.h b/include/IGUIListBox.h index 26fa089d..e4cac7d7 100644 --- a/include/IGUIListBox.h +++ b/include/IGUIListBox.h @@ -75,6 +75,9 @@ namespace gui //! sets the selected item. Set this to -1 if no item should be selected virtual void setSelected(s32 index) = 0; + //! sets the selected item. Set this to 0 if no item should be selected + virtual void setSelected(const wchar_t *item) = 0; + //! set whether the listbox should scroll to new or newly selected items virtual void setAutoScrollEnabled(bool scroll) = 0; @@ -111,6 +114,10 @@ namespace gui //! Swap the items at the given indices virtual void swapItems(u32 index1, u32 index2) = 0; + + //! set global itemHeight + virtual void setItemHeight( s32 height ) = 0; + }; diff --git a/include/IGUITreeView.h b/include/IGUITreeView.h new file mode 100644 index 00000000..2d2cace2 --- /dev/null +++ b/include/IGUITreeView.h @@ -0,0 +1,277 @@ +// This file is part of the "Irrlicht Engine". +// written by Reinhard Ostermeier, reinhard@nospam.r-ostermeier.de + +#ifndef __I_GUI_TREE_VIEW_H_INCLUDED__ +#define __I_GUI_TREE_VIEW_H_INCLUDED__ + +#include "IGUIElement.h" +#include "IGUIImageList.h" +#include "irrTypes.h" + +namespace irr +{ +namespace gui +{ + class IGUIFont; + class IGUITreeView; + + + //! Node for gui tree view + class IGUITreeViewNode : public IReferenceCounted + { + public: + //! constructor + IGUITreeViewNode() {} + + //! destructor + virtual ~IGUITreeViewNode() {} + + //! returns the owner (tree view) of this node + virtual IGUITreeView* getOwner() const = 0; + + //! Returns the parent node of this node. + //! For the root node this will return 0. + virtual IGUITreeViewNode* getParent() const = 0; + + //! returns the text of the node + virtual const wchar_t* getText() const = 0; + + //! sets the text of the node + virtual void setText( const wchar_t* text ) = 0; + + //! returns the icon text of the node + virtual const wchar_t* getIcon() const = 0; + + //! sets the icon text of the node + virtual void setIcon( const wchar_t* icon ) = 0; + + //! returns the image index of the node + virtual u32 getImageIndex() const = 0; + + //! sets the image index of the node + virtual void setImageIndex( u32 imageIndex ) = 0; + + //! returns the image index of the node + virtual u32 getSelectedImageIndex() const = 0; + + //! sets the image index of the node + virtual void setSelectedImageIndex( u32 imageIndex ) = 0; + + //! returns the user data (void*) of this node + virtual void* getData() const = 0; + + //! sets the user data (void*) of this node + virtual void setData( void* data ) = 0; + + //! returns the user data2 (IReferenceCounted) of this node + virtual IReferenceCounted* getData2() const = 0; + + //! sets the user data2 (IReferenceCounted) of this node + virtual void setData2( IReferenceCounted* data ) = 0; + + //! returns the child item count + virtual u32 getChildCount() const = 0; + + //! removes all childs (recursive) from this node + virtual void clearChilds() = 0; + + //! returns true if this node has child nodes + virtual bool hasChilds() const = 0; + + //! Adds a new node behind the last child node. + //! \param text text of the new node + //! \param icon icon text of the new node + //! \param imageIndex index of the image for the new node (-1 = none) + //! \param selectedImageIndex index of the selected image for the new node (-1 = same as imageIndex) + //! \param data user data (void*) of the new node + //! \param data2 user data2 (IReferenceCounted*) of the new node + //! \return + //! returns the new node + virtual IGUITreeViewNode* addChildBack( + const wchar_t* text, + const wchar_t* icon = 0, + s32 imageIndex = -1, + s32 selectedImageIndex = -1, + void* data = 0, + IReferenceCounted* data2 = 0 ) = 0; + + //! Adds a new node before the first child node. + //! \param text text of the new node + //! \param icon icon text of the new node + //! \param imageIndex index of the image for the new node (-1 = none) + //! \param selectedImageIndex index of the selected image for the new node (-1 = same as imageIndex) + //! \param data user data (void*) of the new node + //! \param data2 user data2 (IReferenceCounted*) of the new node + //! \return + //! returns the new node + virtual IGUITreeViewNode* addChildFront( + const wchar_t* text, + const wchar_t* icon = 0, + s32 imageIndex = -1, + s32 selectedImageIndex = -1, + void* data = 0, + IReferenceCounted* data2 = 0 ) = 0; + + //! Adds a new node behind the other node. + //! The other node has also te be a child node from this node. + //! \param text text of the new node + //! \param icon icon text of the new node + //! \param imageIndex index of the image for the new node (-1 = none) + //! \param selectedImageIndex index of the selected image for the new node (-1 = same as imageIndex) + //! \param data user data (void*) of the new node + //! \param data2 user data2 (IReferenceCounted*) of the new node + //! \return + //! returns the new node or 0 if other is no child node from this + virtual IGUITreeViewNode* insertChildAfter( + IGUITreeViewNode* other, + const wchar_t* text, + const wchar_t* icon = 0, + s32 imageIndex = -1, + s32 selectedImageIndex = -1, + void* data = 0, + IReferenceCounted* data2 = 0 ) = 0; + + //! Adds a new node before the other node. + //! The other node has also te be a child node from this node. + //! \param text text of the new node + //! \param icon icon text of the new node + //! \param imageIndex index of the image for the new node (-1 = none) + //! \param selectedImageIndex index of the selected image for the new node (-1 = same as imageIndex) + //! \param data user data (void*) of the new node + //! \param data2 user data2 (IReferenceCounted*) of the new node + //! \return + //! returns the new node or 0 if other is no child node from this + virtual IGUITreeViewNode* insertChildBefore( + IGUITreeViewNode* other, + const wchar_t* text, + const wchar_t* icon = 0, + s32 imageIndex = -1, + s32 selectedImageIndex = -1, + void* data = 0, + IReferenceCounted* data2 = 0 ) = 0; + + //! Return the first child node from this node. + //! \return + //! Returns the first child node or 0 if this node has no childs. + virtual IGUITreeViewNode* getFirstChild() const = 0; + + //! Return the last child node from this node. + //! \return + //! Returns the last child node or 0 if this node has no childs. + virtual IGUITreeViewNode* getLastChild() const = 0; + + //! Returns the preverse sibling node from this node. + //! \return + //! Returns the preverse sibling node from this node or 0 if this is + //! the first node from the parent node. + virtual IGUITreeViewNode* getPrevSibling() const = 0; + + //! Returns the next sibling node from this node. + //! \return + //! Returns the next sibling node from this node or 0 if this is + //! the last node from the parent node. + virtual IGUITreeViewNode* getNextSibling() const = 0; + + //! Returns the next visible (expanded, may be out of scrolling) node from this node. + //! \return + //! Returns the next visible node from this node or 0 if this is + //! the last visible node. + virtual IGUITreeViewNode* getNextVisible() const = 0; + + //! Deletes a child node. + // \return + //! Returns true if the node was found as a child and is deleted. + virtual bool deleteChild( IGUITreeViewNode* child ) = 0; + + //! Moves a child node one position up. + //! \return + //! Returns true if the node was found as achild node and was not already the first child. + virtual bool moveChildUp( IGUITreeViewNode* child ) = 0; + + //! Moves a child node one position down. + //! \return + //! Returns true if the node was found as achild node and was not already the last child. + virtual bool moveChildDown( IGUITreeViewNode* child ) = 0; + + //! Returns true if the node is expanded (childs are visible). + virtual bool getExpanded() const = 0; + + //! Sets if the node is expanded. + virtual void setExpanded( bool expanded ) = 0; + + //! Returns true if the node is currently selected. + virtual bool getSelected() const = 0; + + //! Sets this node as selected. + virtual void setSelected( bool selected ) = 0; + + //! Returns true if this node is the root node. + virtual bool isRoot() const = 0; + + //! Returns the level of this node. + //! The root node has level 0. Direct childs of the root has level 1 ... + virtual s32 getLevel() const = 0; + + //! Returns true if this node is visible (all parents are expanded). + virtual bool isVisible() const = 0; + }; + + + //! Default tree view GUI element. + //! Displays a windows like tree buttons to expand/collaps the child nodes of an node + //! and optional tree lines. + //! Each node consits of an text, an icon text and a void pointer for user data. + class IGUITreeView : public IGUIElement + { + public: + //! constructor + IGUITreeView( IGUIEnvironment* environment, IGUIElement* parent, s32 id, + core::rect rectangle ) + : IGUIElement( EGUIET_TREE_VIEW, environment, parent, id, rectangle ) {} + + //! destructor + virtual ~IGUITreeView() {} + + //! returns the root node (not visible) from the tree. + virtual IGUITreeViewNode* getRoot() const = 0; + + //! returns the selected node of the tree or 0 if none is selected + virtual IGUITreeViewNode* getSelected() const = 0; + + //! returns true if the tree lines are visible + virtual bool getLinesVisible() const = 0; + + //! sets if the tree lines are visible + //! \param visible true for visible, false for invisible + virtual void setLinesVisible( bool visible ) = 0; + + //! Sets the font which should be used as icon font. This font is set to the Irrlicht engine + //! built-in-font by default. Icons can be displayed in front of every list item. + //! An icon is a string, displayed with the icon font. When using the build-in-font of the + //! Irrlicht engine as icon font, the icon strings defined in GUIIcons.h can be used. + virtual void setIconFont( IGUIFont* font ) = 0; + + //! Sets the image list which should be used for the image and selected image of every node. + //! The default is 0 (no images). + virtual void setImageList( IGUIImageList* imageList ) = 0; + + //! Returns the image list which is used for the nodes. + virtual IGUIImageList* getImageList() const = 0; + + //! Sets if the image is left of the icon. Default is true. + virtual void setImageLeftOfIcon( bool bLeftOf ) = 0; + + //! Returns if the Image is left of the icon. Default is true. + virtual bool getImageLeftOfIcon() const = 0; + + //! Returns the node which is associated to the last event. + //! This pointer is only valid inside the OnEvent call! + virtual IGUITreeViewNode* getLastEventNode() const = 0; + }; + + +} // end namespace gui +} // end namespace irr + +#endif + diff --git a/include/IQ3Shader.h b/include/IQ3Shader.h index fbaf1deb..2e8954e0 100644 --- a/include/IQ3Shader.h +++ b/include/IQ3Shader.h @@ -18,7 +18,7 @@ namespace scene namespace quake3 { - static const core::stringc irrEmptyStringc(""); + static core::stringc irrEmptyStringc(""); //! Hold the different Mesh Types used for getMesh enum eQ3MeshIndex @@ -26,47 +26,63 @@ namespace quake3 E_Q3_MESH_GEOMETRY = 0, E_Q3_MESH_ITEMS, E_Q3_MESH_BILLBOARD, + E_Q3_MESH_FOG, + E_Q3_MESH_UNRESOLVED, E_Q3_MESH_SIZE }; - // we are not using gamma, so quake3 is very dark. - // define the standard multiplication for lightmaps and vertex colors - const video::E_MATERIAL_TYPE defaultMaterialType = video::EMT_LIGHTMAP_M4; - const video::E_MODULATE_FUNC defaultModulate = video::EMFN_MODULATE_4X; + /*! used to customize Quake3 BSP Loader + */ + + struct Q3LevelLoadParameter + { + Q3LevelLoadParameter () + :defaultLightMapMaterial ( video::EMT_LIGHTMAP_M4 ), + defaultModulate ( video::EMFN_MODULATE_4X ), + patchTesselation ( 8 ), + verbose ( 0 ), + startTime ( 0 ), endTime ( 0 ), + mergeShaderBuffer ( 1 ), + cleanUnResolvedMeshes ( 1 ), + loadAllShaders ( 0 ), + loadSkyShader ( 0 ), + swapLump ( 0 ), + alpharef ( 1 ), + #ifdef __BIG_ENDIAN__ + swapHeader ( 1 ) + #else + swapHeader ( 0 ) + #endif + { + memcpy ( scriptDir, "scripts\x0", 8 ); + } + + video::E_MATERIAL_TYPE defaultLightMapMaterial; + video::E_MODULATE_FUNC defaultModulate; + s32 patchTesselation; + s32 verbose; + u32 startTime; + u32 endTime; + s32 mergeShaderBuffer; + s32 cleanUnResolvedMeshes; + s32 alpharef; + s32 swapLump; + s32 swapHeader; + s32 loadAllShaders; + s32 loadSkyShader; + c8 scriptDir [ 64 ]; + }; // some useful typedefs typedef core::array< core::stringc > tStringList; typedef core::array< video::ITexture* > tTexArray; - // name = "a b c .." - struct SVariable - { - core::stringc name; - core::stringc content; - - void clear () - { - name = ""; - content = ""; - } - - s32 isValid () const - { - return name.size(); - } - - bool operator == ( const SVariable &other ) const - { - return name == other.name; - } - }; - // string helper.. TODO: move to generic files - inline s32 isEqual ( const core::stringc &string, u32 &pos, const c8 *list[], u32 listSize ) + inline s16 isEqual ( const core::stringc &string, u32 &pos, const c8 *list[], u16 listSize ) { const char * in = string.c_str () + pos; - for ( u32 i = 0; i != listSize; ++i ) + for ( u16 i = 0; i != listSize; ++i ) { if (string.size() < pos) return -2; @@ -79,7 +95,7 @@ namespace quake3 continue; pos += len + 1; - return (s32) i; + return (s16) i; } return -2; } @@ -93,6 +109,7 @@ namespace quake3 return value; } + //! get a quake3 vector translated to irrlicht position (x,-z,y ) inline core::vector3df getAsVector3df ( const core::stringc &string, u32 &pos ) { core::vector3df v; @@ -135,31 +152,33 @@ namespace quake3 //! A blend function for a q3 shader. struct SBlendFunc { - SBlendFunc () - : type ( video::EMT_SOLID ), modulate ( defaultModulate ), param ( 0.f ), - isTransparent ( false ) {} + SBlendFunc ( video::E_MODULATE_FUNC mod ) + : type ( video::EMT_SOLID ), modulate ( mod ), + param0( 0.f ), + isTransparent ( 0 ) {} video::E_MATERIAL_TYPE type; video::E_MODULATE_FUNC modulate; - f32 param; - bool isTransparent; + f32 param0; + u32 isTransparent; }; // parses the content of Variable cull - inline bool isDisabled ( const core::stringc &string ) + inline bool getCullingFunction ( const core::stringc &cull ) { - if ( string.size() == 0 ) + if ( cull.size() == 0 ) return true; bool ret = true; - static const c8 * funclist[] = { "none", "disable" }; + static const c8 * funclist[] = { "none", "disable", "twosided" }; u32 pos = 0; - switch ( isEqual ( string, pos, funclist, 2 ) ) + switch ( isEqual ( cull, pos, funclist, 3 ) ) { case 0: case 1: + case 2: ret = false; break; } @@ -168,12 +187,13 @@ namespace quake3 // parses the content of Variable depthfunc // return a z-test - inline u32 getDepthFunction ( const core::stringc &string ) + inline u8 getDepthFunction ( const core::stringc &string ) { - if ( string.size() == 0 ) - return 1; + u8 ret = video::EMDF_DEPTH_LESS_EQUAL; + + if ( string.size() == 0 ) + return ret; - u32 ret = 1; static const c8 * funclist[] = { "lequal","equal" }; u32 pos = 0; @@ -189,7 +209,17 @@ namespace quake3 } - // parses the content of Variable blendfunc,alphafunc + /*! + parses the content of Variable blendfunc,alphafunc + it also make a hint for rendering as transparent or solid node. + + we assume a typical quake scene would look like this.. + 1) Big Static Mesh ( solid ) + 2) static scene item ( may use transparency ) but rendered in the solid pass + 3) additional transparency item in the transparent pass + + it's not 100% accurate! it just empirical.. + */ inline static void getBlendFunc ( const core::stringc &string, SBlendFunc &blendfunc ) { if ( string.size() == 0 ) @@ -215,7 +245,7 @@ namespace quake3 "blend", "ge128", - "gt0" + "gt0", }; @@ -230,20 +260,32 @@ namespace quake3 switch ( srcFact ) { + case video::EBF_ZERO: + switch ( dstFact ) + { + // gl_zero gl_src_color == gl_dst_color gl_zero + case video::EBF_SRC_COLOR: + blendfunc.type = video::EMT_ONETEXTURE_BLEND; + blendfunc.param0 = video::pack_texureBlendFunc ( video::EBF_DST_COLOR, video::EBF_ZERO, blendfunc.modulate ); + blendfunc.isTransparent = 1; + resolved = 1; + break; + } break; + case video::EBF_ONE: switch ( dstFact ) { // gl_one gl_zero case video::EBF_ZERO: blendfunc.type = video::EMT_SOLID; - blendfunc.isTransparent = false; + blendfunc.isTransparent = 0; resolved = 1; break; // gl_one gl_one case video::EBF_ONE: blendfunc.type = video::EMT_TRANSPARENT_ADD_COLOR; - blendfunc.isTransparent = true; + blendfunc.isTransparent = 1; resolved = 1; break; } break; @@ -254,8 +296,8 @@ namespace quake3 // gl_src_alpha gl_one_minus_src_alpha case video::EBF_ONE_MINUS_SRC_ALPHA: blendfunc.type = video::EMT_TRANSPARENT_ALPHA_CHANNEL; - blendfunc.param = 1.f / 255.f; - blendfunc.isTransparent = true; + blendfunc.param0 = 1.f/255.f; + blendfunc.isTransparent = 1; resolved = 1; break; } break; @@ -263,74 +305,133 @@ namespace quake3 case 11: // add blendfunc.type = video::EMT_TRANSPARENT_ADD_COLOR; - blendfunc.isTransparent = true; + blendfunc.isTransparent = 1; resolved = 1; break; case 12: - // filter = gl_dst_color gl_zero + // filter = gl_dst_color gl_zero or gl_zero gl_src_color blendfunc.type = video::EMT_ONETEXTURE_BLEND; - blendfunc.param = video::pack_texureBlendFunc ( video::EBF_DST_COLOR, video::EBF_ZERO, defaultModulate ); - blendfunc.isTransparent = false; + blendfunc.param0 = video::pack_texureBlendFunc ( video::EBF_DST_COLOR, video::EBF_ZERO, blendfunc.modulate ); + blendfunc.isTransparent = 1; resolved = 1; break; case 13: - // blend + // blend = gl_src_alpha gl_one_minus_src_alpha blendfunc.type = video::EMT_TRANSPARENT_ALPHA_CHANNEL; - blendfunc.param = 1.f / 255.f; - blendfunc.isTransparent = true; + blendfunc.param0 = 1.f/255.f; + blendfunc.isTransparent = 1; resolved = 1; break; case 14: // alphafunc ge128 - blendfunc.type = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; - blendfunc.param = 0.5f; - blendfunc.isTransparent = true; + blendfunc.type = video::EMT_TRANSPARENT_ALPHA_CHANNEL; + blendfunc.param0 = 0.5f; + blendfunc.isTransparent = 1; resolved = 1; break; case 15: // alphafunc gt0 - blendfunc.type = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; - blendfunc.param = 1.f / 255.f; - blendfunc.isTransparent = true; + blendfunc.type = video::EMT_TRANSPARENT_ALPHA_CHANNEL; + blendfunc.param0 = 1.f / 255.f; + blendfunc.isTransparent = 1; resolved = 1; break; + } // use the generic blender if ( 0 == resolved ) { blendfunc.type = video::EMT_ONETEXTURE_BLEND; - blendfunc.param = video::pack_texureBlendFunc ( + blendfunc.param0 = video::pack_texureBlendFunc ( (video::E_BLEND_FACTOR) srcFact, (video::E_BLEND_FACTOR) dstFact, blendfunc.modulate); - if (srcFact == video::EBF_SRC_COLOR && dstFact == video::EBF_ZERO) - { - blendfunc.isTransparent = 0; - } - else - { - blendfunc.isTransparent = true; - } - + blendfunc.isTransparent = 1; } } + // random noise [-1;1] + struct Noiser + { + static f32 get () + { + static u32 RandomSeed = 0x69666966; + RandomSeed = (RandomSeed * 3631 + 1); + + f32 value = ( (f32) (RandomSeed & 0x7FFF ) * (1.0f / (f32)(0x7FFF >> 1) ) ) - 1.f; + return value; + } + }; + + enum eQ3ModifierFunction + { + TCMOD = 0, + DEFORMVERTEXES = 1, + RGBGEN = 2, + TCGEN = 3, + MAP = 4, + ALPHAGEN = 5, + + FUNCTION2 = 0x10, + SCROLL = FUNCTION2 + 1, + SCALE = FUNCTION2 + 2, + ROTATE = FUNCTION2 + 3, + STRETCH = FUNCTION2 + 4, + TURBULENCE = FUNCTION2 + 5, + WAVE = FUNCTION2 + 6, + + IDENTITY = FUNCTION2 + 7, + VERTEX = FUNCTION2 + 8, + TEXTURE = FUNCTION2 + 9, + LIGHTMAP = FUNCTION2 + 10, + ENVIRONMENT = FUNCTION2 + 11, + $LIGHTMAP = FUNCTION2 + 12, + BULGE = FUNCTION2 + 13, + AUTOSPRITE = FUNCTION2 + 14, + AUTOSPRITE2 = FUNCTION2 + 15, + TRANSFORM = FUNCTION2 + 16, + EXACTVERTEX = FUNCTION2 + 17, + CONSTANT = FUNCTION2 + 18, + LIGHTINGSPECULAR = FUNCTION2 + 19, + MOVE = FUNCTION2 + 20, + NORMAL = FUNCTION2 + 21, + IDENTITYLIGHTING = FUNCTION2 + 22, + + WAVE_MODIFIER_FUNCTION = 0x30, + SINUS = WAVE_MODIFIER_FUNCTION + 1, + COSINUS = WAVE_MODIFIER_FUNCTION + 2, + SQUARE = WAVE_MODIFIER_FUNCTION + 3, + TRIANGLE = WAVE_MODIFIER_FUNCTION + 4, + SAWTOOTH = WAVE_MODIFIER_FUNCTION + 5, + SAWTOOTH_INVERSE = WAVE_MODIFIER_FUNCTION + 6, + NOISE = WAVE_MODIFIER_FUNCTION + 7, + + + UNKNOWN = -2 + + }; + struct SModifierFunction { SModifierFunction () - : masterfunc0 ( -2 ), masterfunc1(0), func ( 0 ), - tcgen( 8 ), base ( 0 ), amp ( 1 ), phase ( 0 ), freq ( 1 ), wave(1) {} + : masterfunc0 ( UNKNOWN ), masterfunc1( UNKNOWN ), func ( SINUS ), + tcgen( TEXTURE ), rgbgen ( IDENTITY ), alphagen ( UNKNOWN ), + base ( 0 ), amp ( 1 ), phase ( 0 ), frequency ( 1 ), + wave ( 1 ), + x ( 0 ), y ( 0 ), z( 0 ), count( 0 ) {} // "tcmod","deformvertexes","rgbgen", "tcgen" - s32 masterfunc0; + eQ3ModifierFunction masterfunc0; // depends - s32 masterfunc1; + eQ3ModifierFunction masterfunc1; // depends - s32 func; + eQ3ModifierFunction func; - s32 tcgen; + eQ3ModifierFunction tcgen; + eQ3ModifierFunction rgbgen; + eQ3ModifierFunction alphagen; union { @@ -348,44 +449,50 @@ namespace quake3 union { - f32 freq; + f32 frequency; f32 bulgespeed; }; - f32 wave; + union + { + f32 wave; + f32 div; + }; + + f32 x; + f32 y; + f32 z; + u32 count; f32 evaluate ( f32 dt ) const { // phase in 0 and 1.. - f32 x = core::fract( (dt + phase ) * freq ); + f32 x = core::fract( (dt + phase ) * frequency ); f32 y = 0.f; switch ( func ) { - // sin - case 0: - y = (f32) sin ( x * core::PI64 * 2.0 ); + case SINUS: + y = sinf ( x * core::PI * 2.f ); break; - // cos - case 1: - y = (f32) cos ( x * core::PI64 * 2.0 ); + case COSINUS: + y = cosf ( x * core::PI * 2.f ); break; - // square - case 2: + case SQUARE: y = x < 0.5f ? 1.f : -1.f; break; - // triangle - case 3: - y = x < 0.5f ? ( 2.f * x ) - 1.f : ( -2.f * x ) + 2.f; + case TRIANGLE: + y = x < 0.5f ? ( 4.f * x ) - 1.f : ( -4.f * x ) + 3.f; break; - // sawtooth: - case 4: + case SAWTOOTH: y = x; break; - // inverse sawtooth: - case 5: + case SAWTOOTH_INVERSE: y = 1.f - x; break; + case NOISE: + y = Noiser::get(); + break; } return base + ( y * amp ); @@ -394,6 +501,15 @@ namespace quake3 }; + inline core::vector3df getMD3Normal ( u32 i, u32 j ) + { + const f32 lng = i * 2.0f * core::PI / 255.0f; + const f32 lat = j * 2.0f * core::PI / 255.0f; + return core::vector3df(cosf ( lat ) * sinf ( lng ), + sinf ( lat ) * sinf ( lng ), + cosf ( lng )); + } + // inline void getModifierFunc ( SModifierFunction& fill, const core::stringc &string, u32 &pos ) { @@ -402,64 +518,110 @@ namespace quake3 static const c8 * funclist[] = { - "sin","cos","square", "triangle", "sawtooth","inversesawtooth" + "sin","cos","square", + "triangle", "sawtooth","inversesawtooth", "noise" }; - fill.func = quake3::isEqual ( string,pos, funclist,6 ); - if ( fill.func == -2 ) - fill.func = 0; + fill.func = (eQ3ModifierFunction) isEqual ( string,pos, funclist,7 ); + fill.func = fill.func == UNKNOWN ? SINUS : (eQ3ModifierFunction) ((u32) fill.func + WAVE_MODIFIER_FUNCTION + 1); - fill.base = quake3::getAsFloat ( string, pos ); - fill.amp = quake3::getAsFloat ( string, pos ); - fill.phase = quake3::getAsFloat ( string, pos ); - fill.freq = quake3::getAsFloat ( string, pos ); + fill.base = getAsFloat ( string, pos ); + fill.amp = getAsFloat ( string, pos ); + fill.phase = getAsFloat ( string, pos ); + fill.frequency = getAsFloat ( string, pos ); } + // name = "a b c .." + struct SVariable + { + core::stringc name; + core::stringc content; + SVariable ( const c8 * n, const c8 *c = 0 ) : name ( n ), content (c) {} + virtual ~SVariable () {} + + void clear () + { + name = ""; + content = ""; + } + + s32 isValid () const + { + return name.size(); + } + + bool operator == ( const SVariable &other ) const + { + return 0 == strcmp ( name.c_str(), other.name.c_str () ); + } + + bool operator < ( const SVariable &other ) const + { + return 0 > strcmp ( name.c_str(), other.name.c_str () ); + } + + }; + + + // string database. "a" = "Hello", "b" = "1234.6" struct SVarGroup { - // simple assoziative array - s32 getIndex( const c8 * name ) const - { - SVariable search; - search.name = name; + SVarGroup () {} + virtual ~SVarGroup () {} - return Variable.linear_search ( search ); + u32 isDefined ( const c8 * name, const c8 * content = 0 ) const + { + for ( u32 i = 0; i != Variable.size (); ++i ) + { + if ( 0 == strcmp ( Variable[i].name.c_str(), name ) && + ( 0 == content || strstr ( Variable[i].content.c_str(), content ) ) + ) + { + return i + 1; + } + } + return 0; } // searches for Variable name and returns is content // if Variable is not found a reference to an Empty String is returned const core::stringc &get( const c8 * name ) const { - s32 index = getIndex ( name ); + SVariable search ( name ); + s32 index = Variable.linear_search ( search ); if ( index < 0 ) return irrEmptyStringc; return Variable [ index ].content; } - bool isDefined ( const c8 * name, const c8 * content = 0 ) const + // set the Variable name + void set ( const c8 * name, const c8 * content = 0 ) { - for ( u32 i = 0; i != Variable.size (); ++i ) + u32 index = isDefined ( name, 0 ); + if ( 0 == index ) { - if ( 0 == strcmp ( Variable[i].name.c_str(), name ) ) - { - if ( 0 == content ) - return true; - if ( 0 == strcmp ( Variable[i].content.c_str(), content ) ) - return true; - } + Variable.push_back ( SVariable ( name, content ) ); + } + else + { + Variable [ index ].content = content; } - return false; } + core::array < SVariable > Variable; }; + //! holding a group a variable struct SVarGroupList: public IReferenceCounted { - SVarGroupList () {} + SVarGroupList () + { + VariableGroup.setAllocStrategy ( core::ALLOC_STRATEGY_SAFE ); + } virtual ~SVarGroupList () {} core::array < SVarGroup > VariableGroup; @@ -467,37 +629,49 @@ namespace quake3 //! A Parsed Shader Holding Variables ordered in Groups - class SShader + struct IShader { - public: - bool operator == (const SShader &other ) const - { - return name == other.name; - } + IShader () + : id ( 0 ), VarGroup ( 0 ) {} + virtual ~IShader () {} - bool operator < (const SShader &other ) const - { - return name < other.name; - } + void operator = (const IShader &other ) + { + id = other.id; + VarGroup = other.VarGroup; + name = other.name; + } - const SVarGroup * getGroup ( u32 stage ) const - { - if ( 0 == VarGroup || stage >= VarGroup->VariableGroup.size () ) - return 0; + bool operator == (const IShader &other ) const + { + return 0 == strcmp ( name.c_str(), other.name.c_str () ); + //return name == other.name; + } - return &VarGroup->VariableGroup [ stage ]; - } + bool operator < (const IShader &other ) const + { + return strcmp ( name.c_str(), other.name.c_str () ) < 0; + //return name < other.name; + } - // id - s32 id; + const SVarGroup * getGroup ( u32 stage ) const + { + if ( 0 == VarGroup || stage >= VarGroup->VariableGroup.size () ) + return 0; - // Shader: shader name ( also first variable in first Vargroup ) - // Entity: classname ( variable in Group(1) ) - core::stringc name; - SVarGroupList *VarGroup; // reference + return &VarGroup->VariableGroup [ stage ]; + } + + // id + s32 id; + SVarGroupList *VarGroup; // reference + + // Shader: shader name ( also first variable in first Vargroup ) + // Entity: classname ( variable in Group(1) ) + core::stringc name; }; - typedef SShader SEntity; + typedef IShader SEntity; typedef core::array < SEntity > tQ3EntityList; @@ -546,16 +720,14 @@ namespace quake3 } - inline core::stringc & dumpShader ( core::stringc &dest, const SShader * shader ) + inline core::stringc & dumpShader ( core::stringc &dest, const IShader * shader ) { - dest = ""; if ( 0 == shader ) return dest; const SVarGroup * group; const u32 size = shader->VarGroup->VariableGroup.size (); - for ( u32 i = 0; i != size; ++i ) { group = &shader->VarGroup->VariableGroup[ i ]; @@ -600,15 +772,76 @@ namespace quake3 video::ITexture* texture = 0; for ( u32 g = 0; g != 2 ; ++g ) { - core::cutFilenameExtension ( loadFile, stringList[i] ).append ( extension[g] ); + core::cutFilenameExtension ( loadFile, stringList[i] ); - if ( fileSystem->existFile ( loadFile.c_str() ) ) + if ( loadFile == "$whiteimage" ) { - texture = driver->getTexture( loadFile.c_str () ); - if ( texture ) + texture = driver->getTexture( "$whiteimage" ); + if ( 0 == texture ) { - break; + core::dimension2du s ( 2, 2 ); + u32 image[4] = { 0xFFFFFFFF, 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF }; + video::IImage* w = driver->createImageFromData ( video::ECF_A8R8G8B8, s,&image ); + texture = driver->addTexture( "$whiteimage", w ); + w->drop (); } + + } + else + if ( loadFile == "$redimage" ) + { + texture = driver->getTexture( "$redimage" ); + if ( 0 == texture ) + { + core::dimension2du s ( 2, 2 ); + u32 image[4] = { 0xFFFF0000, 0xFFFF0000,0xFFFF0000,0xFFFF0000 }; + video::IImage* w = driver->createImageFromData ( video::ECF_A8R8G8B8, s,&image ); + texture = driver->addTexture( "$redimage", w ); + w->drop (); + } + } + else + if ( loadFile == "$blueimage" ) + { + texture = driver->getTexture( "$blueimage" ); + if ( 0 == texture ) + { + core::dimension2du s ( 2, 2 ); + u32 image[4] = { 0xFF0000FF, 0xFF0000FF,0xFF0000FF,0xFF0000FF }; + video::IImage* w = driver->createImageFromData ( video::ECF_A8R8G8B8, s,&image ); + texture = driver->addTexture( "$blueimage", w ); + w->drop (); + } + } + else + if ( loadFile == "$checkerimage" ) + { + texture = driver->getTexture( "$checkerimage" ); + if ( 0 == texture ) + { + core::dimension2du s ( 2, 2 ); + u32 image[4] = { 0xFFFFFFFF, 0xFF000000,0xFF000000,0xFFFFFFFF }; + video::IImage* w = driver->createImageFromData ( video::ECF_A8R8G8B8, s,&image ); + texture = driver->addTexture( "$checkerimage", w ); + w->drop (); + } + } + else + if ( loadFile == "$lightmap" ) + { + texture = 0; + } + else + { + loadFile.append ( extension[g] ); + } + + if ( fileSystem->existFile ( loadFile ) ) + { + texture = driver->getTexture( loadFile ); + if ( texture ) + break; + texture = 0; } } // take 0 Texture diff --git a/include/ISceneManager.h b/include/ISceneManager.h index abf364eb..ecfbcf21 100644 --- a/include/ISceneManager.h +++ b/include/ISceneManager.h @@ -7,6 +7,7 @@ #include "IReferenceCounted.h" #include "irrArray.h" +#include "irrString.h" #include "vector3d.h" #include "dimension2d.h" #include "SColor.h" @@ -83,6 +84,9 @@ namespace scene //! Transparent scene nodes, drawn after shadow nodes. They are sorted from back to front and drawn in that order. ESNRP_TRANSPARENT, + //! Transparent effect scene nodes, drawn after Transparent nodes. They are sorted from back to front and drawn in that order. + ESNRP_TRANSPARENT_EFFECT, + //! Never used, value specifing how much parameters there are. ESNRP_COUNT }; @@ -117,7 +121,7 @@ namespace scene namespace quake3 { - class SShader; + struct IShader; } // end namespace quake3 //! The Scene Manager manages scene nodes, mesh recources, cameras and all the other stuff. @@ -157,7 +161,7 @@ namespace scene * * 3D Studio (.3ds) * Loader for 3D-Studio files which lots of 3D packages - * are able to export. Only static meshes are currently + * are able to export. Only static meshes are currently * supported by this importer. * * @@ -171,7 +175,7 @@ namespace scene * architecture and calculating lighting. Irrlicht can * directly import .csm files thanks to the IrrCSM library * created by Saurav Mohapatra which is now integrated - * directly in Irrlicht. If you are using this loader, + * directly in Irrlicht. If you are using this loader, * please note that you'll have to set the path of the * textures before loading .csm files. You can do this * using @@ -181,51 +185,52 @@ namespace scene * * COLLADA (.dae, .xml) * COLLADA is an open Digital Asset Exchange Schema for - * the interactive 3D industry. There are exporters and - * importers for this format available for most of the - * big 3d packagesat http://collada.org. Irrlicht can - * import COLLADA files by using the - * ISceneManager::getMesh() method. COLLADA files need - * not contain only one single mesh but multiple meshes - * and a whole scene setup with lights, cameras and mesh - * instances, this loader can set up a scene as - * described by the COLLADA file instead of loading and - * returning one single mesh. By default, this loader - * behaves like the other loaders and does not create - * instances, but it can be switched into this mode by - * using - * SceneManager->getParameters()->setParameter(COLLADA_CREATE_SCENE_INSTANCES, true); - * Created scene nodes will be named as the names of the - * nodes in the COLLADA file. The returned mesh is just - * a dummy object in this mode. Meshes included in the - * scene will be added into the scene manager with the - * following naming scheme: - * path/to/file/file.dea#meshname. The loading of such - * meshes is logged. Currently, this loader is able to - * create meshes (made of only polygons), lights, and - * cameras. Materials and animations are currently not - * supported but this will change with future releases. + * the interactive 3D industry. There are exporters and + * importers for this format available for most of the + * big 3d packagesat http://collada.org. Irrlicht can + * import COLLADA files by using the + * ISceneManager::getMesh() method. COLLADA files need + * not contain only one single mesh but multiple meshes + * and a whole scene setup with lights, cameras and mesh + * instances, this loader can set up a scene as + * described by the COLLADA file instead of loading and + * returning one single mesh. By default, this loader + * behaves like the other loaders and does not create + * instances, but it can be switched into this mode by + * using + * SceneManager->getParameters()->setParameter(COLLADA_CREATE_SCENE_INSTANCES, true); + * Created scene nodes will be named as the names of the + * nodes in the COLLADA file. The returned mesh is just + * a dummy object in this mode. Meshes included in the + * scene will be added into the scene manager with the + * following naming scheme: + * path/to/file/file.dea#meshname. The loading of such + * meshes is logged. Currently, this loader is able to + * create meshes (made of only polygons), lights, and + * cameras. Materials and animations are currently not + * supported but this will change with future releases. * * * * Delgine DeleD (.dmf) * DeleD (delgine.com) is a 3D editor and level-editor - * combined into one and is specifically designed for 3D - * game-development. With this loader, it is possible to - * directly load all geometry is as well as textures and - * lightmaps from .dmf files. To set texture and - * material paths, see scene::DMF_USE_MATERIALS_DIRS and - * scene::DMF_TEXTURE_PATH. It is also possible to flip - * the alpha texture by setting - * scene::DMF_FLIP_ALPHA_TEXTURES to true and to set the - * material transparent reference value by setting - * scene::DMF_ALPHA_CHANNEL_REF to a float between 0 and - * 1. The loader is based on Salvatore Russo's .dmf - * loader, I just changed some parts of it. Thanks to - * Salvatore for his work and for allowing me to use his - * code in Irrlicht and put it under Irrlicht's license. - * For newer and more enchanced versions of the loader, - * take a look at delgine.com. + * combined into one and is specifically designed for 3D + * game-development. With this loader, it is possible to + * directly load all geometry is as well as textures and + * lightmaps from .dmf files. To set texture and + * material paths, see scene::DMF_USE_MATERIALS_DIRS and + * scene::DMF_TEXTURE_PATH. It is also possible to flip + * the alpha texture by setting + * scene::DMF_FLIP_ALPHA_TEXTURES to true and to set the + * material transparent reference value by setting + * scene::DMF_ALPHA_CHANNEL_REF to a float between 0 and + * 1. The loader is based on Salvatore Russo's .dmf + * loader, I just changed some parts of it. Thanks to + * Salvatore for his work and for allowing me to use his + * code in Irrlicht and put it under Irrlicht's license. + * For newer and more enchanced versions of the loader, + * take a look at delgine.com. + * * * * DirectX (.x) @@ -234,7 +239,7 @@ namespace scene * and there are several tools for them available, e.g. * the Maya exporter included in the DX SDK. * .x files can include skeletal animations and Irrlicht - * is able to play and display them. Currently, Irrlicht + * is able to play and display them. Currently, Irrlicht * only supports uncompressed .x files. * * @@ -249,82 +254,83 @@ namespace scene * .MS3D files contain models and sometimes skeletal * animations from the Milkshape 3D modeling and animation * software. This importer for Irrlicht can display and/or - * animate these files. + * animate these files. * * - * My3D (.my3d) - * .my3D is a flexible 3D file format. The My3DTools - * contains plug-ins to export .my3D files from several - * 3D packages. With this built-in importer, Irrlicht - * can read and display those files directly. This - * loader was written by Zhuck Dimitry who also created - * the whole My3DTools package. If you are using this - * loader, please note that you can set the path of the - * textures before loading .my3d files. You can do this - * using - * SceneManager->getParameters()->setParameter(scene::MY3D_TEXTURE_PATH, - * "path/to/your/textures"); - * - * - * OCT (.oct) - * The oct file format contains 3D geometry and - * lightmaps and can be loaded directly by Irrlicht. OCT - * files
can be created by FSRad, Paul Nette's - * radiosity processor or exported from Blender using - * OCTTools which can be found in the exporters/OCTTools - * directory of the SDK. Thanks to Murphy McCauley for - * creating all this. - * - * - * OGRE Meshes (.mesh) - * Ogre .mesh files contain 3D data for the OGRE 3D - * engine. Irrlicht can read and display them directly - * with this importer. To define materials for the mesh, - * copy a .material file named like the corresponding - * .mesh file where the .mesh file is. (For example - * ogrehead.material for ogrehead.mesh). Thanks to - * Christian Stehno who wrote and contributed this - * loader. - * - * - * Pulsar LMTools (.lmts) - * LMTools is a set of tools (Windows & Linux) for - * creating lightmaps. Irrlicht can directly read .lmts - * files thanks to
the importer created by Jonas - * Petersen. If you are using this loader, please note - * that you can set the path of the textures before - * loading .lmts files. You can do this using - * SceneManager->getParameters()->setParameter(scene::LMTS_TEXTURE_PATH, - * "path/to/your/textures"); - * Notes for
this version of the loader:
- * - It does not recognise/support user data in the - * *.lmts files.
- * - The TGAs generated by LMTools don't work in - * Irrlicht for some reason (the textures are upside - * down). Opening and resaving them in a graphics app - * will solve the problem. - * - * - * Quake 3 levels (.bsp) - * Quake 3 is a popular game by IDSoftware, and .pk3 - * files contain .bsp files and textures/lightmaps - * describing huge prelighted levels. Irrlicht can read - * .pk3 and .bsp files directly and thus render Quake 3 - * levels directly. Written by Nikolaus Gebhardt - * enhanced by Dean P. Macri with the curved surfaces - * feature. - * - * - * Quake 2 models (.md2) - * Quake 2 models are characters with morph target - * animation. Irrlicht can read, display and animate - * them directly with this importer. - * - * + * My3D (.my3d) + * .my3D is a flexible 3D file format. The My3DTools + * contains plug-ins to export .my3D files from several + * 3D packages. With this built-in importer, Irrlicht + * can read and display those files directly. This + * loader was written by Zhuck Dimitry who also created + * the whole My3DTools package. If you are using this + * loader, please note that you can set the path of the + * textures before loading .my3d files. You can do this + * using + * SceneManager->getParameters()->setParameter(scene::MY3D_TEXTURE_PATH, + * "path/to/your/textures"); + * + * + * + * OCT (.oct) + * The oct file format contains 3D geometry and + * lightmaps and can be loaded directly by Irrlicht. OCT + * files
can be created by FSRad, Paul Nette's + * radiosity processor or exported from Blender using + * OCTTools which can be found in the exporters/OCTTools + * directory of the SDK. Thanks to Murphy McCauley for + * creating all this. + * + * + * OGRE Meshes (.mesh) + * Ogre .mesh files contain 3D data for the OGRE 3D + * engine. Irrlicht can read and display them directly + * with this importer. To define materials for the mesh, + * copy a .material file named like the corresponding + * .mesh file where the .mesh file is. (For example + * ogrehead.material for ogrehead.mesh). Thanks to + * Christian Stehno who wrote and contributed this + * loader. + * + * + * Pulsar LMTools (.lmts) + * LMTools is a set of tools (Windows & Linux) for + * creating lightmaps. Irrlicht can directly read .lmts + * files thanks to
the importer created by Jonas + * Petersen. If you are using this loader, please note + * that you can set the path of the textures before + * loading .lmts files. You can do this using + * SceneManager->getParameters()->setParameter(scene::LMTS_TEXTURE_PATH, + * "path/to/your/textures"); + * Notes for
this version of the loader:
+ * - It does not recognise/support user data in the + * *.lmts files.
+ * - The TGAs generated by LMTools don't work in + * Irrlicht for some reason (the textures are upside + * down). Opening and resaving them in a graphics app + * will solve the problem. + * + * + * Quake 3 levels (.bsp) + * Quake 3 is a popular game by IDSoftware, and .pk3 + * files contain .bsp files and textures/lightmaps + * describing huge prelighted levels. Irrlicht can read + * .pk3 and .bsp files directly and thus render Quake 3 + * levels directly. Written by Nikolaus Gebhardt + * enhanced by Dean P. Macri with the curved surfaces + * feature. + * + * + * Quake 2 models (.md2) + * Quake 2 models are characters with morph target + * animation. Irrlicht can read, display and animate + * them directly with this importer. + * + * * - * To load and display a mesh quickly, just do this: - * \code - * SceneManager->addAnimatedMeshSceneNode( + * To load and display a mesh quickly, just do this: + * \code + * SceneManager->addAnimatedMeshSceneNode( * SceneManager->getMesh("yourmesh.3ds")); * \endcode * If you would like to implement and add your own file format loader to Irrlicht, @@ -333,13 +339,13 @@ namespace scene * \return Null if failed, otherwise pointer to the mesh. * This pointer should not be dropped. See IReferenceCounted::drop() for more information. **/ - virtual IAnimatedMesh* getMesh(const c8* filename) = 0; + virtual IAnimatedMesh* getMesh(const core::string& filename) = 0; //! Get pointer to an animateable mesh. Loads the file if not loaded already. /** Works just as getMesh(const char* filename). If you want to remove a loaded mesh from the cache again, use removeMesh(). \param file File handle of the mesh to load. - \return 0 if failed and pointer to the mesh if successful. + \return NULL if failed and pointer to the mesh if successful. This pointer should not be dropped. See IReferenceCounted::drop() for more information. */ virtual IAnimatedMesh* getMesh(io::IReadFile* file) = 0; @@ -364,8 +370,8 @@ namespace scene /** Example Usage: scene::IVolumeLightSceneNode * n = smgr->addVolumeLightSceneNode(NULL, -1, 32, 32, //Subdivide U/V - video::SColor(0, 180, 180, 180), //foot color - video::SColor(0, 0, 0, 0) //tail color + video::SColor(0, 180, 180, 180), //foot color + video::SColor(0, 0, 0, 0) //tail color ); if (n) { @@ -529,52 +535,49 @@ namespace scene const core::vector3df& lookat = core::vector3df(0,0,100), s32 id=-1) = 0; //! Adds a maya style user controlled camera scene node to the scene graph. - /** This is a standard camera with an animator that provides - mouse control similar to camera in the 3D Software Maya. - \param parent: Parent scene node of the camera. Can be null. - \param rotateSpeed: Rotation speed of the camera. - \param zoomSpeed: Zoom speed of the camera. - \param translationSpeed: Translation speed of the camera. - \param id: id of the camera. This id can be used to identify the camera. - \return Pointer to the interface of the camera if successful, otherwise 0. - This pointer should not be dropped. See - IReferenceCounted::drop() for more information. */ + /** This is a standard camera with an animator that provides mouse control similar + to camera in the 3D Software Maya by Alias Wavefront. + \param parent: Parent scene node of the camera. Can be null. + \param rotateSpeed: Rotation speed of the camera. + \param zoomSpeed: Zoom speed of the camera. + \param translationSpeed: TranslationSpeed of the camera. + \param id: id of the camera. This id can be used to identify the camera. + \return Returns a pointer to the interface of the camera if successful, otherwise 0. + This pointer should not be dropped. See IReferenceCounted::drop() for more information. */ virtual ICameraSceneNode* addCameraSceneNodeMaya(ISceneNode* parent = 0, f32 rotateSpeed = -1500.0f, f32 zoomSpeed = 200.0f, f32 translationSpeed = 1500.0f, s32 id=-1) = 0; //! Adds a camera scene node with an animator which provides mouse and keyboard control appropriate for first person shooters (FPS). - /** This FPS camera is intended to provide a demonstration of a - camera that behaves like a typical First Person Shooter. It is - useful for simple demos and prototyping but is not intended to - provide a full solution for a production quality game. It binds - the camera scene node rotation to the look-at target; @see - ICameraSceneNode::bindTargetAndRotation(). With this camera, - you look with the mouse, and move with cursor keys. If you want - to change the key layout, you can specify your own keymap. For - example to make the camera be controlled by the cursor keys AND - the keys W,A,S, and D, do something like this: - \code - SKeyMap keyMap[8]; - keyMap[0].Action = EKA_MOVE_FORWARD; - keyMap[0].KeyCode = KEY_UP; - keyMap[1].Action = EKA_MOVE_FORWARD; - keyMap[1].KeyCode = KEY_KEY_W; + /** This FPS camera is intended to provide a demonstration of a camera that behaves + like a typical First Person Shooter. It is useful for simple demos and prototyping but is not + intended to provide a full solution for a production quality game. It binds the camera scene node + rotation to the look-at target; @see ICameraSceneNode::bindTargetAndRotation(). + With this camera, you look with the mouse, and move with cursor keys. If you want to + change the key layout, you can specify your own keymap. For example to make the camera + be controlled by the cursor keys AND the keys W,A,S, and D, do something + like this: + \code + SKeyMap keyMap[8]; + keyMap[0].Action = EKA_MOVE_FORWARD; + keyMap[0].KeyCode = KEY_UP; + keyMap[1].Action = EKA_MOVE_FORWARD; + keyMap[1].KeyCode = KEY_KEY_W; - keyMap[2].Action = EKA_MOVE_BACKWARD; - keyMap[2].KeyCode = KEY_DOWN; - keyMap[3].Action = EKA_MOVE_BACKWARD; - keyMap[3].KeyCode = KEY_KEY_S; + keyMap[2].Action = EKA_MOVE_BACKWARD; + keyMap[2].KeyCode = KEY_DOWN; + keyMap[3].Action = EKA_MOVE_BACKWARD; + keyMap[3].KeyCode = KEY_KEY_S; - keyMap[4].Action = EKA_STRAFE_LEFT; - keyMap[4].KeyCode = KEY_LEFT; - keyMap[5].Action = EKA_STRAFE_LEFT; - keyMap[5].KeyCode = KEY_KEY_A; + keyMap[4].Action = EKA_STRAFE_LEFT; + keyMap[4].KeyCode = KEY_LEFT; + keyMap[5].Action = EKA_STRAFE_LEFT; + keyMap[5].KeyCode = KEY_KEY_A; - keyMap[6].Action = EKA_STRAFE_RIGHT; - keyMap[6].KeyCode = KEY_RIGHT; - keyMap[7].Action = EKA_STRAFE_RIGHT; - keyMap[7].KeyCode = KEY_KEY_D; + keyMap[6].Action = EKA_STRAFE_RIGHT; + keyMap[6].KeyCode = KEY_RIGHT; + keyMap[7].Action = EKA_STRAFE_RIGHT; + keyMap[7].KeyCode = KEY_KEY_D; camera = sceneManager->addCameraSceneNodeFPS(0, 100, 500, -1, keyMap, 8); \endcode @@ -602,7 +605,7 @@ namespace scene This pointer should not be dropped. See IReferenceCounted::drop() for more information. */ virtual ICameraSceneNode* addCameraSceneNodeFPS(ISceneNode* parent = 0, - f32 rotateSpeed = 100.0f, f32 moveSpeed = .5f, s32 id=-1, + f32 rotateSpeed = 100.0f, f32 moveSpeed = 0.5f, s32 id=-1, SKeyMap* keyMapArray=0, s32 keyMapSize=0, bool noVerticalMovement=false, f32 jumpSpeed = 0.f, bool invertMouse=false) = 0; @@ -682,7 +685,7 @@ namespace scene This pointer should not be dropped. See IReferenceCounted::drop() for more information. */ virtual ISceneNode* addSkyDomeSceneNode(video::ITexture* texture, u32 horiRes=16, u32 vertRes=8, - f64 texturePercentage=0.9, f64 spherePercentage=2.0, + f32 texturePercentage=0.9, f32 spherePercentage=2.0,f32 radius = 1000.f, ISceneNode* parent=0, s32 id=-1) = 0; //! Adds a particle system scene node to the scene graph. @@ -761,7 +764,7 @@ namespace scene not be dropped. See IReferenceCounted::drop() for more information. */ virtual ITerrainSceneNode* addTerrainSceneNode( - const c8* heightMapFileName, + const core::string& heightMapFileName, ISceneNode* parent=0, s32 id=-1, const core::vector3df& position = core::vector3df(0.0f,0.0f,0.0f), const core::vector3df& rotation = core::vector3df(0.0f,0.0f,0.0f), @@ -812,7 +815,7 @@ namespace scene /** A Quake3 Scene renders multiple meshes for a specific HighLanguage Shader (Quake3 Style ) \return Pointer to the quake3 scene node if successful, otherwise NULL. This pointer should not be dropped. See IReferenceCounted::drop() for more information. */ - virtual ISceneNode* addQuake3SceneNode(IMeshBuffer* meshBuffer, const quake3::SShader * shader, + virtual IMeshSceneNode* addQuake3SceneNode(IMeshBuffer* meshBuffer, const quake3::IShader * shader, ISceneNode* parent=0, s32 id=-1 ) = 0; @@ -840,11 +843,11 @@ namespace scene ISceneNode* parent = 0, const core::vector3df& position = core::vector3df(0,0,0), s32 id=-1) = 0; - //! Adds a text scene node, which uses billboards. The node, and the text on it, will scale with distance. + //! Adds a text scene node, which uses billboards. The node, and the text on it, will scale with distance. /** \param font The font to use on the billboard. Pass 0 to use the GUI environment's default font. \param text The text to display on the billboard. - \param parent The billboard's parent. Pass 0 to use the root scene node. + \param parent The billboard's parent. Pass 0 to use the root scene node. \param size The billboard's width and height. \param position The billboards position relative to its parent. \param id: An id of the node. This id can be used to identify the node. @@ -885,7 +888,7 @@ namespace scene specified some invalid parameters or that a mesh with that name already exists. If successful, a pointer to the mesh is returned. This pointer should not be dropped. See IReferenceCounted::drop() for more information. */ - virtual IAnimatedMesh* addHillPlaneMesh(const c8* name, + virtual IAnimatedMesh* addHillPlaneMesh(const core::string& name, const core::dimension2d& tileSize, const core::dimension2d& tileCount, video::SMaterial* material = 0, f32 hillHeight = 0.0f, const core::dimension2d& countHills = core::dimension2d(0.0f, 0.0f), @@ -914,7 +917,7 @@ namespace scene specified some invalid parameters, that a mesh with that name already exists, or that a texture could not be found. If successful, a pointer to the mesh is returned. This pointer should not be dropped. See IReferenceCounted::drop() for more information. */ - virtual IAnimatedMesh* addTerrainMesh(const c8* meshname, + virtual IAnimatedMesh* addTerrainMesh(const core::string& meshname, video::IImage* texture, video::IImage* heightmap, const core::dimension2d& stretchSize = core::dimension2d(10.0f,10.0f), f32 maxHeight=200.0f, @@ -932,7 +935,7 @@ namespace scene \param width1 Diameter of the cone's base, should be not smaller than the cylinder's diameter \return Pointer to the arrow mesh if successful, otherwise 0. This pointer should not be dropped. See IReferenceCounted::drop() for more information. */ - virtual IAnimatedMesh* addArrowMesh(const c8* name, + virtual IAnimatedMesh* addArrowMesh(const core::string& name, video::SColor vtxColor0=0xFFFFFFFF, video::SColor vtxColor1=0xFFFFFFFF, u32 tesselationCylinder=4, u32 tesselationCone=8, @@ -946,7 +949,7 @@ namespace scene \param polyCountY Number of quads used for the vertical tiling \return Pointer to the sphere mesh if successful, otherwise 0. This pointer should not be dropped. See IReferenceCounted::drop() for more information. */ - virtual IAnimatedMesh* addSphereMesh(const c8* name, + virtual IAnimatedMesh* addSphereMesh(const c16* name, f32 radius=5.f, u32 polyCountX = 16, u32 polyCountY = 16) = 0; @@ -1354,7 +1357,7 @@ namespace scene file, implement the ISceneUserDataSerializer interface and provide it as parameter here. Otherwise, simply specify 0 as this parameter. \return True if successful. */ - virtual bool saveScene(const c8* filename, ISceneUserDataSerializer* userDataSerializer=0) = 0; + virtual bool saveScene(const core::string& filename, ISceneUserDataSerializer* userDataSerializer=0) = 0; //! Saves the current scene into a file. /** Scene nodes with the option isDebugObject set to true are not being saved. @@ -1379,7 +1382,7 @@ namespace scene as parameter here. Otherwise, simply specify 0 as this parameter. \return True if successful. */ - virtual bool loadScene(const c8* filename, ISceneUserDataSerializer* userDataSerializer=0) = 0; + virtual bool loadScene(const core::string& filename, ISceneUserDataSerializer* userDataSerializer=0) = 0; //! Loads a scene. Note that the current scene is not cleared before. /** The scene is usually load from an .irr file, an xml based format. .irr files can diff --git a/include/IVideoDriver.h b/include/IVideoDriver.h index 4288be8e..4eb8dcb1 100644 --- a/include/IVideoDriver.h +++ b/include/IVideoDriver.h @@ -188,19 +188,7 @@ namespace video \return Pointer to the texture, or 0 if the texture could not be loaded. This pointer should not be dropped. See IReferenceCounted::drop() for more information. */ - virtual ITexture* getTexture(const c8* filename) =0; - - //! Get access to a named texture. - /** Loads the texture from disk if it is not - already loaded and generates mipmap levels if desired. - Texture loading can be influenced using the - setTextureCreationFlag() method. The texture can be in several - imageformats, such as BMP, JPG, TGA, PCX, PNG, and PSD. - \param filename Filename of the texture to be loaded. - \return Pointer to the texture, or 0 if the texture - could not be loaded. This pointer should not be dropped. See - IReferenceCounted::drop() for more information. */ - virtual ITexture* getTexture(const core::stringc& filename) =0; + virtual ITexture* getTexture(const core::string& filename) = 0; //! Get access to a named texture. /** Loads the texture from disk if it is not @@ -230,7 +218,7 @@ namespace video //! Renames a texture /** \param texture Pointer to the texture to rename. \param newName New name for the texture. This should be a unique name. */ - virtual void renameTexture(ITexture* texture, const c8* newName) =0; + virtual void renameTexture(ITexture* texture, const core::string& newName) = 0; //! Creates an empty texture of specified size. /** \param size: Size of the texture. @@ -243,7 +231,7 @@ namespace video should not be dropped. See IReferenceCounted::drop() for more information. */ virtual ITexture* addTexture(const core::dimension2d& size, - const c8* name, ECOLOR_FORMAT format = ECF_A8R8G8B8) =0; + const core::string& name, ECOLOR_FORMAT format = ECF_A8R8G8B8) = 0; //! Creates a texture from an IImage. /** \param name A name for the texture. Later calls of @@ -252,7 +240,7 @@ namespace video \return Pointer to the newly created texture. This pointer should not be dropped. See IReferenceCounted::drop() for more information. */ - virtual ITexture* addTexture(const c8* name, IImage* image) =0; + virtual ITexture* addTexture(const core::string& name, IImage* image) = 0; //! Adds a new render target texture to the texture cache. /** \param size Size of the texture, in pixels. Width and @@ -264,12 +252,7 @@ namespace video could not be created. This pointer should not be dropped. See IReferenceCounted::drop() for more information. */ virtual ITexture* addRenderTargetTexture(const core::dimension2d& size, - const c8* name=0) =0; - - //! Adds a new render target texture - /** \deprecated use addRenderTargetTexture instead. */ - virtual ITexture* createRenderTargetTexture(const core::dimension2d& size, - const c8* name=0) =0; + const core::string& name = "rt" ) =0; //! Removes a texture from the texture cache and deletes it. /** This method can free a lot of memory! @@ -349,7 +332,7 @@ namespace video way: \code // create render target - ITexture* target = driver->addRenderTargetTexture(core::dimension2d(128,128), "rtt1"); + ITexture* target = driver->addRenderTargetTexture(core::dimension2d(128,128), "rtt1"); // ... @@ -854,7 +837,7 @@ namespace video \return The created image. If you no longer need the image, you should call IImage::drop(). See IReferenceCounted::drop() for more information. */ - virtual IImage* createImageFromFile(const c8* filename) =0; + virtual IImage* createImageFromFile(const core::string& filename) = 0; //! Creates a software image from a file. /** No hardware texture will be created for this image. This @@ -874,7 +857,7 @@ namespace video \param param Control parameter for the backend (e.g. compression level). \return True on successful write. */ - virtual bool writeImageToFile(IImage* image, const c8* filename, u32 param =0) =0; + virtual bool writeImageToFile(IImage* image, const core::string& filename, u32 param = 0) = 0; //! Writes the provided image to a file. /** Requires that there is a suitable image writer registered @@ -1052,7 +1035,7 @@ namespace video if it is not currently loaded. \param filename Name of the texture. \return Pointer to loaded texture, or 0 if not found. */ - virtual video::ITexture* findTexture(const c8* filename) =0; + virtual video::ITexture* findTexture(const core::string& filename) = 0; //! Set or unset a clipping plane. /** There are at least 6 clipping planes available for the user diff --git a/media/irrlichtlogo3.png b/media/irrlichtlogo3.png new file mode 100644 index 0000000000000000000000000000000000000000..255b3ac01396f905be37df30dcc3d82b5ecc5ba2 GIT binary patch literal 8225 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 literal 0 HcmV?d00001 diff --git a/media/smoke3.jpg b/media/smoke3.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5dee86bfe81d06766d044d86c38e8b96e7b8ce2e GIT binary patch literal 23083 zcmeHPcUV(N*PokC2)!r@80kVFjWnc%0D_7j&9b(T1c;CXQv^FI_TI27x;AvxwfA1I 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