Commit ba385262 authored by bitplane's avatar bitplane

Had to rename some reserved words in (maybe objc++ but possibly macros to do...

Had to rename some reserved words in (maybe objc++ but possibly macros to do with xcode?) IQ3Shader::id to ID, string::verify to validate.
Put casts back into OSX driver

git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@2215 dfc29bdd-3216-0410-991c-e03cc46cb475
parent 510bb72e
......@@ -637,12 +637,12 @@ namespace quake3
struct IShader
{
IShader ()
: id ( 0 ), VarGroup ( 0 ) {}
: ID ( 0 ), VarGroup ( 0 ) {}
virtual ~IShader () {}
void operator = (const IShader &other )
{
id = other.id;
ID = other.ID;
VarGroup = other.VarGroup;
name = other.name;
}
......@@ -675,7 +675,7 @@ namespace quake3
}
// id
s32 id;
s32 ID;
SVarGroupList *VarGroup; // reference
// Shader: shader name ( also first variable in first Vargroup )
......
// Copyright (C) 2002-2009 Nikolaus Gebhardt
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h
#ifndef __IRR_CORE_UTIL_H_INCLUDED__
#define __IRR_CORE_UTIL_H_INCLUDED__
#include "irrString.h"
namespace irr
{
namespace core
{
/*! \file irrxml.h
\brief File containing useful basic utility functions
*/
// ----------- some basic quite often used string functions -----------------
//! search if a filename has a proper extension
inline s32 isFileExtension ( const core::string<c16>& filename,
const core::string<c16>& ext0,
const core::string<c16>& ext1,
const core::string<c16>& ext2
)
{
s32 extPos = filename.findLast ( '.' );
if ( extPos < 0 )
return 0;
extPos += 1;
if ( filename.equals_substring_ignore_case ( ext0, extPos ) ) return 1;
if ( filename.equals_substring_ignore_case ( ext1, extPos ) ) return 2;
if ( filename.equals_substring_ignore_case ( ext2, extPos ) ) return 3;
return 0;
}
//! search if a filename has a proper extension
inline bool hasFileExtension ( const core::string<c16>& filename,
const core::string<c16>& ext0,
const core::string<c16>& ext1 = "",
const core::string<c16>& ext2 = ""
)
{
return isFileExtension ( filename, ext0, ext1, ext2 ) > 0;
}
//! cut the filename extension from a source string and stores in the dest string
inline stringc& cutFilenameExtension ( stringc &dest, const stringc &source )
{
s32 endPos = source.findLast ( '.' );
dest = source.subString ( 0, endPos < 0 ? source.size () : endPos );
return dest;
}
//! cut the filename extension from a source string and stores in the dest string
inline stringw& cutFilenameExtension ( stringw &dest, const stringw &source )
{
s32 endPos = source.findLast ( '.' );
dest = source.subString ( 0, endPos < 0 ? source.size () : endPos );
return dest;
}
//! get the filename extension from a string
inline stringc& getFileNameExtension ( stringc &dest, const stringc &source )
{
s32 endPos = source.findLast ( '.' );
if ( endPos < 0 )
dest = "";
else
dest = source.subString ( endPos, source.size () );
return dest;
}
//! delete path from filename
inline core::stringw& deletePathFromFilename(core::stringw& filename)
{
// delete path from filename
const wchar_t *s = filename.c_str();
const wchar_t* p = s + filename.size();
// search for path separator or beginning
while ( *p != '/' && *p != '\\' && p != s )
p--;
if ( p != s )
{
++p;
filename = p;
}
return filename;
}
//! delete path from filename
inline core::stringc& deletePathFromFilename(core::stringc& filename)
{
// delete path from filename
const c8 *s = filename.c_str();
const c8* p = s + filename.size();
// search for path separator or beginning
while ( *p != '/' && *p != '\\' && p != s )
p--;
if ( p != s )
{
++p;
filename = p;
}
return filename;
}
//! trim paths
inline core::string<c16>& deletePathFromPath(core::string<c16>& filename, s32 pathCount)
{
// delete path from filename
s32 i = filename.size();
// search for path separator or beginning
while ( i )
{
if ( filename[i] == '/' || filename[i] == '\\' )
{
if ( --pathCount <= 0 )
break;
}
i -= 1;
}
if ( i )
{
filename [ i + 1 ] = 0;
filename.verify();
}
return filename;
}
//! gets the last char of a string or null
inline c16 lastChar( const core::string<c16>& s)
{
return s.size() ? s [ s.size() - 1 ] : 0;
}
//! looks if file is in the same directory of path. returns offset of directory.
//! 0 means in same directory. 1 means file is direct child of path
inline s32 isInSameDirectory ( const core::string<c16>& path, const core::string<c16>& file )
{
s32 subA = 0;
s32 subB = 0;
s32 pos;
if ( path.size() && !path.equalsn ( file, path.size() ) )
return -1;
pos = 0;
while ( (pos = path.findNext ( '/', pos )) >= 0 )
{
subA += 1;
pos += 1;
}
pos = 0;
while ( (pos = file.findNext ( '/', pos )) >= 0 )
{
subB += 1;
pos += 1;
}
return subB - subA;
}
//! some standard function ( to remove dependencies )
#undef isdigit
#undef isspace
#undef isupper
inline s32 isdigit(s32 c) { return c >= '0' && c <= '9'; }
inline s32 isspace(s32 c) { return c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v'; }
inline s32 isupper(s32 c) { return c >= 'A' && c <= 'Z'; }
} // end namespace core
} // end namespace irr
#endif
// Copyright (C) 2002-2009 Nikolaus Gebhardt
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h
#ifndef __IRR_CORE_UTIL_H_INCLUDED__
#define __IRR_CORE_UTIL_H_INCLUDED__
#include "irrString.h"
namespace irr
{
namespace core
{
/*! \file irrxml.h
\brief File containing useful basic utility functions
*/
// ----------- some basic quite often used string functions -----------------
//! search if a filename has a proper extension
inline s32 isFileExtension ( const core::string<c16>& filename,
const core::string<c16>& ext0,
const core::string<c16>& ext1,
const core::string<c16>& ext2
)
{
s32 extPos = filename.findLast ( '.' );
if ( extPos < 0 )
return 0;
extPos += 1;
if ( filename.equals_substring_ignore_case ( ext0, extPos ) ) return 1;
if ( filename.equals_substring_ignore_case ( ext1, extPos ) ) return 2;
if ( filename.equals_substring_ignore_case ( ext2, extPos ) ) return 3;
return 0;
}
//! search if a filename has a proper extension
inline bool hasFileExtension ( const core::string<c16>& filename,
const core::string<c16>& ext0,
const core::string<c16>& ext1 = "",
const core::string<c16>& ext2 = ""
)
{
return isFileExtension ( filename, ext0, ext1, ext2 ) > 0;
}
//! cut the filename extension from a source string and stores in the dest string
inline stringc& cutFilenameExtension ( stringc &dest, const stringc &source )
{
s32 endPos = source.findLast ( '.' );
dest = source.subString ( 0, endPos < 0 ? source.size () : endPos );
return dest;
}
//! cut the filename extension from a source string and stores in the dest string
inline stringw& cutFilenameExtension ( stringw &dest, const stringw &source )
{
s32 endPos = source.findLast ( '.' );
dest = source.subString ( 0, endPos < 0 ? source.size () : endPos );
return dest;
}
//! get the filename extension from a string
inline stringc& getFileNameExtension ( stringc &dest, const stringc &source )
{
s32 endPos = source.findLast ( '.' );
if ( endPos < 0 )
dest = "";
else
dest = source.subString ( endPos, source.size () );
return dest;
}
//! delete path from filename
inline core::stringw& deletePathFromFilename(core::stringw& filename)
{
// delete path from filename
const wchar_t *s = filename.c_str();
const wchar_t* p = s + filename.size();
// search for path separator or beginning
while ( *p != '/' && *p != '\\' && p != s )
p--;
if ( p != s )
{
++p;
filename = p;
}
return filename;
}
//! delete path from filename
inline core::stringc& deletePathFromFilename(core::stringc& filename)
{
// delete path from filename
const c8 *s = filename.c_str();
const c8* p = s + filename.size();
// search for path separator or beginning
while ( *p != '/' && *p != '\\' && p != s )
p--;
if ( p != s )
{
++p;
filename = p;
}
return filename;
}
//! trim paths
inline core::string<c16>& deletePathFromPath(core::string<c16>& filename, s32 pathCount)
{
// delete path from filename
s32 i = filename.size();
// search for path separator or beginning
while ( i )
{
if ( filename[i] == '/' || filename[i] == '\\' )
{
if ( --pathCount <= 0 )
break;
}
i -= 1;
}
if ( i )
{
filename [ i + 1 ] = 0;
filename.validate();
}
return filename;
}
//! gets the last char of a string or null
inline c16 lastChar( const core::string<c16>& s)
{
return s.size() ? s [ s.size() - 1 ] : 0;
}
//! looks if file is in the same directory of path. returns offset of directory.
//! 0 means in same directory. 1 means file is direct child of path
inline s32 isInSameDirectory ( const core::string<c16>& path, const core::string<c16>& file )
{
s32 subA = 0;
s32 subB = 0;
s32 pos;
if ( path.size() && !path.equalsn ( file, path.size() ) )
return -1;
pos = 0;
while ( (pos = path.findNext ( '/', pos )) >= 0 )
{
subA += 1;
pos += 1;
}
pos = 0;
while ( (pos = file.findNext ( '/', pos )) >= 0 )
{
subB += 1;
pos += 1;
}
return subB - subA;
}
//! some standard function ( to remove dependencies )
#undef isdigit
#undef isspace
#undef isupper
inline s32 isdigit(s32 c) { return c >= '0' && c <= '9'; }
inline s32 isspace(s32 c) { return c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v'; }
inline s32 isupper(s32 c) { return c >= 'A' && c <= 'Z'; }
} // end namespace core
} // end namespace irr
#endif
// Copyright (C) 2002-2009 Nikolaus Gebhardt
// This file is part of the "Irrlicht Engine" and the "irrXML" project.
// For conditions of distribution and use, see copyright notice in irrlicht.h and irrXML.h
#ifndef __IRR_STRING_H_INCLUDED__
#define __IRR_STRING_H_INCLUDED__
#include "irrTypes.h"
#include "irrAllocator.h"
#include "irrMath.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
namespace irr
{
namespace core
{
//! Very simple string class with some useful features.
/** string<c8> and string<wchar_t> both accept Unicode AND ASCII/Latin-1,
so you can assign Unicode to string<c8> and ASCII/Latin-1 to string<wchar_t>
(and the other way round) if you want to.
However, note that the conversation between both is not done using any encoding.
This means that c8 strings are treated as ASCII/Latin-1, not UTF-8, and
are simply expanded to the equivalent wchar_t, while Unicode/wchar_t
characters are truncated to 8-bit ASCII/Latin-1 characters, discarding all
other information in the wchar_t.
*/
enum eLocaleID
{
IRR_LOCALE_ANSI = 0,
IRR_LOCALE_GERMAN = 1
};
static eLocaleID locale_current = IRR_LOCALE_ANSI;
static inline void locale_set ( eLocaleID id )
{
locale_current = id;
}
//! Returns a character converted to lower case
static inline u32 locale_lower ( u32 x )
{
switch ( locale_current )
{
case IRR_LOCALE_GERMAN:
case IRR_LOCALE_ANSI:
break;
}
// ansi
return x >= 'A' && x <= 'Z' ? x + 0x20 : x;
}
//! Returns a character converted to upper case
static inline u32 locale_upper ( u32 x )
{
switch ( locale_current )
{
case IRR_LOCALE_GERMAN:
case IRR_LOCALE_ANSI:
break;
}
// ansi
return x >= 'a' && x <= 'z' ? x + ( 'A' - 'a' ) : x;
}
template <typename T, typename TAlloc = irrAllocator<T> >
class string
{
public:
//! Default constructor
string()
: array(0), allocated(1), used(1)
{
array = allocator.allocate(1); // new T[1];
array[0] = 0x0;
}
//! Constructor
string(const string<T>& other)
: array(0), allocated(0), used(0)
{
*this = other;
}
//! Constructor from other string types
template <class B>
string(const string<B>& other)
: array(0), allocated(0), used(0)
{
*this = other;
}
//! Constructs a string from a float
explicit string(const double number)
: array(0), allocated(0), used(0)
{
c8 tmpbuf[255];
snprintf(tmpbuf, 255, "%0.6f", number);
*this = tmpbuf;
}
//! Constructs a string from an int
explicit string(int number)
: array(0), allocated(0), used(0)
{
// store if negative and make positive
bool negative = false;
if (number < 0)
{
number *= -1;
negative = true;
}
// temporary buffer for 16 numbers
c8 tmpbuf[16];
tmpbuf[15] = 0;
u32 idx = 15;
// special case '0'
if (!number)
{
tmpbuf[14] = '0';
*this = &tmpbuf[14];
return;
}
// add numbers
while(number && idx)
{
--idx;
tmpbuf[idx] = (c8)('0' + (number % 10));
number /= 10;
}
// add sign
if (negative)
{
--idx;
tmpbuf[idx] = '-';
}
*this = &tmpbuf[idx];
}
//! Constructs a string from an unsigned int
explicit string(unsigned int number)
: array(0), allocated(0), used(0)
{
// temporary buffer for 16 numbers
c8 tmpbuf[16];
tmpbuf[15] = 0;
u32 idx = 15;
// special case '0'
if (!number)
{
tmpbuf[14] = '0';
*this = &tmpbuf[14];
return;
}
// add numbers
while(number && idx)
{
--idx;
tmpbuf[idx] = (c8)('0' + (number % 10));
number /= 10;
}
*this = &tmpbuf[idx];
}
//! Constructor for copying a string from a pointer with a given length
template <class B>
string(const B* const c, u32 length)
: array(0), allocated(0), used(0)
{
if (!c)
{
// correctly init the string to an empty one
*this="";
return;
}
allocated = used = length+1;
array = allocator.allocate(used); // new T[used];
for (u32 l = 0; l<length; ++l)
array[l] = (T)c[l];
array[length] = 0;
}
//! Constructor for unicode and ascii strings
template <class B>
string(const B* const c)
: array(0), allocated(0), used(0)
{
*this = c;
}
//! Destructor
virtual ~string()
{
allocator.deallocate(array); // delete [] array;
}
//! Assignment operator
string<T>& operator=(const string<T>& other)
{
if (this == &other)
return *this;
allocator.deallocate(array); // delete [] array;
allocated = used = other.size()+1;
array = allocator.allocate(used); //new T[used];
const T* p = other.c_str();
for (u32 i=0; i<used; ++i, ++p)
array[i] = *p;
return *this;
}
//! Assignment operator for other string types
template <class B>
string<T>& operator=(const string<B>& other)
{
*this = other.c_str();
return *this;
}
//! Assignment operator for strings, ascii and unicode
template <class B>
string<T>& operator=(const B* const c)
{
if (!c)
{
if (!array)
{
array = allocator.allocate(1); //new T[1];
allocated = 1;
}
used = 1;
array[0] = 0x0;
return *this;
}
if ((void*)c == (void*)array)
return *this;
u32 len = 0;
const B* p = c;
while(*p)
{
++len;
++p;
}
// we'll take the old string for a while, because the new
// string could be a part of the current string.
T* oldArray = array;
++len;
allocated = used = len;
array = allocator.allocate(used); //new T[used];
for (u32 l = 0; l<len; ++l)
array[l] = (T)c[l];
allocator.deallocate(oldArray); // delete [] oldArray;
return *this;
}
//! Append operator for other strings
string<T> operator+(const string<T>& other) const
{
string<T> str(*this);
str.append(other);
return str;
}
//! Append operator for strings, ascii and unicode
template <class B>
string<T> operator+(const B* const c) const
{
string<T> str(*this);
str.append(c);
return str;
}
//! Direct access operator
T& operator [](const u32 index)
{
_IRR_DEBUG_BREAK_IF(index>=used) // bad index
return array[index];
}
//! Direct access operator
const T& operator [](const u32 index) const
{
_IRR_DEBUG_BREAK_IF(index>=used) // bad index
return array[index];
}
//! Equality operator
bool operator ==(const T* const str) const
{
if (!str)
return false;
u32 i;
for(i=0; array[i] && str[i]; ++i)
if (array[i] != str[i])
return false;
return !array[i] && !str[i];
}
//! Equality operator
bool operator ==(const string<T>& other) const
{
for(u32 i=0; array[i] && other.array[i]; ++i)
if (array[i] != other.array[i])
return false;
return used == other.used;
}
//! Is smaller comparator
bool operator <(const string<T>& other) const
{
for(u32 i=0; array[i] && other.array[i]; ++i)
{
s32 diff = array[i] - other.array[i];
if ( diff )
return diff < 0;
/*
if (array[i] != other.array[i])
return (array[i] < other.array[i]);
*/
}
return used < other.used;
}
//! Inequality operator
bool operator !=(const T* const str) const
{
return !(*this == str);
}
//! Inequality operator
bool operator !=(const string<T>& other) const
{
return !(*this == other);
}
//! Returns length of string
/** \return Length of the string in characters. */
u32 size() const
{
return used-1;
}
//! Returns character string
/** \return pointer to C-style zero terminated string. */
const T* c_str() const
{
return array;
}
//! Makes the string lower case.
void make_lower()
{
for (u32 i=0; i<used; ++i)
array[i] = locale_lower ( array[i] );
}
//! Makes the string upper case.
void make_upper()
{
for (u32 i=0; i<used; ++i)
array[i] = locale_upper ( array[i] );
}
//! Compares the strings ignoring case.
/** \param other: Other string to compare.
\return True if the strings are equal ignoring case. */
bool equals_ignore_case(const string<T>& other) const
{
for(u32 i=0; array[i] && other[i]; ++i)
if (locale_lower( array[i]) != locale_lower(other[i]))
return false;
return used == other.used;
}
//! Compares the strings ignoring case.
/** \param other: Other string to compare.
\param sourcePos: where to start to compare in the string
\return True if the strings are equal ignoring case. */
bool equals_substring_ignore_case(const string<T>&other, const s32 sourcePos = 0 ) const
{
if ( (u32) sourcePos > used )
return false;
u32 i;
for( i=0; array[sourcePos + i] && other[i]; ++i)
if (locale_lower( array[sourcePos + i]) != locale_lower(other[i]))
return false;
return array[sourcePos + i] == 0 && other[i] == 0;
}
//! Compares the strings ignoring case.
/** \param other: Other string to compare.
\return True if this string is smaller ignoring case. */
bool lower_ignore_case(const string<T>& other) const
{
for(u32 i=0; array[i] && other.array[i]; ++i)
{
s32 diff = (s32) locale_lower ( array[i] ) - (s32) locale_lower ( other.array[i] );
if ( diff )
return diff < 0;
}
return used < other.used;
}
//! compares the first n characters of the strings
/** \param other Other string to compare.
\param n Number of characters to compare
\return True if the n first characters of this string are smaller. */
bool equalsn(const string<T>& other, u32 n) const
{
u32 i;
for(i=0; array[i] && other[i] && i < n; ++i)
if (array[i] != other[i])
return false;
// if one (or both) of the strings was smaller then they
// are only equal if they have the same length
return (i == n) || (used == other.used);
}
//! compares the first n characters of the strings
/** \param str Other string to compare.
\param n Number of characters to compare
\return True if the n first characters of this string are smaller. */
bool equalsn(const T* const str, u32 n) const
{
if (!str)
return false;
u32 i;
for(i=0; array[i] && str[i] && i < n; ++i)
if (array[i] != str[i])
return false;
// if one (or both) of the strings was smaller then they
// are only equal if they have the same length
return (i == n) || (array[i] == 0 && str[i] == 0);
}
//! Appends a character to this string
/** \param character: Character to append. */
void append(T character)
{
if (used + 1 > allocated)
reallocate(used + 1);
++used;
array[used-2] = character;
array[used-1] = 0;
}
//! Appends a char string to this string
/** \param other: Char string to append. */
void append(const T* const other)
{
if (!other)
return;
u32 len = 0;
const T* p = other;
while(*p)
{
++len;
++p;
}
if (used + len > allocated)
reallocate(used + len);
--used;
++len;
for (u32 l=0; l<len; ++l)
array[l+used] = *(other+l);
used += len;
}
//! Appends a string to this string
/** \param other: String to append. */
void append(const string<T>& other)
{
--used;
u32 len = other.size()+1;
if (used + len > allocated)
reallocate(used + len);
for (u32 l=0; l<len; ++l)
array[used+l] = other[l];
used += len;
}
//! Appends a string of the length l to this string.
/** \param other: other String to append to this string.
\param length: How much characters of the other string to add to this one. */
void append(const string<T>& other, u32 length)
{
if (other.size() < length)
{
append(other);
return;
}
if (used + length > allocated)
reallocate(used + length);
--used;
for (u32 l=0; l<length; ++l)
array[l+used] = other[l];
used += length;
// ensure proper termination
array[used]=0;
++used;
}
//! Reserves some memory.
/** \param count: Amount of characters to reserve. */
void reserve(u32 count)
{
if (count < allocated)
return;
reallocate(count);
}
//! finds first occurrence of character in string
/** \param c: Character to search for.
\return Position where the character has been found,
or -1 if not found. */
s32 findFirst(T c) const
{
for (u32 i=0; i<used; ++i)
if (array[i] == c)
return i;
return -1;
}
//! finds first occurrence of a character of a list in string
/** \param c: List of characters to find. For example if the method
should find the first occurrence of 'a' or 'b', this parameter should be "ab".
\param count: Amount of characters in the list. Usually,
this should be strlen(c)
\return Position where one of the characters has been found,
or -1 if not found. */
s32 findFirstChar(const T* const c, u32 count) const
{
if (!c)
return -1;
for (u32 i=0; i<used; ++i)
for (u32 j=0; j<count; ++j)
if (array[i] == c[j])
return i;
return -1;
}
//! Finds first position of a character not in a given list.
/** \param c: List of characters not to find. For example if the method
should find the first occurrence of a character not 'a' or 'b', this parameter should be "ab".
\param count: Amount of characters in the list. Usually,
this should be strlen(c)
\return Position where the character has been found,
or -1 if not found. */
template <class B>
s32 findFirstCharNotInList(const B* const c, u32 count) const
{
for (u32 i=0; i<used-1; ++i)
{
u32 j;
for (j=0; j<count; ++j)
if (array[i] == c[j])
break;
if (j==count)
return i;
}
return -1;
}
//! Finds last position of a character not in a given list.
/** \param c: List of characters not to find. For example if the method
should find the first occurrence of a character not 'a' or 'b', this parameter should be "ab".
\param count: Amount of characters in the list. Usually,
this should be strlen(c)
\return Position where the character has been found,
or -1 if not found. */
template <class B>
s32 findLastCharNotInList(const B* const c, u32 count) const
{
for (s32 i=(s32)(used-2); i>=0; --i)
{
u32 j;
for (j=0; j<count; ++j)
if (array[i] == c[j])
break;
if (j==count)
return i;
}
return -1;
}
//! finds next occurrence of character in string
/** \param c: Character to search for.
\param startPos: Position in string to start searching.
\return Position where the character has been found,
or -1 if not found. */
s32 findNext(T c, u32 startPos) const
{
for (u32 i=startPos; i<used; ++i)
if (array[i] == c)
return i;
return -1;
}
//! finds last occurrence of character in string
/** \param c: Character to search for.
\param start: start to search reverse ( default = -1, on end )
\return Position where the character has been found,
or -1 if not found. */
s32 findLast(T c, s32 start = -1) const
{
start = core::clamp ( start < 0 ? (s32)(used) - 1 : start, 0, (s32)(used) - 1 );
for (s32 i=start; i>=0; --i)
if (array[i] == c)
return i;
return -1;
}
//! finds last occurrence of a character of a list in string
/** \param c: List of strings to find. For example if the method
should find the last occurrence of 'a' or 'b', this parameter should be "ab".
\param count: Amount of characters in the list. Usually,
this should be strlen(c)
\return Position where one of the characters has been found,
or -1 if not found. */
s32 findLastChar(const T* const c, u32 count) const
{
if (!c)
return -1;
for (s32 i=used-1; i>=0; --i)
for (u32 j=0; j<count; ++j)
if (array[i] == c[j])
return i;
return -1;
}
//! finds another string in this string
/** \param str: Another string
\return Positions where the string has been found,
or -1 if not found. */
template <class B>
s32 find(const B* const str) const
{
if (str && *str)
{
u32 len = 0;
while (str[len])
++len;
if (len > used-1)
return -1;
for (u32 i=0; i<used-len; ++i)
{
u32 j=0;
while(str[j] && array[i+j] == str[j])
++j;
if (!str[j])
return i;
}
}
return -1;
}
//! Returns a substring
/** \param begin: Start of substring.
\param length: Length of substring. */
string<T> subString(u32 begin, s32 length) const
{
// if start after string
// or no proper substring length
if ((length <= 0) || (begin>=size()))
return string<T>("");
// clamp length to maximal value
if ((length+begin) > size())
length = size()-begin;
string<T> o;
o.reserve(length+1);
for (s32 i=0; i<length; ++i)
o.array[i] = array[i+begin];
o.array[length] = 0;
o.used = o.allocated;
return o;
}
//! Appends a character to this string
/** \param c Character to append. */
string<T>& operator += (T c)
{
append(c);
return *this;
}
//! Appends a char string to this string
/** \param c Char string to append. */
string<T>& operator += (const T* const c)
{
append(c);
return *this;
}
//! Appends a string to this string
/** \param other String to append. */
string<T>& operator += (const string<T>& other)
{
append(other);
return *this;
}
//! Appends a string representation of a number to this string
/** \param i Number to append. */
string<T>& operator += (const int i)
{
append(string<T>(i));
return *this;
}
//! Appends a string representation of a number to this string
/** \param i Number to append. */
string<T>& operator += (const unsigned int i)
{
append(string<T>(i));
return *this;
}
//! Appends a string representation of a number to this string
/** \param i Number to append. */
string<T>& operator += (const long i)
{
append(string<T>(i));
return *this;
}
//! Appends a string representation of a number to this string
/** \param i Number to append. */
string<T>& operator += (const unsigned long& i)
{
append(string<T>(i));
return *this;
}
//! Appends a string representation of a number to this string
/** \param i Number to append. */
string<T>& operator += (const double i)
{
append(string<T>(i));
return *this;
}
//! Appends a string representation of a number to this string
/** \param i Number to append. */
string<T>& operator += (const float i)
{
append(string<T>(i));
return *this;
}
//! Replaces all characters of a special type with another one
/** \param toReplace Character to replace.
\param replaceWith Character replacing the old one. */
void replace(T toReplace, T replaceWith)
{
for (u32 i=0; i<used; ++i)
if (array[i] == toReplace)
array[i] = replaceWith;
}
//! Trims the string.
/** Removes the specified characters (by default, Latin-1 whitespace)
from the begining and the end of the string. */
string<T>& trim(const string<T> & whitespace = " \t\n\r")
{
// find start and end of the substring without the specified characters
const s32 begin = findFirstCharNotInList(whitespace.c_str(), whitespace.used);
if (begin == -1)
return (*this="");
const s32 end = findLastCharNotInList(whitespace.c_str(), whitespace.used);
return (*this = subString(begin, (end +1) - begin));
}
//! Erases a character from the string.
/** May be slow, because all elements
following after the erased element have to be copied.
\param index: Index of element to be erased. */
void erase(u32 index)
{
_IRR_DEBUG_BREAK_IF(index>=used) // access violation
for (u32 i=index+1; i<used; ++i)
array[i-1] = array[i];
--used;
}
//! verify the existing string.
void verify()
{
// terminate on existing null
for (u32 i=0; i<allocated; ++i)
{
if (array[i] == 0)
{
used = i + 1;
return;
}
}
// terminate
if ( allocated > 0 )
{
used = allocated - 1;
array[used] = 0;
}
else
{
used = 0;
}
}
private:
//! Reallocate the array, make it bigger or smaller
void reallocate(u32 new_size)
{
T* old_array = array;
array = allocator.allocate(new_size); //new T[new_size];
allocated = new_size;
u32 amount = used < new_size ? used : new_size;
for (u32 i=0; i<amount; ++i)
array[i] = old_array[i];
if (allocated < used)
used = allocated;
allocator.deallocate(old_array); // delete [] old_array;
}
//--- member variables
T* array;
u32 allocated;
u32 used;
TAlloc allocator;
};
//! Typedef for character strings
typedef string<c8> stringc;
//! Typedef for wide character strings
typedef string<wchar_t> stringw;
} // end namespace core
} // end namespace irr
#endif
// Copyright (C) 2002-2009 Nikolaus Gebhardt
// This file is part of the "Irrlicht Engine" and the "irrXML" project.
// For conditions of distribution and use, see copyright notice in irrlicht.h and irrXML.h
#ifndef __IRR_STRING_H_INCLUDED__
#define __IRR_STRING_H_INCLUDED__
#include "irrTypes.h"
#include "irrAllocator.h"
#include "irrMath.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
namespace irr
{
namespace core
{
//! Very simple string class with some useful features.
/** string<c8> and string<wchar_t> both accept Unicode AND ASCII/Latin-1,
so you can assign Unicode to string<c8> and ASCII/Latin-1 to string<wchar_t>
(and the other way round) if you want to.
However, note that the conversation between both is not done using any encoding.
This means that c8 strings are treated as ASCII/Latin-1, not UTF-8, and
are simply expanded to the equivalent wchar_t, while Unicode/wchar_t
characters are truncated to 8-bit ASCII/Latin-1 characters, discarding all
other information in the wchar_t.
*/
enum eLocaleID
{
IRR_LOCALE_ANSI = 0,
IRR_LOCALE_GERMAN = 1
};
static eLocaleID locale_current = IRR_LOCALE_ANSI;
static inline void locale_set ( eLocaleID id )
{
locale_current = id;
}
//! Returns a character converted to lower case
static inline u32 locale_lower ( u32 x )
{
switch ( locale_current )
{
case IRR_LOCALE_GERMAN:
case IRR_LOCALE_ANSI:
break;
}
// ansi
return x >= 'A' && x <= 'Z' ? x + 0x20 : x;
}
//! Returns a character converted to upper case
static inline u32 locale_upper ( u32 x )
{
switch ( locale_current )
{
case IRR_LOCALE_GERMAN:
case IRR_LOCALE_ANSI:
break;
}
// ansi
return x >= 'a' && x <= 'z' ? x + ( 'A' - 'a' ) : x;
}
template <typename T, typename TAlloc = irrAllocator<T> >
class string
{
public:
//! Default constructor
string()
: array(0), allocated(1), used(1)
{
array = allocator.allocate(1); // new T[1];
array[0] = 0x0;
}
//! Constructor
string(const string<T>& other)
: array(0), allocated(0), used(0)
{
*this = other;
}
//! Constructor from other string types
template <class B>
string(const string<B>& other)
: array(0), allocated(0), used(0)
{
*this = other;
}
//! Constructs a string from a float
explicit string(const double number)
: array(0), allocated(0), used(0)
{
c8 tmpbuf[255];
snprintf(tmpbuf, 255, "%0.6f", number);
*this = tmpbuf;
}
//! Constructs a string from an int
explicit string(int number)
: array(0), allocated(0), used(0)
{
// store if negative and make positive
bool negative = false;
if (number < 0)
{
number *= -1;
negative = true;
}
// temporary buffer for 16 numbers
c8 tmpbuf[16];
tmpbuf[15] = 0;
u32 idx = 15;
// special case '0'
if (!number)
{
tmpbuf[14] = '0';
*this = &tmpbuf[14];
return;
}
// add numbers
while(number && idx)
{
--idx;
tmpbuf[idx] = (c8)('0' + (number % 10));
number /= 10;
}
// add sign
if (negative)
{
--idx;
tmpbuf[idx] = '-';
}
*this = &tmpbuf[idx];
}
//! Constructs a string from an unsigned int
explicit string(unsigned int number)
: array(0), allocated(0), used(0)
{
// temporary buffer for 16 numbers
c8 tmpbuf[16];
tmpbuf[15] = 0;
u32 idx = 15;
// special case '0'
if (!number)
{
tmpbuf[14] = '0';
*this = &tmpbuf[14];
return;
}
// add numbers
while(number && idx)
{
--idx;
tmpbuf[idx] = (c8)('0' + (number % 10));
number /= 10;
}
*this = &tmpbuf[idx];
}
//! Constructor for copying a string from a pointer with a given length
template <class B>
string(const B* const c, u32 length)
: array(0), allocated(0), used(0)
{
if (!c)
{
// correctly init the string to an empty one
*this="";
return;
}
allocated = used = length+1;
array = allocator.allocate(used); // new T[used];
for (u32 l = 0; l<length; ++l)
array[l] = (T)c[l];
array[length] = 0;
}
//! Constructor for unicode and ascii strings
template <class B>
string(const B* const c)
: array(0), allocated(0), used(0)
{
*this = c;
}
//! Destructor
virtual ~string()
{
allocator.deallocate(array); // delete [] array;
}
//! Assignment operator
string<T>& operator=(const string<T>& other)
{
if (this == &other)
return *this;
allocator.deallocate(array); // delete [] array;
allocated = used = other.size()+1;
array = allocator.allocate(used); //new T[used];
const T* p = other.c_str();
for (u32 i=0; i<used; ++i, ++p)
array[i] = *p;
return *this;
}
//! Assignment operator for other string types
template <class B>
string<T>& operator=(const string<B>& other)
{
*this = other.c_str();
return *this;
}
//! Assignment operator for strings, ascii and unicode
template <class B>
string<T>& operator=(const B* const c)
{
if (!c)
{
if (!array)
{
array = allocator.allocate(1); //new T[1];
allocated = 1;
}
used = 1;
array[0] = 0x0;
return *this;
}
if ((void*)c == (void*)array)
return *this;
u32 len = 0;
const B* p = c;
while(*p)
{
++len;
++p;
}
// we'll take the old string for a while, because the new
// string could be a part of the current string.
T* oldArray = array;
++len;
allocated = used = len;
array = allocator.allocate(used); //new T[used];
for (u32 l = 0; l<len; ++l)
array[l] = (T)c[l];
allocator.deallocate(oldArray); // delete [] oldArray;
return *this;
}
//! Append operator for other strings
string<T> operator+(const string<T>& other) const
{
string<T> str(*this);
str.append(other);
return str;
}
//! Append operator for strings, ascii and unicode
template <class B>
string<T> operator+(const B* const c) const
{
string<T> str(*this);
str.append(c);
return str;
}
//! Direct access operator
T& operator [](const u32 index)
{
_IRR_DEBUG_BREAK_IF(index>=used) // bad index
return array[index];
}
//! Direct access operator
const T& operator [](const u32 index) const
{
_IRR_DEBUG_BREAK_IF(index>=used) // bad index
return array[index];
}
//! Equality operator
bool operator ==(const T* const str) const
{
if (!str)
return false;
u32 i;
for(i=0; array[i] && str[i]; ++i)
if (array[i] != str[i])
return false;
return !array[i] && !str[i];
}
//! Equality operator
bool operator ==(const string<T>& other) const
{
for(u32 i=0; array[i] && other.array[i]; ++i)
if (array[i] != other.array[i])
return false;
return used == other.used;
}
//! Is smaller comparator
bool operator <(const string<T>& other) const
{
for(u32 i=0; array[i] && other.array[i]; ++i)
{
s32 diff = array[i] - other.array[i];
if ( diff )
return diff < 0;
/*
if (array[i] != other.array[i])
return (array[i] < other.array[i]);
*/
}
return used < other.used;
}
//! Inequality operator
bool operator !=(const T* const str) const
{
return !(*this == str);
}
//! Inequality operator
bool operator !=(const string<T>& other) const
{
return !(*this == other);
}
//! Returns length of string
/** \return Length of the string in characters. */
u32 size() const
{
return used-1;
}
//! Returns character string
/** \return pointer to C-style zero terminated string. */
const T* c_str() const
{
return array;
}
//! Makes the string lower case.
void make_lower()
{
for (u32 i=0; i<used; ++i)
array[i] = locale_lower ( array[i] );
}
//! Makes the string upper case.
void make_upper()
{
for (u32 i=0; i<used; ++i)
array[i] = locale_upper ( array[i] );
}
//! Compares the strings ignoring case.
/** \param other: Other string to compare.
\return True if the strings are equal ignoring case. */
bool equals_ignore_case(const string<T>& other) const
{
for(u32 i=0; array[i] && other[i]; ++i)
if (locale_lower( array[i]) != locale_lower(other[i]))
return false;
return used == other.used;
}
//! Compares the strings ignoring case.
/** \param other: Other string to compare.
\param sourcePos: where to start to compare in the string
\return True if the strings are equal ignoring case. */
bool equals_substring_ignore_case(const string<T>&other, const s32 sourcePos = 0 ) const
{
if ( (u32) sourcePos > used )
return false;
u32 i;
for( i=0; array[sourcePos + i] && other[i]; ++i)
if (locale_lower( array[sourcePos + i]) != locale_lower(other[i]))
return false;
return array[sourcePos + i] == 0 && other[i] == 0;
}
//! Compares the strings ignoring case.
/** \param other: Other string to compare.
\return True if this string is smaller ignoring case. */
bool lower_ignore_case(const string<T>& other) const
{
for(u32 i=0; array[i] && other.array[i]; ++i)
{
s32 diff = (s32) locale_lower ( array[i] ) - (s32) locale_lower ( other.array[i] );
if ( diff )
return diff < 0;
}
return used < other.used;
}
//! compares the first n characters of the strings
/** \param other Other string to compare.
\param n Number of characters to compare
\return True if the n first characters of this string are smaller. */
bool equalsn(const string<T>& other, u32 n) const
{
u32 i;
for(i=0; array[i] && other[i] && i < n; ++i)
if (array[i] != other[i])
return false;
// if one (or both) of the strings was smaller then they
// are only equal if they have the same length
return (i == n) || (used == other.used);
}
//! compares the first n characters of the strings
/** \param str Other string to compare.
\param n Number of characters to compare
\return True if the n first characters of this string are smaller. */
bool equalsn(const T* const str, u32 n) const
{
if (!str)
return false;
u32 i;
for(i=0; array[i] && str[i] && i < n; ++i)
if (array[i] != str[i])
return false;
// if one (or both) of the strings was smaller then they
// are only equal if they have the same length
return (i == n) || (array[i] == 0 && str[i] == 0);
}
//! Appends a character to this string
/** \param character: Character to append. */
void append(T character)
{
if (used + 1 > allocated)
reallocate(used + 1);
++used;
array[used-2] = character;
array[used-1] = 0;
}
//! Appends a char string to this string
/** \param other: Char string to append. */
void append(const T* const other)
{
if (!other)
return;
u32 len = 0;
const T* p = other;
while(*p)
{
++len;
++p;
}
if (used + len > allocated)
reallocate(used + len);
--used;
++len;
for (u32 l=0; l<len; ++l)
array[l+used] = *(other+l);
used += len;
}
//! Appends a string to this string
/** \param other: String to append. */
void append(const string<T>& other)
{
--used;
u32 len = other.size()+1;
if (used + len > allocated)
reallocate(used + len);
for (u32 l=0; l<len; ++l)
array[used+l] = other[l];
used += len;
}
//! Appends a string of the length l to this string.
/** \param other: other String to append to this string.
\param length: How much characters of the other string to add to this one. */
void append(const string<T>& other, u32 length)
{
if (other.size() < length)
{
append(other);
return;
}
if (used + length > allocated)
reallocate(used + length);
--used;
for (u32 l=0; l<length; ++l)
array[l+used] = other[l];
used += length;
// ensure proper termination
array[used]=0;
++used;
}
//! Reserves some memory.
/** \param count: Amount of characters to reserve. */
void reserve(u32 count)
{
if (count < allocated)
return;
reallocate(count);
}
//! finds first occurrence of character in string
/** \param c: Character to search for.
\return Position where the character has been found,
or -1 if not found. */
s32 findFirst(T c) const
{
for (u32 i=0; i<used; ++i)
if (array[i] == c)
return i;
return -1;
}
//! finds first occurrence of a character of a list in string
/** \param c: List of characters to find. For example if the method
should find the first occurrence of 'a' or 'b', this parameter should be "ab".
\param count: Amount of characters in the list. Usually,
this should be strlen(c)
\return Position where one of the characters has been found,
or -1 if not found. */
s32 findFirstChar(const T* const c, u32 count) const
{
if (!c)
return -1;
for (u32 i=0; i<used; ++i)
for (u32 j=0; j<count; ++j)
if (array[i] == c[j])
return i;
return -1;
}
//! Finds first position of a character not in a given list.
/** \param c: List of characters not to find. For example if the method
should find the first occurrence of a character not 'a' or 'b', this parameter should be "ab".
\param count: Amount of characters in the list. Usually,
this should be strlen(c)
\return Position where the character has been found,
or -1 if not found. */
template <class B>
s32 findFirstCharNotInList(const B* const c, u32 count) const
{
for (u32 i=0; i<used-1; ++i)
{
u32 j;
for (j=0; j<count; ++j)
if (array[i] == c[j])
break;
if (j==count)
return i;
}
return -1;
}
//! Finds last position of a character not in a given list.
/** \param c: List of characters not to find. For example if the method
should find the first occurrence of a character not 'a' or 'b', this parameter should be "ab".
\param count: Amount of characters in the list. Usually,
this should be strlen(c)
\return Position where the character has been found,
or -1 if not found. */
template <class B>
s32 findLastCharNotInList(const B* const c, u32 count) const
{
for (s32 i=(s32)(used-2); i>=0; --i)
{
u32 j;
for (j=0; j<count; ++j)
if (array[i] == c[j])
break;
if (j==count)
return i;
}
return -1;
}
//! finds next occurrence of character in string
/** \param c: Character to search for.
\param startPos: Position in string to start searching.
\return Position where the character has been found,
or -1 if not found. */
s32 findNext(T c, u32 startPos) const
{
for (u32 i=startPos; i<used; ++i)
if (array[i] == c)
return i;
return -1;
}
//! finds last occurrence of character in string
/** \param c: Character to search for.
\param start: start to search reverse ( default = -1, on end )
\return Position where the character has been found,
or -1 if not found. */
s32 findLast(T c, s32 start = -1) const
{
start = core::clamp ( start < 0 ? (s32)(used) - 1 : start, 0, (s32)(used) - 1 );
for (s32 i=start; i>=0; --i)
if (array[i] == c)
return i;
return -1;
}
//! finds last occurrence of a character of a list in string
/** \param c: List of strings to find. For example if the method
should find the last occurrence of 'a' or 'b', this parameter should be "ab".
\param count: Amount of characters in the list. Usually,
this should be strlen(c)
\return Position where one of the characters has been found,
or -1 if not found. */
s32 findLastChar(const T* const c, u32 count) const
{
if (!c)
return -1;
for (s32 i=used-1; i>=0; --i)
for (u32 j=0; j<count; ++j)
if (array[i] == c[j])
return i;
return -1;
}
//! finds another string in this string
/** \param str: Another string
\return Positions where the string has been found,
or -1 if not found. */
template <class B>
s32 find(const B* const str) const
{
if (str && *str)
{
u32 len = 0;
while (str[len])
++len;
if (len > used-1)
return -1;
for (u32 i=0; i<used-len; ++i)
{
u32 j=0;
while(str[j] && array[i+j] == str[j])
++j;
if (!str[j])
return i;
}
}
return -1;
}
//! Returns a substring
/** \param begin: Start of substring.
\param length: Length of substring. */
string<T> subString(u32 begin, s32 length) const
{
// if start after string
// or no proper substring length
if ((length <= 0) || (begin>=size()))
return string<T>("");
// clamp length to maximal value
if ((length+begin) > size())
length = size()-begin;
string<T> o;
o.reserve(length+1);
for (s32 i=0; i<length; ++i)
o.array[i] = array[i+begin];
o.array[length] = 0;
o.used = o.allocated;
return o;
}
//! Appends a character to this string
/** \param c Character to append. */
string<T>& operator += (T c)
{
append(c);
return *this;
}
//! Appends a char string to this string
/** \param c Char string to append. */
string<T>& operator += (const T* const c)
{
append(c);
return *this;
}
//! Appends a string to this string
/** \param other String to append. */
string<T>& operator += (const string<T>& other)
{
append(other);
return *this;
}
//! Appends a string representation of a number to this string
/** \param i Number to append. */
string<T>& operator += (const int i)
{
append(string<T>(i));
return *this;
}
//! Appends a string representation of a number to this string
/** \param i Number to append. */
string<T>& operator += (const unsigned int i)
{
append(string<T>(i));
return *this;
}
//! Appends a string representation of a number to this string
/** \param i Number to append. */
string<T>& operator += (const long i)
{
append(string<T>(i));
return *this;
}
//! Appends a string representation of a number to this string
/** \param i Number to append. */
string<T>& operator += (const unsigned long& i)
{
append(string<T>(i));
return *this;
}
//! Appends a string representation of a number to this string
/** \param i Number to append. */
string<T>& operator += (const double i)
{
append(string<T>(i));
return *this;
}
//! Appends a string representation of a number to this string
/** \param i Number to append. */
string<T>& operator += (const float i)
{
append(string<T>(i));
return *this;
}
//! Replaces all characters of a special type with another one
/** \param toReplace Character to replace.
\param replaceWith Character replacing the old one. */
void replace(T toReplace, T replaceWith)
{
for (u32 i=0; i<used; ++i)
if (array[i] == toReplace)
array[i] = replaceWith;
}
//! Trims the string.
/** Removes the specified characters (by default, Latin-1 whitespace)
from the begining and the end of the string. */
string<T>& trim(const string<T> & whitespace = " \t\n\r")
{
// find start and end of the substring without the specified characters
const s32 begin = findFirstCharNotInList(whitespace.c_str(), whitespace.used);
if (begin == -1)
return (*this="");
const s32 end = findLastCharNotInList(whitespace.c_str(), whitespace.used);
return (*this = subString(begin, (end +1) - begin));
}
//! Erases a character from the string.
/** May be slow, because all elements
following after the erased element have to be copied.
\param index: Index of element to be erased. */
void erase(u32 index)
{
_IRR_DEBUG_BREAK_IF(index>=used) // access violation
for (u32 i=index+1; i<used; ++i)
array[i-1] = array[i];
--used;
}
//! verify the existing string.
void validate()
{
// terminate on existing null
for (u32 i=0; i<allocated; ++i)
{
if (array[i] == 0)
{
used = i + 1;
return;
}
}
// terminate
if ( allocated > 0 )
{
used = allocated - 1;
array[used] = 0;
}
else
{
used = 0;
}
}
private:
//! Reallocate the array, make it bigger or smaller
void reallocate(u32 new_size)
{
T* old_array = array;
array = allocator.allocate(new_size); //new T[new_size];
allocated = new_size;
u32 amount = used < new_size ? used : new_size;
for (u32 i=0; i<amount; ++i)
array[i] = old_array[i];
if (allocated < used)
used = allocated;
allocator.deallocate(old_array); // delete [] old_array;
}
//--- member variables
T* array;
u32 allocated;
u32 used;
TAlloc allocator;
};
//! Typedef for character strings
typedef string<c8> stringc;
//! Typedef for wide character strings
typedef string<wchar_t> stringw;
} // end namespace core
} // end namespace irr
#endif
// Copyright (C) 2002-2009 Nikolaus Gebhardt
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h
#include "IrrCompileConfig.h"
#include "CFileSystem.h"
#include "IReadFile.h"
#include "IWriteFile.h"
#include "CZipReader.h"
#include "CPakReader.h"
#include "CFileList.h"
#include "CXMLReader.h"
#include "CXMLWriter.h"
#include "stdio.h"
#include "os.h"
#include "CAttributes.h"
#include "CMemoryFile.h"
#if defined (_IRR_WINDOWS_API_)
#if !defined ( _WIN32_WCE )
#include <direct.h> // for _chdir
#endif
#else
#include <unistd.h>
#include <limits.h>
#include <stdlib.h>
#endif
namespace irr
{
namespace io
{
//! constructor
CFileSystem::CFileSystem()
{
#ifdef _DEBUG
setDebugName("CFileSystem");
#endif
setFileListSystem ( FILESYSTEM_NATIVE );
addArchiveLoader ( new CArchiveLoaderZIP ( this ) );
addArchiveLoader ( new CArchiveLoaderMount ( this ) );
addArchiveLoader ( new CArchiveLoaderPAK ( this ) );
}
//! destructor
CFileSystem::~CFileSystem()
{
u32 i;
for ( i=0; i < FileArchive.size(); ++i)
{
FileArchive[i]->drop ();
}
for ( i=0; i < ArchiveLoader.size(); ++i)
{
ArchiveLoader[i]->drop ();
}
}
//! opens a file for read access
IReadFile* CFileSystem::createAndOpenFile( const core::string<c16>& filename)
{
IReadFile* file = 0;
u32 i;
for ( i=0; i< FileArchive.size(); ++i)
{
file = FileArchive[i]->openFile(filename);
if (file)
return file;
}
// Create the file using an absolute path so that it matches
// the scheme used by CNullDriver::getTexture().
return createReadFile(getAbsolutePath(filename));
}
//! Creates an IReadFile interface for treating memory like a file.
IReadFile* CFileSystem::createMemoryReadFile(void* memory, s32 len,
const core::string<c16>& fileName, bool deleteMemoryWhenDropped)
{
if (!memory)
return 0;
else
return new CMemoryFile(memory, len, fileName, deleteMemoryWhenDropped);
}
//! Creates an IReadFile interface for treating memory like a file.
IWriteFile* CFileSystem::createMemoryWriteFile(void* memory, s32 len,
const core::string<c16>& fileName, bool deleteMemoryWhenDropped)
{
if (!memory)
return 0;
else
return new CMemoryFile(memory, len, fileName, deleteMemoryWhenDropped);
}
//! Opens a file for write access.
IWriteFile* CFileSystem::createAndWriteFile(const core::string<c16>& filename, bool append)
{
return createWriteFile(filename, append);
}
//! Adds an external archive loader to the engine.
void CFileSystem::addArchiveLoader(IArchiveLoader* loader)
{
if (!loader)
return;
//loader->grab();
ArchiveLoader.push_back ( loader );
}
//! move the hirarchy of the filesystem. moves sourceIndex relative up or down
bool CFileSystem::moveFileArchive( u32 sourceIndex, s32 relative )
{
bool r = false;
const s32 dest = (s32) sourceIndex + relative;
const s32 dir = relative < 0 ? -1 : 1;
const s32 sourceEnd = ((s32) FileArchive.size() ) - 1;
IFileArchive *t;
for ( s32 s = (s32) sourceIndex;s != dest; s += dir )
{
if ( s < 0 || s > sourceEnd || s + dir < 0 || s + dir > sourceEnd )
continue;
t = FileArchive [ s + dir ];
FileArchive [ s + dir ] = FileArchive [ s ];
FileArchive [ s ] = t;
r = true;
}
return r;
}
//! Adds an archive to the file system.
bool CFileSystem::registerFileArchive( const core::string<c16>& filename, bool ignoreCase, bool ignorePaths )
{
IFileArchive* archive = 0;
bool ret = false;
u32 i;
// try to load archive based on file name
for ( i = 0; i < ArchiveLoader.size(); ++i)
{
if ( ArchiveLoader[i]->isALoadableFileFormat( filename ) )
{
archive = ArchiveLoader[i]->createArchive( filename, ignoreCase, ignorePaths );
if (archive)
break;
}
}
// try to load archive based on content
if ( 0 == archive )
{
io::IReadFile* file = createAndOpenFile(filename);
if ( file )
{
for ( i = 0; i < ArchiveLoader.size(); ++i)
{
file->seek ( 0 );
if ( ArchiveLoader[i]->isALoadableFileFormat( file ) )
{
file->seek ( 0 );
archive = ArchiveLoader[i]->createArchive( file, ignoreCase, ignorePaths );
if (archive)
break;
}
}
file->drop ();
}
}
if ( archive )
{
FileArchive.push_back ( archive );
ret = true;
}
else
{
char buf[256];
core::stringw showName ( filename );
snprintf ( buf, sizeof (buf), "Could not create archive for: %ls\n", showName.c_str() );
os::Printer::log( buf ,ELL_ERROR);
}
return ret;
}
//! removes an archive to the file system.
bool CFileSystem::unregisterFileArchive( u32 index )
{
bool ret = false;
if ( index < FileArchive.size () )
{
FileArchive[index]->drop();
FileArchive.erase ( index );
ret = true;
}
return ret;
}
//! gets an archive
u32 CFileSystem::getFileArchiveCount()
{
return FileArchive.size();
}
IFileArchive* CFileSystem::getFileArchive( u32 index )
{
return index < getFileArchiveCount () ? FileArchive [ index ] : 0;
}
//! Returns the string of the current working directory
const core::string<c16>& CFileSystem::getWorkingDirectory()
{
eFileSystemType type = FileSystemType;
if ( type != FILESYSTEM_NATIVE )
{
type = FILESYSTEM_VIRTUAL;
}
else
{
const s32 FILE_SYSTEM_MAX_PATH = 1024;
WorkingDirectory [ type ].reserve ( FILE_SYSTEM_MAX_PATH );
c16* r = (c16*) WorkingDirectory [ type ].c_str();
#if defined( _IRR_USE_WINDOWS_CE_DEVICE_)
#elif defined( _IRR_WINDOWS_API_)
#if defined ( _IRR_WCHAR_FILESYSTEM )
_wgetcwd ( r, FILE_SYSTEM_MAX_PATH );
#else
_getcwd(r, FILE_SYSTEM_MAX_PATH);
#endif
#endif
#if (defined(_IRR_POSIX_API_) || defined(_IRR_OSX_PLATFORM_))
#if defined ( _IRR_WCHAR_FILESYSTEM )
wgetcwd ( r, FILE_SYSTEM_MAX_PATH );
#else
getcwd(r, (size_t)FILE_SYSTEM_MAX_PATH);
#endif
#endif
WorkingDirectory [ type ].verify();
}
return WorkingDirectory [ type ];
}
//! Changes the current Working Directory to the given string.
bool CFileSystem::changeWorkingDirectoryTo(const core::string<c16>& newDirectory)
{
bool success=false;
if ( FileSystemType != FILESYSTEM_NATIVE )
{
WorkingDirectory [ FILESYSTEM_VIRTUAL ].append ( newDirectory );
flattenFilename ( WorkingDirectory [ FILESYSTEM_VIRTUAL ], "" );
success = 1;
}
else
{
WorkingDirectory [ FILESYSTEM_NATIVE ] = newDirectory;
#if defined( _IRR_USE_WINDOWS_CE_DEVICE_)
success = true;
#elif defined( _MSC_VER)
#if defined ( _IRR_WCHAR_FILESYSTEM )
success=(_wchdir(newDirectory.c_str()) == 0);
#else
success=(_chdir(newDirectory.c_str()) == 0);
#endif
#else
success=(chdir(newDirectory.c_str()) == 0);
#endif
}
return success;
}
core::string<c16> CFileSystem::getAbsolutePath(const core::string<c16>& filename) const
{
c16 *p=0;
#if defined( _IRR_USE_WINDOWS_CE_DEVICE_)
return filename;
#elif defined( _IRR_WINDOWS_API_)
#if defined ( _IRR_WCHAR_FILESYSTEM )
c16 fpath[_MAX_PATH];
p = _wfullpath( fpath, filename.c_str(), _MAX_PATH);
#else
c8 fpath[_MAX_PATH];
p = _fullpath( fpath, filename.c_str(), _MAX_PATH);
#endif
#elif (defined(_IRR_POSIX_API_) || defined(_IRR_OSX_PLATFORM_))
c8 fpath[4096];
fpath[0]=0;
p = realpath(filename.c_str(), fpath);
if (!p)
{
// content in fpath is undefined at this point
if ('0'==fpath[0]) // seems like fpath wasn't altered
{
// at least remove a ./ prefix
if ('.'==filename[0] && '/'==filename[1])
return filename.subString(2, filename.size()-2);
else
return filename;
}
else
return core::string<c16>(fpath);
}
#endif
return core::string<c16>(p);
}
//! returns the directory part of a filename, i.e. all until the first
//! slash or backslash, excluding it. If no directory path is prefixed, a '.'
//! is returned.
core::string<c16> CFileSystem::getFileDir(const core::string<c16>& filename) const
{
// find last forward or backslash
s32 lastSlash = filename.findLast('/');
const s32 lastBackSlash = filename.findLast('\\');
lastSlash = lastSlash > lastBackSlash ? lastSlash : lastBackSlash;
if ((u32)lastSlash < filename.size())
return filename.subString(0, lastSlash);
else
return ".";
}
//! returns the base part of a filename, i.e. all except for the directory
//! part. If no directory path is prefixed, the full name is returned.
core::string<c16> CFileSystem::getFileBasename(const core::string<c16>& filename, bool keepExtension) const
{
// find last forward or backslash
s32 lastSlash = filename.findLast('/');
const s32 lastBackSlash = filename.findLast('\\');
lastSlash = core::max_(lastSlash, lastBackSlash);
s32 end = 0;
if (!keepExtension)
{
end = filename.findLast('.');
if (end == -1)
end=0;
else
end = filename.size()-end;
}
if ((u32)lastSlash < filename.size())
return filename.subString(lastSlash+1, filename.size()-lastSlash-1-end);
else if (end != 0)
return filename.subString(0, filename.size()-end);
else
return filename;
}
//! flaten a path and file name for example: "/you/me/../." becomes "/you"
core::string<c16>& CFileSystem::flattenFilename( core::string<c16>& directory, const core::string<c16>& root ) const
{
directory.replace ( '\\', '/' );
if ( lastChar ( directory ) != '/' )
directory.append ( '/' );
core::string<c16> dir;
core::string<c16> subdir;
s32 lastpos = 0;
s32 pos = 0;
while ( ( pos = directory.findNext ( '/', lastpos ) ) >= 0 )
{
subdir = directory.subString ( lastpos, pos - lastpos + 1 );
if ( subdir == "../" )
{
deletePathFromPath ( dir, 2 );
}
else
if ( subdir == "/" )
{
dir = root;
}
else
if ( subdir != "./" )
{
dir.append ( subdir );
}
lastpos = pos + 1;
}
directory = dir;
return directory;
}
//! Creates a list of files and directories in the current working directory
eFileSystemType CFileSystem::setFileListSystem( eFileSystemType listType)
{
eFileSystemType current = FileSystemType;
FileSystemType = listType;
return current;
}
//! Creates a list of files and directories in the current working directory
IFileList* CFileSystem::createFileList()
{
FileEntry e2;
FileEntry e3;
if ( FileSystemType == FILESYSTEM_NATIVE )
return new CFileList();
CFileList* r = new CFileList( "virtual" );
r->Path = WorkingDirectory [ FILESYSTEM_VIRTUAL ];
for ( u32 i = 0; i != FileArchive.size(); ++i)
{
CFileList* flist[2] = { 0, 0 };
//! merge relative folder file archives
if ( FileArchive[i]->getArchiveType() == "mount" )
{
eFileSystemType currentType = setFileListSystem ( FILESYSTEM_NATIVE );
core::string<c16> save ( getWorkingDirectory () );
core::string<c16> current;
current = FileArchive[i]->getArchiveName() + WorkingDirectory [ FILESYSTEM_VIRTUAL ];
flattenFilename ( current );
if ( changeWorkingDirectoryTo ( current ) )
{
flist[0] = new CFileList( "mountpoint" );
flist[0]->constructNative ();
changeWorkingDirectoryTo ( save );
}
setFileListSystem ( currentType );
}
else
if ( FileArchive[i]->getArchiveType() == "zip" )
{
flist[0] = new CFileList( "zip" );
flist[1] = new CFileList( "zip directory" );
// add relative navigation
e2.isDirectory = 1;
e2.Name = ".";
e2.FullName = r->Path + e2.Name;
e2.Size = 0;
flist[1]->Files.push_back ( e2 );
e2.Name = "..";
e2.FullName = r->Path + e2.Name;
flist[1]->Files.push_back ( e2 );
for ( u32 g = 0; g < FileArchive[i]->getFileCount(); ++g)
{
const SZipFileEntry *e = (SZipFileEntry*) FileArchive[i]->getFileInfo ( g );
s32 test = isInSameDirectory ( r->Path, e->zipFileName );
if ( test < 0 || test > 1 )
continue;
e2.Size = e->header.DataDescriptor.UncompressedSize;
e2.isDirectory = e2.Size == 0;
// check missing directories
if ( !e2.isDirectory && test == 1 )
{
e3.Size = 0;
e3.isDirectory = 1;
e3.FullName = e->path;
e3.Name = e->path.subString ( r->Path.size(), e->path.size() - r->Path.size() - 1 );
if ( flist[1]->Files.binary_search ( e3 ) < 0 )
flist[1]->Files.push_back ( e3 );
}
else
{
e2.FullName = e->zipFileName;
e2.Name = e->simpleFileName;
if ( !e2.isDirectory )
core::deletePathFromFilename ( e2.Name );
flist[0]->Files.push_back ( e2 );
}
}
}
// add file to virtual directory
for ( u32 g = 0; g < 2; ++g )
{
if ( !flist[g] )
continue;
for ( u32 j = 0; j != flist[g]->Files.size(); ++j )
r->Files.push_back ( flist[g]->Files[j] );
flist[g]->drop();
}
}
r->Files.sort();
return r;
}
//! determines if a file exists and would be able to be opened.
bool CFileSystem::existFile(const core::string<c16>& filename) const
{
u32 i;
for (i=0; i < FileArchive.size(); ++i)
if ( FileArchive[i]->findFile(filename)!=-1)
return true;
#if defined ( _IRR_WCHAR_FILESYSTEM )
FILE* f = _wfopen(filename.c_str(), L"rb");
#else
FILE* f = fopen(filename.c_str(), "rb");
#endif
if (f)
{
fclose(f);
return true;
}
_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
return false;
}
//! Creates a XML Reader from a file.
IXMLReader* CFileSystem::createXMLReader(const core::string<c16>& filename)
{
IReadFile* file = createAndOpenFile(filename);
if (!file)
return 0;
IXMLReader* reader = createXMLReader(file);
file->drop();
return reader;
}
//! Creates a XML Reader from a file.
IXMLReader* CFileSystem::createXMLReader(IReadFile* file)
{
if (!file)
return 0;
return createIXMLReader(file);
}
//! Creates a XML Reader from a file.
IXMLReaderUTF8* CFileSystem::createXMLReaderUTF8(const core::string<c16>& filename)
{
IReadFile* file = createAndOpenFile(filename);
if (!file)
return 0;
IXMLReaderUTF8* reader = createIXMLReaderUTF8(file);
file->drop();
return reader;
}
//! Creates a XML Reader from a file.
IXMLReaderUTF8* CFileSystem::createXMLReaderUTF8(IReadFile* file)
{
if (!file)
return 0;
return createIXMLReaderUTF8(file);
}
//! Creates a XML Writer from a file.
IXMLWriter* CFileSystem::createXMLWriter(const core::string<c16>& filename)
{
IWriteFile* file = createAndWriteFile(filename);
IXMLWriter* writer = createXMLWriter(file);
file->drop();
return writer;
}
//! Creates a XML Writer from a file.
IXMLWriter* CFileSystem::createXMLWriter(IWriteFile* file)
{
return new CXMLWriter(file);
}
//! creates a filesystem which is able to open files from the ordinary file system,
//! and out of zipfiles, which are able to be added to the filesystem.
IFileSystem* createFileSystem()
{
return new CFileSystem();
}
//! Creates a new empty collection of attributes, usable for serialization and more.
IAttributes* CFileSystem::createEmptyAttributes(video::IVideoDriver* driver)
{
return new CAttributes(driver);
}
} // end namespace irr
} // end namespace io
// Copyright (C) 2002-2009 Nikolaus Gebhardt
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h
#include "IrrCompileConfig.h"
#include "CFileSystem.h"
#include "IReadFile.h"
#include "IWriteFile.h"
#include "CZipReader.h"
#include "CPakReader.h"
#include "CFileList.h"
#include "CXMLReader.h"
#include "CXMLWriter.h"
#include "stdio.h"
#include "os.h"
#include "CAttributes.h"
#include "CMemoryFile.h"
#if defined (_IRR_WINDOWS_API_)
#if !defined ( _WIN32_WCE )
#include <direct.h> // for _chdir
#endif
#else
#include <unistd.h>
#include <limits.h>
#include <stdlib.h>
#endif
namespace irr
{
namespace io
{
//! constructor
CFileSystem::CFileSystem()
{
#ifdef _DEBUG
setDebugName("CFileSystem");
#endif
setFileListSystem ( FILESYSTEM_NATIVE );
addArchiveLoader ( new CArchiveLoaderZIP ( this ) );
addArchiveLoader ( new CArchiveLoaderMount ( this ) );
addArchiveLoader ( new CArchiveLoaderPAK ( this ) );
}
//! destructor
CFileSystem::~CFileSystem()
{
u32 i;
for ( i=0; i < FileArchive.size(); ++i)
{
FileArchive[i]->drop ();
}
for ( i=0; i < ArchiveLoader.size(); ++i)
{
ArchiveLoader[i]->drop ();
}
}
//! opens a file for read access
IReadFile* CFileSystem::createAndOpenFile( const core::string<c16>& filename)
{
IReadFile* file = 0;
u32 i;
for ( i=0; i< FileArchive.size(); ++i)
{
file = FileArchive[i]->openFile(filename);
if (file)
return file;
}
// Create the file using an absolute path so that it matches
// the scheme used by CNullDriver::getTexture().
return createReadFile(getAbsolutePath(filename));
}
//! Creates an IReadFile interface for treating memory like a file.
IReadFile* CFileSystem::createMemoryReadFile(void* memory, s32 len,
const core::string<c16>& fileName, bool deleteMemoryWhenDropped)
{
if (!memory)
return 0;
else
return new CMemoryFile(memory, len, fileName, deleteMemoryWhenDropped);
}
//! Creates an IReadFile interface for treating memory like a file.
IWriteFile* CFileSystem::createMemoryWriteFile(void* memory, s32 len,
const core::string<c16>& fileName, bool deleteMemoryWhenDropped)
{
if (!memory)
return 0;
else
return new CMemoryFile(memory, len, fileName, deleteMemoryWhenDropped);
}
//! Opens a file for write access.
IWriteFile* CFileSystem::createAndWriteFile(const core::string<c16>& filename, bool append)
{
return createWriteFile(filename, append);
}
//! Adds an external archive loader to the engine.
void CFileSystem::addArchiveLoader(IArchiveLoader* loader)
{
if (!loader)
return;
//loader->grab();
ArchiveLoader.push_back ( loader );
}
//! move the hirarchy of the filesystem. moves sourceIndex relative up or down
bool CFileSystem::moveFileArchive( u32 sourceIndex, s32 relative )
{
bool r = false;
const s32 dest = (s32) sourceIndex + relative;
const s32 dir = relative < 0 ? -1 : 1;
const s32 sourceEnd = ((s32) FileArchive.size() ) - 1;
IFileArchive *t;
for ( s32 s = (s32) sourceIndex;s != dest; s += dir )
{
if ( s < 0 || s > sourceEnd || s + dir < 0 || s + dir > sourceEnd )
continue;
t = FileArchive [ s + dir ];
FileArchive [ s + dir ] = FileArchive [ s ];
FileArchive [ s ] = t;
r = true;
}
return r;
}
//! Adds an archive to the file system.
bool CFileSystem::registerFileArchive( const core::string<c16>& filename, bool ignoreCase, bool ignorePaths )
{
IFileArchive* archive = 0;
bool ret = false;
u32 i;
// try to load archive based on file name
for ( i = 0; i < ArchiveLoader.size(); ++i)
{
if ( ArchiveLoader[i]->isALoadableFileFormat( filename ) )
{
archive = ArchiveLoader[i]->createArchive( filename, ignoreCase, ignorePaths );
if (archive)
break;
}
}
// try to load archive based on content
if ( 0 == archive )
{
io::IReadFile* file = createAndOpenFile(filename);
if ( file )
{
for ( i = 0; i < ArchiveLoader.size(); ++i)
{
file->seek ( 0 );
if ( ArchiveLoader[i]->isALoadableFileFormat( file ) )
{
file->seek ( 0 );
archive = ArchiveLoader[i]->createArchive( file, ignoreCase, ignorePaths );
if (archive)
break;
}
}
file->drop ();
}
}
if ( archive )
{
FileArchive.push_back ( archive );
ret = true;
}
else
{
char buf[256];
core::stringw showName ( filename );
snprintf ( buf, sizeof (buf), "Could not create archive for: %ls\n", showName.c_str() );
os::Printer::log( buf ,ELL_ERROR);
}
return ret;
}
//! removes an archive to the file system.
bool CFileSystem::unregisterFileArchive( u32 index )
{
bool ret = false;
if ( index < FileArchive.size () )
{
FileArchive[index]->drop();
FileArchive.erase ( index );
ret = true;
}
return ret;
}
//! gets an archive
u32 CFileSystem::getFileArchiveCount()
{
return FileArchive.size();
}
IFileArchive* CFileSystem::getFileArchive( u32 index )
{
return index < getFileArchiveCount () ? FileArchive [ index ] : 0;
}
//! Returns the string of the current working directory
const core::string<c16>& CFileSystem::getWorkingDirectory()
{
eFileSystemType type = FileSystemType;
if ( type != FILESYSTEM_NATIVE )
{
type = FILESYSTEM_VIRTUAL;
}
else
{
const s32 FILE_SYSTEM_MAX_PATH = 1024;
WorkingDirectory [ type ].reserve ( FILE_SYSTEM_MAX_PATH );
c16* r = (c16*) WorkingDirectory [ type ].c_str();
#if defined( _IRR_USE_WINDOWS_CE_DEVICE_)
#elif defined( _IRR_WINDOWS_API_)
#if defined ( _IRR_WCHAR_FILESYSTEM )
_wgetcwd ( r, FILE_SYSTEM_MAX_PATH );
#else
_getcwd(r, FILE_SYSTEM_MAX_PATH);
#endif
#endif
#if (defined(_IRR_POSIX_API_) || defined(_IRR_OSX_PLATFORM_))
#if defined ( _IRR_WCHAR_FILESYSTEM )
wgetcwd ( r, FILE_SYSTEM_MAX_PATH );
#else
getcwd(r, (size_t)FILE_SYSTEM_MAX_PATH);
#endif
#endif
WorkingDirectory [ type ].validate();
}
return WorkingDirectory [ type ];
}
//! Changes the current Working Directory to the given string.
bool CFileSystem::changeWorkingDirectoryTo(const core::string<c16>& newDirectory)
{
bool success=false;
if ( FileSystemType != FILESYSTEM_NATIVE )
{
WorkingDirectory [ FILESYSTEM_VIRTUAL ].append ( newDirectory );
flattenFilename ( WorkingDirectory [ FILESYSTEM_VIRTUAL ], "" );
success = 1;
}
else
{
WorkingDirectory [ FILESYSTEM_NATIVE ] = newDirectory;
#if defined( _IRR_USE_WINDOWS_CE_DEVICE_)
success = true;
#elif defined( _MSC_VER)
#if defined ( _IRR_WCHAR_FILESYSTEM )
success=(_wchdir(newDirectory.c_str()) == 0);
#else
success=(_chdir(newDirectory.c_str()) == 0);
#endif
#else
success=(chdir(newDirectory.c_str()) == 0);
#endif
}
return success;
}
core::string<c16> CFileSystem::getAbsolutePath(const core::string<c16>& filename) const
{
c16 *p=0;
#if defined( _IRR_USE_WINDOWS_CE_DEVICE_)
return filename;
#elif defined( _IRR_WINDOWS_API_)
#if defined ( _IRR_WCHAR_FILESYSTEM )
c16 fpath[_MAX_PATH];
p = _wfullpath( fpath, filename.c_str(), _MAX_PATH);
#else
c8 fpath[_MAX_PATH];
p = _fullpath( fpath, filename.c_str(), _MAX_PATH);
#endif
#elif (defined(_IRR_POSIX_API_) || defined(_IRR_OSX_PLATFORM_))
c8 fpath[4096];
fpath[0]=0;
p = realpath(filename.c_str(), fpath);
if (!p)
{
// content in fpath is undefined at this point
if ('0'==fpath[0]) // seems like fpath wasn't altered
{
// at least remove a ./ prefix
if ('.'==filename[0] && '/'==filename[1])
return filename.subString(2, filename.size()-2);
else
return filename;
}
else
return core::string<c16>(fpath);
}
#endif
return core::string<c16>(p);
}
//! returns the directory part of a filename, i.e. all until the first
//! slash or backslash, excluding it. If no directory path is prefixed, a '.'
//! is returned.
core::string<c16> CFileSystem::getFileDir(const core::string<c16>& filename) const
{
// find last forward or backslash
s32 lastSlash = filename.findLast('/');
const s32 lastBackSlash = filename.findLast('\\');
lastSlash = lastSlash > lastBackSlash ? lastSlash : lastBackSlash;
if ((u32)lastSlash < filename.size())
return filename.subString(0, lastSlash);
else
return ".";
}
//! returns the base part of a filename, i.e. all except for the directory
//! part. If no directory path is prefixed, the full name is returned.
core::string<c16> CFileSystem::getFileBasename(const core::string<c16>& filename, bool keepExtension) const
{
// find last forward or backslash
s32 lastSlash = filename.findLast('/');
const s32 lastBackSlash = filename.findLast('\\');
lastSlash = core::max_(lastSlash, lastBackSlash);
s32 end = 0;
if (!keepExtension)
{
end = filename.findLast('.');
if (end == -1)
end=0;
else
end = filename.size()-end;
}
if ((u32)lastSlash < filename.size())
return filename.subString(lastSlash+1, filename.size()-lastSlash-1-end);
else if (end != 0)
return filename.subString(0, filename.size()-end);
else
return filename;
}
//! flaten a path and file name for example: "/you/me/../." becomes "/you"
core::string<c16>& CFileSystem::flattenFilename( core::string<c16>& directory, const core::string<c16>& root ) const
{
directory.replace ( '\\', '/' );
if ( lastChar ( directory ) != '/' )
directory.append ( '/' );
core::string<c16> dir;
core::string<c16> subdir;
s32 lastpos = 0;
s32 pos = 0;
while ( ( pos = directory.findNext ( '/', lastpos ) ) >= 0 )
{
subdir = directory.subString ( lastpos, pos - lastpos + 1 );
if ( subdir == "../" )
{
deletePathFromPath ( dir, 2 );
}
else
if ( subdir == "/" )
{
dir = root;
}
else
if ( subdir != "./" )
{
dir.append ( subdir );
}
lastpos = pos + 1;
}
directory = dir;
return directory;
}
//! Creates a list of files and directories in the current working directory
eFileSystemType CFileSystem::setFileListSystem( eFileSystemType listType)
{
eFileSystemType current = FileSystemType;
FileSystemType = listType;
return current;
}
//! Creates a list of files and directories in the current working directory
IFileList* CFileSystem::createFileList()
{
FileEntry e2;
FileEntry e3;
if ( FileSystemType == FILESYSTEM_NATIVE )
return new CFileList();
CFileList* r = new CFileList( "virtual" );
r->Path = WorkingDirectory [ FILESYSTEM_VIRTUAL ];
for ( u32 i = 0; i != FileArchive.size(); ++i)
{
CFileList* flist[2] = { 0, 0 };
//! merge relative folder file archives
if ( FileArchive[i]->getArchiveType() == "mount" )
{
eFileSystemType currentType = setFileListSystem ( FILESYSTEM_NATIVE );
core::string<c16> save ( getWorkingDirectory () );
core::string<c16> current;
current = FileArchive[i]->getArchiveName() + WorkingDirectory [ FILESYSTEM_VIRTUAL ];
flattenFilename ( current );
if ( changeWorkingDirectoryTo ( current ) )
{
flist[0] = new CFileList( "mountpoint" );
flist[0]->constructNative ();
changeWorkingDirectoryTo ( save );
}
setFileListSystem ( currentType );
}
else
if ( FileArchive[i]->getArchiveType() == "zip" )
{
flist[0] = new CFileList( "zip" );
flist[1] = new CFileList( "zip directory" );
// add relative navigation
e2.isDirectory = 1;
e2.Name = ".";
e2.FullName = r->Path + e2.Name;
e2.Size = 0;
flist[1]->Files.push_back ( e2 );
e2.Name = "..";
e2.FullName = r->Path + e2.Name;
flist[1]->Files.push_back ( e2 );
for ( u32 g = 0; g < FileArchive[i]->getFileCount(); ++g)
{
const SZipFileEntry *e = (SZipFileEntry*) FileArchive[i]->getFileInfo ( g );
s32 test = isInSameDirectory ( r->Path, e->zipFileName );
if ( test < 0 || test > 1 )
continue;
e2.Size = e->header.DataDescriptor.UncompressedSize;
e2.isDirectory = e2.Size == 0;
// check missing directories
if ( !e2.isDirectory && test == 1 )
{
e3.Size = 0;
e3.isDirectory = 1;
e3.FullName = e->path;
e3.Name = e->path.subString ( r->Path.size(), e->path.size() - r->Path.size() - 1 );
if ( flist[1]->Files.binary_search ( e3 ) < 0 )
flist[1]->Files.push_back ( e3 );
}
else
{
e2.FullName = e->zipFileName;
e2.Name = e->simpleFileName;
if ( !e2.isDirectory )
core::deletePathFromFilename ( e2.Name );
flist[0]->Files.push_back ( e2 );
}
}
}
// add file to virtual directory
for ( u32 g = 0; g < 2; ++g )
{
if ( !flist[g] )
continue;
for ( u32 j = 0; j != flist[g]->Files.size(); ++j )
r->Files.push_back ( flist[g]->Files[j] );
flist[g]->drop();
}
}
r->Files.sort();
return r;
}
//! determines if a file exists and would be able to be opened.
bool CFileSystem::existFile(const core::string<c16>& filename) const
{
u32 i;
for (i=0; i < FileArchive.size(); ++i)
if ( FileArchive[i]->findFile(filename)!=-1)
return true;
#if defined ( _IRR_WCHAR_FILESYSTEM )
FILE* f = _wfopen(filename.c_str(), L"rb");
#else
FILE* f = fopen(filename.c_str(), "rb");
#endif
if (f)
{
fclose(f);
return true;
}
_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
return false;
}
//! Creates a XML Reader from a file.
IXMLReader* CFileSystem::createXMLReader(const core::string<c16>& filename)
{
IReadFile* file = createAndOpenFile(filename);
if (!file)
return 0;
IXMLReader* reader = createXMLReader(file);
file->drop();
return reader;
}
//! Creates a XML Reader from a file.
IXMLReader* CFileSystem::createXMLReader(IReadFile* file)
{
if (!file)
return 0;
return createIXMLReader(file);
}
//! Creates a XML Reader from a file.
IXMLReaderUTF8* CFileSystem::createXMLReaderUTF8(const core::string<c16>& filename)
{
IReadFile* file = createAndOpenFile(filename);
if (!file)
return 0;
IXMLReaderUTF8* reader = createIXMLReaderUTF8(file);
file->drop();
return reader;
}
//! Creates a XML Reader from a file.
IXMLReaderUTF8* CFileSystem::createXMLReaderUTF8(IReadFile* file)
{
if (!file)
return 0;
return createIXMLReaderUTF8(file);
}
//! Creates a XML Writer from a file.
IXMLWriter* CFileSystem::createXMLWriter(const core::string<c16>& filename)
{
IWriteFile* file = createAndWriteFile(filename);
IXMLWriter* writer = createXMLWriter(file);
file->drop();
return writer;
}
//! Creates a XML Writer from a file.
IXMLWriter* CFileSystem::createXMLWriter(IWriteFile* file)
{
return new CXMLWriter(file);
}
//! creates a filesystem which is able to open files from the ordinary file system,
//! and out of zipfiles, which are able to be added to the filesystem.
IFileSystem* createFileSystem()
{
return new CFileSystem();
}
//! Creates a new empty collection of attributes, usable for serialization and more.
IAttributes* CFileSystem::createEmptyAttributes(video::IVideoDriver* driver)
{
return new CAttributes(driver);
}
} // end namespace irr
} // end namespace io
// Copyright (C) 2002-2009 Nikolaus Gebhardt
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h
#include "IrrCompileConfig.h"
#ifdef _IRR_COMPILE_WITH_BSP_LOADER_
#include "CQ3LevelMesh.h"
#include "ISceneManager.h"
#include "os.h"
#include "SMeshBufferLightMap.h"
#include "irrString.h"
#include "ILightSceneNode.h"
#include "IQ3Shader.h"
#include "IFileList.h"
//#define TJUNCTION_SOLVER_ROUND
//#define TJUNCTION_SOLVER_0125
namespace irr
{
namespace scene
{
using namespace quake3;
//! constructor
CQ3LevelMesh::CQ3LevelMesh(io::IFileSystem* fs, scene::ISceneManager* smgr,
const Q3LevelLoadParameter &loadParam)
: LoadParam(loadParam), Textures(0), LightMaps(0),
Vertices(0), Faces(0), Planes(0), Nodes(0), Leafs(0), LeafFaces(0),
MeshVerts(0), Brushes(0), FileSystem(fs), SceneManager(smgr)
{
#ifdef _DEBUG
IReferenceCounted::setDebugName("CQ3LevelMesh");
#endif
for ( s32 i = 0; i!= E_Q3_MESH_SIZE; ++i )
{
Mesh[i] = 0;
}
Driver = smgr ? smgr->getVideoDriver() : 0;
if (Driver)
Driver->grab();
if (FileSystem)
FileSystem->grab();
// load default shaders
InitShader();
}
//! destructor
CQ3LevelMesh::~CQ3LevelMesh()
{
cleanLoader ();
if (Driver)
Driver->drop();
if (FileSystem)
FileSystem->drop();
for ( s32 i = 0; i!= E_Q3_MESH_SIZE; ++i )
{
if ( Mesh[i] )
{
Mesh[i]->drop();
Mesh[i] = 0;
}
}
ReleaseShader();
ReleaseEntity();
}
//! loads a level from a .bsp-File. Also tries to load all needed textures. Returns true if successful.
bool CQ3LevelMesh::loadFile(io::IReadFile* file)
{
if (!file)
return false;
LevelName = file->getFileName();
file->read(&header, sizeof(tBSPHeader));
#ifdef __BIG_ENDIAN__
header.strID = os::Byteswap::byteswap(header.strID);
header.version = os::Byteswap::byteswap(header.version);
#endif
if ( (header.strID != 0x50534249 || // IBSP
( header.version != 0x2e // quake3
&& header.version != 0x2f // rtcw
)
)
&&
( header.strID != 0x50534252 || header.version != 1 ) // RBSP, starwars jedi, sof
)
{
os::Printer::log("Could not load .bsp file, unknown header.", file->getFileName(), ELL_ERROR);
return false;
}
#if 0
if ( header.strID == 0x50534252 ) // RBSP Raven
{
LoadParam.swapHeader = 1;
}
#endif
// now read lumps
file->read(&Lumps[0], sizeof(tBSPLump)*kMaxLumps);
s32 i;
if ( LoadParam.swapHeader )
{
for ( i=0; i< kMaxLumps;++i)
{
Lumps[i].offset = os::Byteswap::byteswap(Lumps[i].offset);
Lumps[i].length = os::Byteswap::byteswap(Lumps[i].length);
}
}
for ( i = 0; i!= E_Q3_MESH_SIZE; ++i )
{
Mesh[i] = new SMesh();
}
ReleaseEntity();
// load everything
loadEntities(&Lumps[kEntities], file); // load the entities
loadTextures(&Lumps[kShaders], file); // Load the textures
loadLightmaps(&Lumps[kLightmaps], file); // Load the lightmaps
loadVerts(&Lumps[kVertices], file); // Load the vertices
loadFaces(&Lumps[kFaces], file); // Load the faces
loadPlanes(&Lumps[kPlanes], file); // Load the Planes of the BSP
loadNodes(&Lumps[kNodes], file); // load the Nodes of the BSP
loadLeafs(&Lumps[kLeafs], file); // load the Leafs of the BSP
loadLeafFaces(&Lumps[kLeafFaces], file); // load the Faces of the Leafs of the BSP
loadVisData(&Lumps[kVisData], file); // load the visibility data of the clusters
loadModels(&Lumps[kModels], file); // load the models
loadMeshVerts(&Lumps[kMeshVerts], file); // load the mesh vertices
loadBrushes(&Lumps[kBrushes], file); // load the brushes of the BSP
loadBrushSides(&Lumps[kBrushSides], file); // load the brushsides of the BSP
loadLeafBrushes(&Lumps[kLeafBrushes], file); // load the brushes of the leaf
loadFogs(&Lumps[kFogs], file ); // load the fogs
loadTextures();
constructMesh();
solveTJunction ();
cleanMeshes();
calcBoundingBoxes();
cleanLoader ();
return true;
}
/*!
*/
void CQ3LevelMesh::cleanLoader ()
{
delete [] Textures; Textures = 0;
delete [] LightMaps; LightMaps = 0;
delete [] Vertices; Vertices = 0;
delete [] Faces; Faces = 0;
delete [] Planes; Planes = 0;
delete [] Nodes; Nodes = 0;
delete [] Leafs; Leafs = 0;
delete [] LeafFaces; LeafFaces = 0;
delete [] MeshVerts; MeshVerts = 0;
delete [] Brushes; Brushes = 0;
Lightmap.clear ();
Tex.clear ();
}
//! returns the amount of frames in milliseconds. If the amount is 1, it is a static (=non animated) mesh.
u32 CQ3LevelMesh::getFrameCount() const
{
return 1;
}
//! returns the animated mesh based on a detail level. 0 is the lowest, 255 the highest detail. Note, that some Meshes will ignore the detail level.
IMesh* CQ3LevelMesh::getMesh(s32 frameInMs, s32 detailLevel, s32 startFrameLoop, s32 endFrameLoop)
{
return Mesh[frameInMs];
}
void CQ3LevelMesh::loadTextures(tBSPLump* l, io::IReadFile* file)
{
NumTextures = l->length / sizeof(tBSPTexture);
Textures = new tBSPTexture[NumTextures];
file->seek(l->offset);
file->read(Textures, l->length);
if ( LoadParam.swapHeader )
{
for (s32 i=0;i<NumTextures;++i)
{
Textures[i].flags = os::Byteswap::byteswap(Textures[i].flags);
Textures[i].contents = os::Byteswap::byteswap(Textures[i].contents);
//os::Printer::log("Loaded texture", Textures[i].strName, ELL_INFORMATION);
}
}
}
void CQ3LevelMesh::loadLightmaps(tBSPLump* l, io::IReadFile* file)
{
NumLightMaps = l->length / sizeof(tBSPLightmap);
LightMaps = new tBSPLightmap[NumLightMaps];
file->seek(l->offset);
file->read(LightMaps, l->length);
}
/*!
*/
void CQ3LevelMesh::loadVerts(tBSPLump* l, io::IReadFile* file)
{
NumVertices = l->length / sizeof(tBSPVertex);
Vertices = new tBSPVertex[NumVertices];
file->seek(l->offset);
file->read(Vertices, l->length);
if ( LoadParam.swapHeader )
for (s32 i=0;i<NumVertices;i++)
{
Vertices[i].vPosition[0] = os::Byteswap::byteswap(Vertices[i].vPosition[0]);
Vertices[i].vPosition[1] = os::Byteswap::byteswap(Vertices[i].vPosition[1]);
Vertices[i].vPosition[2] = os::Byteswap::byteswap(Vertices[i].vPosition[2]);
Vertices[i].vTextureCoord[0] = os::Byteswap::byteswap(Vertices[i].vTextureCoord[0]);
Vertices[i].vTextureCoord[1] = os::Byteswap::byteswap(Vertices[i].vTextureCoord[1]);
Vertices[i].vLightmapCoord[0] = os::Byteswap::byteswap(Vertices[i].vLightmapCoord[0]);
Vertices[i].vLightmapCoord[1] = os::Byteswap::byteswap(Vertices[i].vLightmapCoord[1]);
Vertices[i].vNormal[0] = os::Byteswap::byteswap(Vertices[i].vNormal[0]);
Vertices[i].vNormal[1] = os::Byteswap::byteswap(Vertices[i].vNormal[1]);
Vertices[i].vNormal[2] = os::Byteswap::byteswap(Vertices[i].vNormal[2]);
}
}
/*!
*/
void CQ3LevelMesh::loadFaces(tBSPLump* l, io::IReadFile* file)
{
NumFaces = l->length / sizeof(tBSPFace);
Faces = new tBSPFace[NumFaces];
file->seek(l->offset);
file->read(Faces, l->length);
if ( LoadParam.swapHeader )
{
for ( s32 i=0;i<NumFaces;i++)
{
Faces[i].textureID = os::Byteswap::byteswap(Faces[i].textureID);
Faces[i].fogNum = os::Byteswap::byteswap(Faces[i].fogNum);
Faces[i].type = os::Byteswap::byteswap(Faces[i].type);
Faces[i].vertexIndex = os::Byteswap::byteswap(Faces[i].vertexIndex);
Faces[i].numOfVerts = os::Byteswap::byteswap(Faces[i].numOfVerts);
Faces[i].meshVertIndex = os::Byteswap::byteswap(Faces[i].meshVertIndex);
Faces[i].numMeshVerts = os::Byteswap::byteswap(Faces[i].numMeshVerts);
Faces[i].lightmapID = os::Byteswap::byteswap(Faces[i].lightmapID);
Faces[i].lMapCorner[0] = os::Byteswap::byteswap(Faces[i].lMapCorner[0]);
Faces[i].lMapCorner[1] = os::Byteswap::byteswap(Faces[i].lMapCorner[1]);
Faces[i].lMapSize[0] = os::Byteswap::byteswap(Faces[i].lMapSize[0]);
Faces[i].lMapSize[1] = os::Byteswap::byteswap(Faces[i].lMapSize[1]);
Faces[i].lMapPos[0] = os::Byteswap::byteswap(Faces[i].lMapPos[0]);
Faces[i].lMapPos[1] = os::Byteswap::byteswap(Faces[i].lMapPos[1]);
Faces[i].lMapPos[2] = os::Byteswap::byteswap(Faces[i].lMapPos[2]);
Faces[i].lMapBitsets[0][0] = os::Byteswap::byteswap(Faces[i].lMapBitsets[0][0]);
Faces[i].lMapBitsets[0][1] = os::Byteswap::byteswap(Faces[i].lMapBitsets[0][1]);
Faces[i].lMapBitsets[0][2] = os::Byteswap::byteswap(Faces[i].lMapBitsets[0][2]);
Faces[i].lMapBitsets[1][0] = os::Byteswap::byteswap(Faces[i].lMapBitsets[1][0]);
Faces[i].lMapBitsets[1][1] = os::Byteswap::byteswap(Faces[i].lMapBitsets[1][1]);
Faces[i].lMapBitsets[1][2] = os::Byteswap::byteswap(Faces[i].lMapBitsets[1][2]);
Faces[i].vNormal[0] = os::Byteswap::byteswap(Faces[i].vNormal[0]);
Faces[i].vNormal[1] = os::Byteswap::byteswap(Faces[i].vNormal[1]);
Faces[i].vNormal[2] = os::Byteswap::byteswap(Faces[i].vNormal[2]);
Faces[i].size[0] = os::Byteswap::byteswap(Faces[i].size[0]);
Faces[i].size[1] = os::Byteswap::byteswap(Faces[i].size[1]);
}
}
}
/*!
*/
void CQ3LevelMesh::loadPlanes(tBSPLump* l, io::IReadFile* file)
{
// ignore
}
/*!
*/
void CQ3LevelMesh::loadNodes(tBSPLump* l, io::IReadFile* file)
{
// ignore
}
/*!
*/
void CQ3LevelMesh::loadLeafs(tBSPLump* l, io::IReadFile* file)
{
// ignore
}
/*!
*/
void CQ3LevelMesh::loadLeafFaces(tBSPLump* l, io::IReadFile* file)
{
// ignore
}
/*!
*/
void CQ3LevelMesh::loadVisData(tBSPLump* l, io::IReadFile* file)
{
// ignore
}
/*!
*/
void CQ3LevelMesh::loadEntities(tBSPLump* l, io::IReadFile* file)
{
core::array<u8> entity;
entity.set_used( l->length + 2 );
entity[l->length + 1 ] = 0;
file->seek(l->offset);
file->read( entity.pointer(), l->length);
parser_parse( entity.pointer(), l->length, &CQ3LevelMesh::scriptcallback_entity );
}
/*!
load fog brushes
*/
void CQ3LevelMesh::loadFogs(tBSPLump* l, io::IReadFile* file)
{
u32 files = l->length / sizeof(tBSPFog);
file->seek( l->offset );
tBSPFog fog;
const IShader *shader;
STexShader t;
for ( u32 i = 0; i!= files; ++i )
{
file->read( &fog, sizeof( fog ) );
shader = getShader( fog.shader );
t.Texture = 0;
t.ShaderID = shader ? shader->id : -1;
FogMap.push_back ( t );
}
}
/*!
load models named in bsp
*/
void CQ3LevelMesh::loadModels(tBSPLump* l, io::IReadFile* file)
{
u32 files = l->length / sizeof(tBSPModel);
file->seek( l->offset );
tBSPModel def;
for ( u32 i = 0; i!= files; ++i )
{
file->read( &def, sizeof( def ) );
}
}
/*!
*/
void CQ3LevelMesh::loadMeshVerts(tBSPLump* l, io::IReadFile* file)
{
NumMeshVerts = l->length / sizeof(s32);
MeshVerts = new s32[NumMeshVerts];
file->seek(l->offset);
file->read(MeshVerts, l->length);
if ( LoadParam.swapHeader )
{
for (int i=0;i<NumMeshVerts;i++)
MeshVerts[i] = os::Byteswap::byteswap(MeshVerts[i]);
}
}
/*!
*/
void CQ3LevelMesh::loadBrushes(tBSPLump* l, io::IReadFile* file)
{
// ignore
}
/*!
*/
void CQ3LevelMesh::loadBrushSides(tBSPLump* l, io::IReadFile* file)
{
// ignore
}
/*!
*/
void CQ3LevelMesh::loadLeafBrushes(tBSPLump* l, io::IReadFile* file)
{
// ignore
}
/*!
*/
inline bool isQ3WhiteSpace( const u8 symbol )
{
return symbol == ' ' || symbol == '\t' || symbol == '\r';
}
/*!
*/
inline bool isQ3ValidName( const u8 symbol )
{
return (symbol >= 'a' && symbol <= 'z' ) ||
(symbol >= 'A' && symbol <= 'Z' ) ||
(symbol >= '0' && symbol <= '9' ) ||
(symbol == '/' || symbol == '_' || symbol == '.' );
}
/*!
*/
void CQ3LevelMesh::parser_nextToken()
{
u8 symbol;
Parser.token = "";
Parser.tokenresult = Q3_TOKEN_UNRESOLVED;
// skip white space
do
{
if ( Parser.index >= Parser.sourcesize )
{
Parser.tokenresult = Q3_TOKEN_EOF;
return;
}
symbol = Parser.source [ Parser.index ];
Parser.index += 1;
} while ( isQ3WhiteSpace( symbol ) );
// first symbol, one symbol
switch ( symbol )
{
case 0:
Parser.tokenresult = Q3_TOKEN_EOF;
return;
case '/':
// comment or divide
if ( Parser.index >= Parser.sourcesize )
{
Parser.tokenresult = Q3_TOKEN_EOF;
return;
}
symbol = Parser.source [ Parser.index ];
Parser.index += 1;
if ( isQ3WhiteSpace( symbol ) )
{
Parser.tokenresult = Q3_TOKEN_MATH_DIVIDE;
return;
}
else
if ( symbol == '*' )
{
// C-style comment in quake?
}
else
if ( symbol == '/' )
{
// skip to eol
do
{
if ( Parser.index >= Parser.sourcesize )
{
Parser.tokenresult = Q3_TOKEN_EOF;
return;
}
symbol = Parser.source [ Parser.index ];
Parser.index += 1;
} while ( symbol != '\n' );
Parser.tokenresult = Q3_TOKEN_COMMENT;
return;
}
// take /[name] as valid token..?!?!?. mhmm, maybe
break;
case '\n':
Parser.tokenresult = Q3_TOKEN_EOL;
return;
case '{':
Parser.tokenresult = Q3_TOKEN_START_LIST;
return;
case '}':
Parser.tokenresult = Q3_TOKEN_END_LIST;
return;
case '"':
// string literal
do
{
if ( Parser.index >= Parser.sourcesize )
{
Parser.tokenresult = Q3_TOKEN_EOF;
return;
}
symbol = Parser.source [ Parser.index ];
Parser.index += 1;
if ( symbol != '"' )
Parser.token.append( symbol );
} while ( symbol != '"' );
Parser.tokenresult = Q3_TOKEN_ENTITY;
return;
}
// user identity
Parser.token.append( symbol );
// continue till whitespace
bool validName = true;
do
{
if ( Parser.index >= Parser.sourcesize )
{
Parser.tokenresult = Q3_TOKEN_EOF;
return;
}
symbol = Parser.source [ Parser.index ];
validName = isQ3ValidName( symbol );
if ( validName )
{
Parser.token.append( symbol );
Parser.index += 1;
}
} while ( validName );
Parser.tokenresult = Q3_TOKEN_TOKEN;
return;
}
/*
parse entity & shader
calls callback on content in {}
*/
void CQ3LevelMesh::parser_parse( const void * data, const u32 size, CQ3LevelMesh::tParserCallback callback )
{
Parser.source = static_cast<const c8*>(data);
Parser.sourcesize = size;
Parser.index = 0;
SVarGroupList *groupList;
s32 active;
s32 last;
SVariable entity ( "" );
groupList = new SVarGroupList();
groupList->VariableGroup.push_back( SVarGroup() );
active = last = 0;
do
{
parser_nextToken();
switch ( Parser.tokenresult )
{
case Q3_TOKEN_START_LIST:
{
//stack = core::min_( stack + 1, 7 );
groupList->VariableGroup.push_back( SVarGroup() );
last = active;
active = groupList->VariableGroup.size() - 1;
entity.clear();
} break;
// a unregisterd variable is finished
case Q3_TOKEN_EOL:
{
if ( entity.isValid() )
{
groupList->VariableGroup[active].Variable.push_back( entity );
entity.clear();
}
} break;
case Q3_TOKEN_TOKEN:
case Q3_TOKEN_ENTITY:
{
Parser.token.make_lower();
// store content based on line-delemiter
if ( 0 == entity.isValid() )
{
entity.name = Parser.token;
entity.content = "";
}
else
{
if ( entity.content.size() )
{
entity.content += " ";
}
entity.content += Parser.token;
}
} break;
case Q3_TOKEN_END_LIST:
{
//stack = core::max_( stack - 1, 0 );
// close tag for first
if ( active == 1 )
{
(this->*callback)( groupList, Q3_TOKEN_END_LIST );
// new group
groupList->drop();
groupList = new SVarGroupList();
groupList->VariableGroup.push_back( SVarGroup() );
last = 0;
}
active = last;
entity.clear();
} break;
}
} while ( Parser.tokenresult != Q3_TOKEN_EOF );
(this->*callback)( groupList, Q3_TOKEN_EOF );
groupList->drop();
}
/*
this loader applies only textures for stage 1 & 2
*/
s32 CQ3LevelMesh::setShaderFogMaterial( video::SMaterial &material, const tBSPFace * face ) const
{
material.MaterialType = video::EMT_SOLID;
material.Wireframe = false;
material.Lighting = false;
material.BackfaceCulling = false;
material.setTexture(0, 0);
material.setTexture(1, 0);
material.setTexture(2, 0);
material.setTexture(3, 0);
material.ZBuffer = video::ECFN_LESSEQUAL;
material.ZWriteEnable = false;
material.MaterialTypeParam = 0.f;
s32 shaderState = -1;
if ( (u32) face->fogNum < FogMap.size() )
{
material.setTexture(0, FogMap [ face->fogNum ].Texture);
shaderState = FogMap [ face->fogNum ].ShaderID;
}
return shaderState;
}
/*
this loader applies only textures for stage 1 & 2
*/
s32 CQ3LevelMesh::setShaderMaterial( video::SMaterial &material, const tBSPFace * face ) const
{
material.MaterialType = video::EMT_SOLID;
material.Wireframe = false;
material.Lighting = false;
material.BackfaceCulling = true;
material.setTexture(0, 0);
material.setTexture(1, 0);
material.setTexture(2, 0);
material.setTexture(3, 0);
material.ZBuffer = video::ECFN_LESSEQUAL;
material.ZWriteEnable = true;
material.MaterialTypeParam = 0.f;
s32 shaderState = -1;
if ( face->textureID >= 0 )
{
material.setTexture(0, Tex [ face->textureID ].Texture);
shaderState = Tex [ face->textureID ].ShaderID;
}
if ( face->lightmapID >= 0 )
{
material.setTexture(1, Lightmap [ face->lightmapID ]);
material.MaterialType = LoadParam.defaultLightMapMaterial;
}
// store shader ID
material.MaterialTypeParam2 = (f32) shaderState;
const IShader *shader = getShader(shaderState);
if ( 0 == shader )
return shaderState;
return shaderState;
#if 0
const SVarGroup *group;
// generic
group = shader->getGroup( 1 );
if ( group )
{
material.BackfaceCulling = getCullingFunction( group->get( "cull" ) );
if ( group->isDefined( "surfaceparm", "nolightmap" ) )
{
material.MaterialType = video::EMT_SOLID;
material.setTexture(1, 0);
}
}
// try to get the best of the 8 texture stages..
// texture 1, texture 2
u32 startPos;
for ( s32 g = 2; g <= 3; ++g )
{
group = shader->getGroup( g );
if ( 0 == group )
continue;
startPos = 0;
if ( group->isDefined( "depthwrite" ) )
{
material.ZWriteEnable = true;
}
SBlendFunc blendfunc ( LoadParam.defaultModulate );
getBlendFunc( group->get( "blendfunc" ), blendfunc );
getBlendFunc( group->get( "alphafunc" ), blendfunc );
if ( 0 == LoadParam.alpharef &&
( blendfunc.type == video::EMT_TRANSPARENT_ALPHA_CHANNEL ||
blendfunc.type == video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF
)
)
{
blendfunc.type = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
blendfunc.param0 = 0.f;
}
material.MaterialType = blendfunc.type;
material.MaterialTypeParam = blendfunc.param0;
// try if we can match better
shaderState |= (material.MaterialType == video::EMT_SOLID ) ? 0x00020000 : 0;
}
//material.BackfaceCulling = false;
if ( shader->VarGroup->VariableGroup.size() <= 4 )
{
shaderState |= 0x00010000;
}
material.MaterialTypeParam2 = (f32) shaderState;
return shaderState;
#endif
}
/*!
*/
void CQ3LevelMesh::solveTJunction()
{
}
/*!
constructs a mesh from the quake 3 level file.
*/
void CQ3LevelMesh::constructMesh()
{
if ( LoadParam.verbose > 0 )
{
LoadParam.startTime = os::Timer::getRealTime();
if ( LoadParam.verbose > 1 )
{
snprintf( buf, sizeof ( buf ),
"quake3::constructMesh start to create %d faces, %d vertices,%d mesh vertices",
NumFaces,
NumVertices,
NumMeshVerts
);
os::Printer::log(buf, ELL_INFORMATION);
}
}
s32 i, j, k,s;
s32 *index;
video::S3DVertex2TCoords temp[3];
video::SMaterial material;
video::SMaterial material2;
SToBuffer item [ E_Q3_MESH_SIZE ];
u32 itemSize;
for ( i=0; i < NumFaces; ++i)
{
const tBSPFace * face = Faces + i;
s32 shaderState = setShaderMaterial( material, face );
itemSize = 0;
const IShader *shader = getShader(shaderState);
if ( face->fogNum >= 0 )
{
setShaderFogMaterial ( material2, face );
item[itemSize].index = E_Q3_MESH_FOG;
item[itemSize].takeVertexColor = 1;
itemSize += 1;
}
switch( face->type )
{
case 1: // normal polygons
case 2: // patches
case 3: // meshes
{
if ( 0 == shader )
{
if ( LoadParam.cleanUnResolvedMeshes || material.getTexture(0) )
{
item[itemSize].takeVertexColor = 1;
item[itemSize].index = E_Q3_MESH_GEOMETRY;
itemSize += 1;
}
else
{
item[itemSize].takeVertexColor = 1;
item[itemSize].index = E_Q3_MESH_UNRESOLVED;
itemSize += 1;
}
}
else
{
item[itemSize].takeVertexColor = 1;
item[itemSize].index = E_Q3_MESH_ITEMS;
itemSize += 1;
}
} break;
case 4: // billboards
//item[itemSize].takeVertexColor = 1;
//item[itemSize].index = E_Q3_MESH_ITEMS;
//itemSize += 1;
break;
}
for ( u32 g = 0; g != itemSize; ++g )
{
scene::SMeshBufferLightMap* buffer = 0;
if ( item[g].index == E_Q3_MESH_GEOMETRY )
{
if ( 0 == item[g].takeVertexColor )
{
item[g].takeVertexColor = material.getTexture(0) == 0 || material.getTexture(1) == 0;
}
if (Faces[i].lightmapID < -1 || Faces[i].lightmapID > NumLightMaps-1)
{
Faces[i].lightmapID = -1;
}
#if 0
// there are lightmapsids and textureid with -1
const s32 tmp_index = ((Faces[i].lightmapID+1) * (NumTextures+1)) + (Faces[i].textureID+1);
buffer = (SMeshBufferLightMap*) Mesh[E_Q3_MESH_GEOMETRY]->getMeshBuffer(tmp_index);
buffer->setHardwareMappingHint ( EHM_STATIC );
buffer->getMaterial() = material;
#endif
}
// Construct a unique mesh for each shader or combine meshbuffers for same shader
if ( 0 == buffer )
{
if ( LoadParam.mergeShaderBuffer == 1 )
{
// combine
buffer = (SMeshBufferLightMap*) Mesh[ item[g].index ]->getMeshBuffer(
item[g].index != E_Q3_MESH_FOG ? material : material2 );
}
// create a seperate mesh buffer
if ( 0 == buffer )
{
buffer = new scene::SMeshBufferLightMap();
Mesh[ item[g].index ]->addMeshBuffer( buffer );
buffer->drop();
buffer->getMaterial() = item[g].index != E_Q3_MESH_FOG ? material : material2;
if ( item[g].index == E_Q3_MESH_GEOMETRY )
buffer->setHardwareMappingHint ( EHM_STATIC );
}
}
switch(Faces[i].type)
{
case 4: // billboards
break;
case 2: // patches
createCurvedSurface_bezier( buffer, i,
LoadParam.patchTesselation,
item[g].takeVertexColor
);
break;
case 1: // normal polygons
case 3: // mesh vertices
index = MeshVerts + face->meshVertIndex;
k = buffer->getVertexCount();
// reallocate better if many small meshes are used
s = buffer->getIndexCount()+face->numMeshVerts;
if ( buffer->Indices.allocated_size () < (u32) s )
{
if ( buffer->Indices.allocated_size () > 0 &&
face->numMeshVerts < 20 && NumFaces > 1000
)
{
s = buffer->getIndexCount() + (NumFaces >> 3 * face->numMeshVerts );
}
buffer->Indices.reallocate( s);
}
for ( j = 0; j < face->numMeshVerts; ++j )
{
buffer->Indices.push_back( k + index [j] );
}
s = k+face->numOfVerts;
if ( buffer->Vertices.allocated_size () < (u32) s )
{
if ( buffer->Indices.allocated_size () > 0 &&
face->numOfVerts < 20 && NumFaces > 1000
)
{
s = buffer->getIndexCount() + (NumFaces >> 3 * face->numOfVerts );
}
buffer->Vertices.reallocate( s);
}
for ( j = 0; j != face->numOfVerts; ++j )
{
copy( &temp[0], &Vertices[ j + face->vertexIndex ], item[g].takeVertexColor );
buffer->Vertices.push_back( temp[0] );
}
break;
} // end switch
}
}
if ( LoadParam.verbose > 0 )
{
LoadParam.endTime = os::Timer::getRealTime();
snprintf( buf, sizeof ( buf ),
"quake3::constructMesh needed %04d ms to create %d faces, %d vertices,%d mesh vertices",
LoadParam.endTime - LoadParam.startTime,
NumFaces,
NumVertices,
NumMeshVerts
);
os::Printer::log(buf, ELL_INFORMATION);
}
}
void CQ3LevelMesh::S3DVertex2TCoords_64::copy( video::S3DVertex2TCoords &dest ) const
{
#if defined (TJUNCTION_SOLVER_ROUND)
dest.Pos.X = core::round_( (f32) Pos.X );
dest.Pos.Y = core::round_( (f32) Pos.Y );
dest.Pos.Z = core::round_( (f32) Pos.Z );
#elif defined (TJUNCTION_SOLVER_0125)
dest.Pos.X = (f32) ( floor ( Pos.X * 8.f + 0.5 ) * 0.125 );
dest.Pos.Y = (f32) ( floor ( Pos.Y * 8.f + 0.5 ) * 0.125 );
dest.Pos.Z = (f32) ( floor ( Pos.Z * 8.f + 0.5 ) * 0.125 );
#else
dest.Pos.X = (f32) Pos.X;
dest.Pos.Y = (f32) Pos.Y;
dest.Pos.Z = (f32) Pos.Z;
#endif
dest.Normal.X = (f32) Normal.X;
dest.Normal.Y = (f32) Normal.Y;
dest.Normal.Z = (f32) Normal.Z;
dest.Normal.normalize();
dest.Color = Color.toSColor();
dest.TCoords.X = (f32) TCoords.X;
dest.TCoords.Y = (f32) TCoords.Y;
dest.TCoords2.X = (f32) TCoords2.X;
dest.TCoords2.Y = (f32) TCoords2.Y;
}
void CQ3LevelMesh::copy( S3DVertex2TCoords_64 * dest, const tBSPVertex * source, s32 vertexcolor ) const
{
#if defined (TJUNCTION_SOLVER_ROUND)
dest->Pos.X = core::round_( source->vPosition[0] );
dest->Pos.Y = core::round_( source->vPosition[2] );
dest->Pos.Z = core::round_( source->vPosition[1] );
#elif defined (TJUNCTION_SOLVER_0125)
dest->Pos.X = (f32) ( floor ( source->vPosition[0] * 8.f + 0.5 ) * 0.125 );
dest->Pos.Y = (f32) ( floor ( source->vPosition[2] * 8.f + 0.5 ) * 0.125 );
dest->Pos.Z = (f32) ( floor ( source->vPosition[1] * 8.f + 0.5 ) * 0.125 );
#else
dest->Pos.X = source->vPosition[0];
dest->Pos.Y = source->vPosition[2];
dest->Pos.Z = source->vPosition[1];
#endif
dest->Normal.X = source->vNormal[0];
dest->Normal.Y = source->vNormal[2];
dest->Normal.Z = source->vNormal[1];
dest->Normal.normalize();
dest->TCoords.X = source->vTextureCoord[0];
dest->TCoords.Y = source->vTextureCoord[1];
dest->TCoords2.X = source->vLightmapCoord[0];
dest->TCoords2.Y = source->vLightmapCoord[1];
if ( vertexcolor )
{
//u32 a = core::s32_min( source->color[3] * LoadParam.defaultModulate, 255 );
u32 a = source->color[3];
u32 r = core::s32_min( source->color[0] * LoadParam.defaultModulate, 255 );
u32 g = core::s32_min( source->color[1] * LoadParam.defaultModulate, 255 );
u32 b = core::s32_min( source->color[2] * LoadParam.defaultModulate, 255 );
dest->Color.set(a * 1.f/255.f, r * 1.f/255.f,
g * 1.f/255.f, b * 1.f/255.f);
}
else
{
dest->Color.set( 1.f, 1.f, 1.f, 1.f );
}
}
inline void CQ3LevelMesh::copy( video::S3DVertex2TCoords * dest, const tBSPVertex * source, s32 vertexcolor ) const
{
#if defined (TJUNCTION_SOLVER_ROUND)
dest->Pos.X = core::round_( source->vPosition[0] );
dest->Pos.Y = core::round_( source->vPosition[2] );
dest->Pos.Z = core::round_( source->vPosition[1] );
#elif defined (TJUNCTION_SOLVER_0125)
dest->Pos.X = (f32) ( floor ( source->vPosition[0] * 8.f + 0.5 ) * 0.125 );
dest->Pos.Y = (f32) ( floor ( source->vPosition[2] * 8.f + 0.5 ) * 0.125 );
dest->Pos.Z = (f32) ( floor ( source->vPosition[1] * 8.f + 0.5 ) * 0.125 );
#else
dest->Pos.X = source->vPosition[0];
dest->Pos.Y = source->vPosition[2];
dest->Pos.Z = source->vPosition[1];
#endif
dest->Normal.X = source->vNormal[0];
dest->Normal.Y = source->vNormal[2];
dest->Normal.Z = source->vNormal[1];
dest->Normal.normalize();
dest->TCoords.X = source->vTextureCoord[0];
dest->TCoords.Y = source->vTextureCoord[1];
dest->TCoords2.X = source->vLightmapCoord[0];
dest->TCoords2.Y = source->vLightmapCoord[1];
if ( vertexcolor )
{
//u32 a = core::s32_min( source->color[3] * LoadParam.defaultModulate, 255 );
u32 a = source->color[3];
u32 r = core::s32_min( source->color[0] * LoadParam.defaultModulate, 255 );
u32 g = core::s32_min( source->color[1] * LoadParam.defaultModulate, 255 );
u32 b = core::s32_min( source->color[2] * LoadParam.defaultModulate, 255 );
dest->Color.color = a << 24 | r << 16 | g << 8 | b;
}
else
{
dest->Color.color = 0xFFFFFFFF;
}
}
void CQ3LevelMesh::SBezier::tesselate( s32 level )
{
//Calculate how many vertices across/down there are
s32 j, k;
column[0].set_used( level + 1 );
column[1].set_used( level + 1 );
column[2].set_used( level + 1 );
const f64 w = 0.0 + (1.0 / (f64) level );
//Tesselate along the columns
for( j = 0; j <= level; ++j)
{
const f64 f = w * (f64) j;
column[0][j] = control[0].getInterpolated_quadratic(control[3], control[6], f );
column[1][j] = control[1].getInterpolated_quadratic(control[4], control[7], f );
column[2][j] = control[2].getInterpolated_quadratic(control[5], control[8], f );
}
const u32 idx = Patch->Vertices.size();
Patch->Vertices.reallocate(idx+level*level);
//Tesselate across the rows to get final vertices
video::S3DVertex2TCoords v;
S3DVertex2TCoords_64 f;
for( j = 0; j <= level; ++j)
{
for( k = 0; k <= level; ++k)
{
f = column[0][j].getInterpolated_quadratic(column[1][j], column[2][j], w * (f64) k);
f.copy( v );
Patch->Vertices.push_back( v );
}
}
Patch->Indices.reallocate(Patch->Indices.size()+6*level*level);
// connect
for( j = 0; j < level; ++j)
{
for( k = 0; k < level; ++k)
{
const s32 inx = idx + ( k * ( level + 1 ) ) + j;
Patch->Indices.push_back( inx + 0 );
Patch->Indices.push_back( inx + (level + 1 ) + 0 );
Patch->Indices.push_back( inx + (level + 1 ) + 1 );
Patch->Indices.push_back( inx + 0 );
Patch->Indices.push_back( inx + (level + 1 ) + 1 );
Patch->Indices.push_back( inx + 1 );
}
}
}
/*!
no subdivision
*/
void CQ3LevelMesh::createCurvedSurface_nosubdivision(SMeshBufferLightMap* meshBuffer,
s32 faceIndex,
s32 patchTesselation,
s32 storevertexcolor)
{
tBSPFace * face = &Faces[faceIndex];
u32 j,k,m;
// number of control points across & up
const u32 controlWidth = face->size[0];
const u32 controlHeight = face->size[1];
if ( 0 == controlWidth || 0 == controlHeight )
return;
video::S3DVertex2TCoords v;
m = meshBuffer->Vertices.size();
meshBuffer->Vertices.reallocate(m+controlHeight * controlWidth);
for ( j = 0; j!= controlHeight * controlWidth; ++j )
{
copy( &v, &Vertices [ face->vertexIndex + j ], storevertexcolor );
meshBuffer->Vertices.push_back( v );
}
meshBuffer->Indices.reallocate(meshBuffer->Indices.size()+6*(controlHeight-1) * (controlWidth-1));
for ( j = 0; j!= controlHeight - 1; ++j )
{
for ( k = 0; k!= controlWidth - 1; ++k )
{
meshBuffer->Indices.push_back( m + k + 0 );
meshBuffer->Indices.push_back( m + k + controlWidth + 0 );
meshBuffer->Indices.push_back( m + k + controlWidth + 1 );
meshBuffer->Indices.push_back( m + k + 0 );
meshBuffer->Indices.push_back( m + k + controlWidth + 1 );
meshBuffer->Indices.push_back( m + k + 1 );
}
m += controlWidth;
}
}
/*!
*/
void CQ3LevelMesh::createCurvedSurface_bezier(SMeshBufferLightMap* meshBuffer,
s32 faceIndex,
s32 patchTesselation,
s32 storevertexcolor)
{
tBSPFace * face = &Faces[faceIndex];
u32 j,k;
// number of control points across & up
const u32 controlWidth = face->size[0];
const u32 controlHeight = face->size[1];
if ( 0 == controlWidth || 0 == controlHeight )
return;
// number of biquadratic patches
const u32 biquadWidth = (controlWidth - 1)/2;
const u32 biquadHeight = (controlHeight -1)/2;
if ( LoadParam.verbose > 1 )
{
LoadParam.startTime = os::Timer::getRealTime();
}
// Create space for a temporary array of the patch's control points
core::array<S3DVertex2TCoords_64> controlPoint;
controlPoint.set_used( controlWidth * controlHeight );
for( j = 0; j < controlPoint.size(); ++j)
{
copy( &controlPoint[j], &Vertices [ face->vertexIndex + j ], storevertexcolor );
}
// create a temporary patch
Bezier.Patch = new scene::SMeshBufferLightMap();
//Loop through the biquadratic patches
for( j = 0; j < biquadHeight; ++j)
{
for( k = 0; k < biquadWidth; ++k)
{
// set up this patch
const s32 inx = j*controlWidth*2 + k*2;
// setup bezier control points for this patch
Bezier.control[0] = controlPoint[ inx + 0];
Bezier.control[1] = controlPoint[ inx + 1];
Bezier.control[2] = controlPoint[ inx + 2];
Bezier.control[3] = controlPoint[ inx + controlWidth + 0 ];
Bezier.control[4] = controlPoint[ inx + controlWidth + 1 ];
Bezier.control[5] = controlPoint[ inx + controlWidth + 2 ];
Bezier.control[6] = controlPoint[ inx + controlWidth * 2 + 0];
Bezier.control[7] = controlPoint[ inx + controlWidth * 2 + 1];
Bezier.control[8] = controlPoint[ inx + controlWidth * 2 + 2];
Bezier.tesselate( patchTesselation );
}
}
// stitch together with existing geometry
// TODO: only border needs to be checked
const u32 bsize = Bezier.Patch->getVertexCount();
const u32 msize = meshBuffer->getVertexCount();
/*
for ( j = 0; j!= bsize; ++j )
{
const core::vector3df &v = Bezier.Patch->Vertices[j].Pos;
for ( k = 0; k!= msize; ++k )
{
const core::vector3df &m = meshBuffer->Vertices[k].Pos;
if ( !v.equals( m, tolerance ) )
continue;
meshBuffer->Vertices[k].Pos = v;
//Bezier.Patch->Vertices[j].Pos = m;
}
}
*/
// add Patch to meshbuffer
meshBuffer->Vertices.reallocate(msize+bsize);
for ( j = 0; j!= bsize; ++j )
{
meshBuffer->Vertices.push_back( Bezier.Patch->Vertices[j] );
}
// add indices to meshbuffer
meshBuffer->Indices.reallocate(meshBuffer->getIndexCount()+Bezier.Patch->getIndexCount());
for ( j = 0; j!= Bezier.Patch->getIndexCount(); ++j )
{
meshBuffer->Indices.push_back( msize + Bezier.Patch->Indices[j] );
}
delete Bezier.Patch;
if ( LoadParam.verbose > 1 )
{
LoadParam.endTime = os::Timer::getRealTime();
snprintf( buf, sizeof ( buf ),
"quake3::createCurvedSurface_bezier needed %04d ms to create bezier patch.(%dx%d)",
LoadParam.endTime - LoadParam.startTime,
biquadWidth,
biquadHeight
);
os::Printer::log(buf, ELL_INFORMATION);
}
}
/*!
Loads entities from file
*/
void CQ3LevelMesh::getConfiguration( io::IReadFile* file )
{
tBSPLump l;
l.offset = file->getPos();
l.length = file->getSize ();
core::array<u8> entity;
entity.set_used( l.length + 2 );
entity[l.length + 1 ] = 0;
file->seek(l.offset);
file->read( entity.pointer(), l.length);
parser_parse( entity.pointer(), l.length, &CQ3LevelMesh::scriptcallback_config );
if ( Entity.size () )
Entity.getLast().name = file->getFileName();
}
//! get's an interface to the entities
tQ3EntityList & CQ3LevelMesh::getEntityList()
{
// Entity.sort();
return Entity;
}
/*!
*/
const IShader * CQ3LevelMesh::getShader(u32 index) const
{
index &= 0xFFFF;
if ( index < Shader.size() )
{
return &Shader[index];
}
return 0;
}
/*!
loads the shader definition
*/
const IShader* CQ3LevelMesh::getShader( const c8 * filename, bool fileNameIsValid )
{
core::stringc searchName ( filename );
IShader search;
search.name = searchName;
search.name.replace( '\\', '/' );
search.name.make_lower();
core::stringc message;
s32 index;
//! is Shader already in cache?
index = Shader.linear_search( search );
if ( index >= 0 )
{
if ( LoadParam.verbose > 1 )
{
message = searchName + " found " + Shader[index].name;
os::Printer::log("quake3:getShader", message.c_str(), ELL_INFORMATION);
}
return &Shader[index];
}
core::string<c16> loadFile;
if ( !fileNameIsValid )
{
// extract the shader name from the last path component in filename
// "scripts/[name].shader"
core::stringc cut( search.name );
s32 end = cut.findLast( '/' );
s32 start = cut.findLast( '/', end - 1 );
loadFile = LoadParam.scriptDir;
loadFile.append( cut.subString( start, end - start ) );
loadFile.append( ".shader" );
}
else
{
loadFile = search.name;
}
// already loaded the file ?
index = ShaderFile.binary_search( loadFile );
if ( index >= 0 )
return 0;
// add file to loaded files
ShaderFile.push_back( loadFile );
if ( !FileSystem->existFile( loadFile.c_str() ) )
{
if ( LoadParam.verbose > 1 )
{
message = loadFile + " for " + searchName + " failed ";
os::Printer::log("quake3:getShader", message.c_str(), ELL_INFORMATION);
}
return 0;
}
if ( LoadParam.verbose )
{
message = loadFile + " for " + searchName;
os::Printer::log("quake3:getShader Load shader", message.c_str(), ELL_INFORMATION);
}
io::IReadFile *file = FileSystem->createAndOpenFile( loadFile.c_str() );
if ( file )
{
getShader ( file );
file->drop ();
}
// search again
index = Shader.linear_search( search );
return index >= 0 ? &Shader[index] : 0;
}
/*!
loads the shader definition
*/
void CQ3LevelMesh::getShader( io::IReadFile* file )
{
if ( 0 == file )
return;
// load script
core::array<u8> script;
const long len = file->getSize();
script.set_used( len + 2 );
file->seek( 0 );
file->read( script.pointer(), len );
script[ len + 1 ] = 0;
// start a parser instance
parser_parse( script.pointer(), len, &CQ3LevelMesh::scriptcallback_shader );
}
//! adding default shaders
void CQ3LevelMesh::InitShader()
{
ReleaseShader();
IShader element;
SVarGroup group;
SVariable variable ( "noshader" );
group.Variable.push_back( variable );
element.VarGroup = new SVarGroupList();
element.VarGroup->VariableGroup.push_back( group );
element.VarGroup->VariableGroup.push_back( SVarGroup() );
element.name = element.VarGroup->VariableGroup[0].Variable[0].name;
element.id = Shader.size();
Shader.push_back( element );
if ( LoadParam.loadAllShaders )
{
io::eFileSystemType current = FileSystem->setFileListSystem ( io::FILESYSTEM_VIRTUAL );
core::string<c16> save = FileSystem->getWorkingDirectory();
core::string<c16> newDir;
newDir = "/";
newDir += LoadParam.scriptDir;
newDir += "/";
FileSystem->changeWorkingDirectoryTo ( newDir.c_str() );
core::stringc s;
io::IFileList *fileList = FileSystem->createFileList ();
for (u32 i=0; i< fileList->getFileCount(); ++i)
{
s = fileList->getFullFileName(i);
if ( s.find ( ".shader" ) >= 0 )
{
if ( 0 == LoadParam.loadSkyShader && s.find ( "sky.shader" ) >= 0 )
{
}
else
{
getShader ( s.c_str () );
}
}
}
fileList->drop ();
FileSystem->changeWorkingDirectoryTo ( save );
FileSystem->setFileListSystem ( current );
}
}
//! script callback for shaders
//! i'm having troubles with the reference counting, during callback.. resorting..
void CQ3LevelMesh::ReleaseShader()
{
for ( u32 i = 0; i!= Shader.size(); ++i )
{
Shader[i].VarGroup->drop();
}
Shader.clear();
ShaderFile.clear();
}
/*!
*/
void CQ3LevelMesh::ReleaseEntity()
{
for ( u32 i = 0; i!= Entity.size(); ++i )
{
Entity[i].VarGroup->drop();
}
Entity.clear();
}
// config in simple (quake3) and advanced style
void CQ3LevelMesh::scriptcallback_config( SVarGroupList *& grouplist, eToken token )
{
IShader element;
if ( token == Q3_TOKEN_END_LIST )
{
if ( 0 == grouplist->VariableGroup[0].Variable.size() )
return;
element.name = grouplist->VariableGroup[0].Variable[0].name;
}
else
{
if ( grouplist->VariableGroup.size() != 2 )
return;
element.name = "configuration";
}
grouplist->grab();
element.VarGroup = grouplist;
element.id = Entity.size();
Entity.push_back( element );
}
// entity only has only one valid level.. and no assoziative name..
void CQ3LevelMesh::scriptcallback_entity( SVarGroupList *& grouplist, eToken token )
{
if ( token != Q3_TOKEN_END_LIST || grouplist->VariableGroup.size() != 2 )
return;
grouplist->grab();
IEntity element;
element.VarGroup = grouplist;
element.id = Entity.size();
element.name = grouplist->VariableGroup[1].get( "classname" );
Entity.push_back( element );
}
//!. script callback for shaders
void CQ3LevelMesh::scriptcallback_shader( SVarGroupList *& grouplist,eToken token )
{
if ( token != Q3_TOKEN_END_LIST || grouplist->VariableGroup[0].Variable.size()==0)
return;
IShader element;
grouplist->grab();
element.VarGroup = grouplist;
element.name = element.VarGroup->VariableGroup[0].Variable[0].name;
element.id = Shader.size();
/*
core::stringc s;
dumpShader ( s, &element );
printf ( s.c_str () );
*/
Shader.push_back( element );
}
/*!
delete all buffers without geometry in it.
*/
void CQ3LevelMesh::cleanMeshes()
{
if ( 0 == LoadParam.cleanUnResolvedMeshes )
return;
// delete all buffers without geometry in it.
u32 run = 0;
u32 remove = 0;
irr::scene::SMesh *m;
IMeshBuffer *b;
for ( u32 g = 0; g < E_Q3_MESH_SIZE; ++g )
{
bool texture0important = ( g == 0 );
run = 0;
remove = 0;
m = Mesh[g];
if ( LoadParam.verbose > 0 )
{
LoadParam.startTime = os::Timer::getRealTime();
if ( LoadParam.verbose > 1 )
{
snprintf( buf, sizeof ( buf ),
"quake3::cleanMeshes%d start for %d meshes",
g,
m->MeshBuffers.size()
);
os::Printer::log(buf, ELL_INFORMATION);
}
}
u32 i = 0;
s32 blockstart = -1;
s32 blockcount;
while( i < m->MeshBuffers.size())
{
run += 1;
b = m->MeshBuffers[i];
if ( b->getVertexCount() == 0 || b->getIndexCount() == 0 ||
( texture0important && b->getMaterial().getTexture(0) == 0 )
)
{
if ( blockstart < 0 )
{
blockstart = i;
blockcount = 0;
}
blockcount += 1;
i += 1;
// delete Meshbuffer
i -= 1;
remove += 1;
b->drop();
m->MeshBuffers.erase(i);
}
else
{
// clean blockwise
if ( blockstart >= 0 )
{
if ( LoadParam.verbose > 1 )
{
snprintf( buf, sizeof ( buf ),
"quake3::cleanMeshes%d cleaning mesh %d %d size",
g,
blockstart,
blockcount
);
os::Printer::log(buf, ELL_INFORMATION);
}
blockstart = -1;
}
i += 1;
}
}
if ( LoadParam.verbose > 0 )
{
LoadParam.endTime = os::Timer::getRealTime();
snprintf( buf, sizeof ( buf ),
"quake3::cleanMeshes%d needed %04d ms to clean %d of %d meshes",
g,
LoadParam.endTime - LoadParam.startTime,
remove,
run
);
os::Printer::log(buf, ELL_INFORMATION);
}
}
}
// recalculate bounding boxes
void CQ3LevelMesh::calcBoundingBoxes()
{
if ( LoadParam.verbose > 0 )
{
LoadParam.startTime = os::Timer::getRealTime();
if ( LoadParam.verbose > 1 )
{
snprintf( buf, sizeof ( buf ),
"quake3::calcBoundingBoxes start create %d textures and %d lightmaps",
NumTextures,
NumLightMaps
);
os::Printer::log(buf, ELL_INFORMATION);
}
}
// create bounding box
for ( u32 g = 0; g != E_Q3_MESH_SIZE; ++g )
{
for ( u32 j=0; j < Mesh[g]->MeshBuffers.size(); ++j)
{
((SMeshBufferLightMap*)Mesh[g]->MeshBuffers[j])->recalculateBoundingBox();
}
Mesh[g]->recalculateBoundingBox();
}
if ( LoadParam.verbose > 0 )
{
LoadParam.endTime = os::Timer::getRealTime();
snprintf( buf, sizeof ( buf ),
"quake3::calcBoundingBoxes needed %04d ms to create %d textures and %d lightmaps",
LoadParam.endTime - LoadParam.startTime,
NumTextures,
NumLightMaps
);
os::Printer::log( buf, ELL_INFORMATION);
}
}
//! loads the textures
void CQ3LevelMesh::loadTextures()
{
if (!Driver)
return;
if ( LoadParam.verbose > 0 )
{
LoadParam.startTime = os::Timer::getRealTime();
if ( LoadParam.verbose > 1 )
{
snprintf( buf, sizeof ( buf ),
"quake3::loadTextures start create %d textures and %d lightmaps",
NumTextures,
NumLightMaps
);
os::Printer::log( buf, ELL_INFORMATION);
}
}
c8 lightmapname[255];
s32 t;
// load lightmaps.
Lightmap.set_used(NumLightMaps+1);
/*
bool oldMipMapState = Driver->getTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS);
Driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);
*/
core::dimension2d<u32> lmapsize(128,128);
video::IImage* lmapImg;
for ( t = 0; t < NumLightMaps ; ++t)
{
sprintf(lightmapname, "%s.lightmap.%d", LevelName.c_str(), t);
// lightmap is a CTexture::R8G8B8 format
lmapImg = Driver->createImageFromData(
video::ECF_R8G8B8,
lmapsize,
LightMaps[t].imageBits, false, true );
Lightmap[t] = Driver->addTexture( lightmapname, lmapImg );
lmapImg->drop();
}
// Driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, oldMipMapState);
// load textures
Tex.set_used( NumTextures+1 );
const IShader * shader;
core::stringc list;
core::stringc check;
tTexArray textureArray;
// pre-load shaders
for ( t=0; t< NumTextures; ++t)
{
shader = getShader(Textures[t].strName, false);
}
for ( t=0; t< NumTextures; ++t)
{
Tex[t].ShaderID = -1;
Tex[t].Texture = 0;
list = "";
// get a shader ( if one exists )
shader = getShader( Textures[t].strName, false);
if ( shader )
{
Tex[t].ShaderID = shader->id;
// if texture name == stage1 Texture map
const SVarGroup * group;
group = shader->getGroup( 2 );
if ( group )
{
if ( core::cutFilenameExtension( check, group->get( "map" ) ) == Textures[t].strName )
{
list += check;
}
else
if ( check == "$lightmap" )
{
// we check if lightmap is in stage 1 and texture in stage 2
group = shader->getGroup( 3 );
if ( group )
list += group->get( "map" );
}
}
}
else
{
// no shader, take it
list += Textures[t].strName;
}
u32 pos = 0;
getTextures( textureArray, list, pos, FileSystem, Driver );
Tex[t].Texture = textureArray[0];
}
if ( LoadParam.verbose > 0 )
{
LoadParam.endTime = os::Timer::getRealTime();
snprintf( buf, sizeof ( buf ),
"quake3::loadTextures needed %04d ms to create %d textures and %d lightmaps",
LoadParam.endTime - LoadParam.startTime,
NumTextures,
NumLightMaps
);
os::Printer::log( buf, ELL_INFORMATION);
}
}
//! Returns an axis aligned bounding box of the mesh.
//! \return A bounding box of this mesh is returned.
const core::aabbox3d<f32>& CQ3LevelMesh::getBoundingBox() const
{
return Mesh[0]->getBoundingBox();
}
void CQ3LevelMesh::setBoundingBox( const core::aabbox3df& box)
{
Mesh[0]->setBoundingBox(box); //?
}
//! Returns the type of the animated mesh.
E_ANIMATED_MESH_TYPE CQ3LevelMesh::getMeshType() const
{
return scene::EAMT_BSP;
}
} // end namespace scene
} // end namespace irr
#endif // _IRR_COMPILE_WITH_BSP_LOADER_
// Copyright (C) 2002-2009 Nikolaus Gebhardt
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h
#include "IrrCompileConfig.h"
#ifdef _IRR_COMPILE_WITH_BSP_LOADER_
#include "CQ3LevelMesh.h"
#include "ISceneManager.h"
#include "os.h"
#include "SMeshBufferLightMap.h"
#include "irrString.h"
#include "ILightSceneNode.h"
#include "IQ3Shader.h"
#include "IFileList.h"
//#define TJUNCTION_SOLVER_ROUND
//#define TJUNCTION_SOLVER_0125
namespace irr
{
namespace scene
{
using namespace quake3;
//! constructor
CQ3LevelMesh::CQ3LevelMesh(io::IFileSystem* fs, scene::ISceneManager* smgr,
const Q3LevelLoadParameter &loadParam)
: LoadParam(loadParam), Textures(0), LightMaps(0),
Vertices(0), Faces(0), Planes(0), Nodes(0), Leafs(0), LeafFaces(0),
MeshVerts(0), Brushes(0), FileSystem(fs), SceneManager(smgr)
{
#ifdef _DEBUG
IReferenceCounted::setDebugName("CQ3LevelMesh");
#endif
for ( s32 i = 0; i!= E_Q3_MESH_SIZE; ++i )
{
Mesh[i] = 0;
}
Driver = smgr ? smgr->getVideoDriver() : 0;
if (Driver)
Driver->grab();
if (FileSystem)
FileSystem->grab();
// load default shaders
InitShader();
}
//! destructor
CQ3LevelMesh::~CQ3LevelMesh()
{
cleanLoader ();
if (Driver)
Driver->drop();
if (FileSystem)
FileSystem->drop();
for ( s32 i = 0; i!= E_Q3_MESH_SIZE; ++i )
{
if ( Mesh[i] )
{
Mesh[i]->drop();
Mesh[i] = 0;
}
}
ReleaseShader();
ReleaseEntity();
}
//! loads a level from a .bsp-File. Also tries to load all needed textures. Returns true if successful.
bool CQ3LevelMesh::loadFile(io::IReadFile* file)
{
if (!file)
return false;
LevelName = file->getFileName();
file->read(&header, sizeof(tBSPHeader));
#ifdef __BIG_ENDIAN__
header.strID = os::Byteswap::byteswap(header.strID);
header.version = os::Byteswap::byteswap(header.version);
#endif
if ( (header.strID != 0x50534249 || // IBSP
( header.version != 0x2e // quake3
&& header.version != 0x2f // rtcw
)
)
&&
( header.strID != 0x50534252 || header.version != 1 ) // RBSP, starwars jedi, sof
)
{
os::Printer::log("Could not load .bsp file, unknown header.", file->getFileName(), ELL_ERROR);
return false;
}
#if 0
if ( header.strID == 0x50534252 ) // RBSP Raven
{
LoadParam.swapHeader = 1;
}
#endif
// now read lumps
file->read(&Lumps[0], sizeof(tBSPLump)*kMaxLumps);
s32 i;
if ( LoadParam.swapHeader )
{
for ( i=0; i< kMaxLumps;++i)
{
Lumps[i].offset = os::Byteswap::byteswap(Lumps[i].offset);
Lumps[i].length = os::Byteswap::byteswap(Lumps[i].length);
}
}
for ( i = 0; i!= E_Q3_MESH_SIZE; ++i )
{
Mesh[i] = new SMesh();
}
ReleaseEntity();
// load everything
loadEntities(&Lumps[kEntities], file); // load the entities
loadTextures(&Lumps[kShaders], file); // Load the textures
loadLightmaps(&Lumps[kLightmaps], file); // Load the lightmaps
loadVerts(&Lumps[kVertices], file); // Load the vertices
loadFaces(&Lumps[kFaces], file); // Load the faces
loadPlanes(&Lumps[kPlanes], file); // Load the Planes of the BSP
loadNodes(&Lumps[kNodes], file); // load the Nodes of the BSP
loadLeafs(&Lumps[kLeafs], file); // load the Leafs of the BSP
loadLeafFaces(&Lumps[kLeafFaces], file); // load the Faces of the Leafs of the BSP
loadVisData(&Lumps[kVisData], file); // load the visibility data of the clusters
loadModels(&Lumps[kModels], file); // load the models
loadMeshVerts(&Lumps[kMeshVerts], file); // load the mesh vertices
loadBrushes(&Lumps[kBrushes], file); // load the brushes of the BSP
loadBrushSides(&Lumps[kBrushSides], file); // load the brushsides of the BSP
loadLeafBrushes(&Lumps[kLeafBrushes], file); // load the brushes of the leaf
loadFogs(&Lumps[kFogs], file ); // load the fogs
loadTextures();
constructMesh();
solveTJunction ();
cleanMeshes();
calcBoundingBoxes();
cleanLoader ();
return true;
}
/*!
*/
void CQ3LevelMesh::cleanLoader ()
{
delete [] Textures; Textures = 0;
delete [] LightMaps; LightMaps = 0;
delete [] Vertices; Vertices = 0;
delete [] Faces; Faces = 0;
delete [] Planes; Planes = 0;
delete [] Nodes; Nodes = 0;
delete [] Leafs; Leafs = 0;
delete [] LeafFaces; LeafFaces = 0;
delete [] MeshVerts; MeshVerts = 0;
delete [] Brushes; Brushes = 0;
Lightmap.clear ();
Tex.clear ();
}
//! returns the amount of frames in milliseconds. If the amount is 1, it is a static (=non animated) mesh.
u32 CQ3LevelMesh::getFrameCount() const
{
return 1;
}
//! returns the animated mesh based on a detail level. 0 is the lowest, 255 the highest detail. Note, that some Meshes will ignore the detail level.
IMesh* CQ3LevelMesh::getMesh(s32 frameInMs, s32 detailLevel, s32 startFrameLoop, s32 endFrameLoop)
{
return Mesh[frameInMs];
}
void CQ3LevelMesh::loadTextures(tBSPLump* l, io::IReadFile* file)
{
NumTextures = l->length / sizeof(tBSPTexture);
Textures = new tBSPTexture[NumTextures];
file->seek(l->offset);
file->read(Textures, l->length);
if ( LoadParam.swapHeader )
{
for (s32 i=0;i<NumTextures;++i)
{
Textures[i].flags = os::Byteswap::byteswap(Textures[i].flags);
Textures[i].contents = os::Byteswap::byteswap(Textures[i].contents);
//os::Printer::log("Loaded texture", Textures[i].strName, ELL_INFORMATION);
}
}
}
void CQ3LevelMesh::loadLightmaps(tBSPLump* l, io::IReadFile* file)
{
NumLightMaps = l->length / sizeof(tBSPLightmap);
LightMaps = new tBSPLightmap[NumLightMaps];
file->seek(l->offset);
file->read(LightMaps, l->length);
}
/*!
*/
void CQ3LevelMesh::loadVerts(tBSPLump* l, io::IReadFile* file)
{
NumVertices = l->length / sizeof(tBSPVertex);
Vertices = new tBSPVertex[NumVertices];
file->seek(l->offset);
file->read(Vertices, l->length);
if ( LoadParam.swapHeader )
for (s32 i=0;i<NumVertices;i++)
{
Vertices[i].vPosition[0] = os::Byteswap::byteswap(Vertices[i].vPosition[0]);
Vertices[i].vPosition[1] = os::Byteswap::byteswap(Vertices[i].vPosition[1]);
Vertices[i].vPosition[2] = os::Byteswap::byteswap(Vertices[i].vPosition[2]);
Vertices[i].vTextureCoord[0] = os::Byteswap::byteswap(Vertices[i].vTextureCoord[0]);
Vertices[i].vTextureCoord[1] = os::Byteswap::byteswap(Vertices[i].vTextureCoord[1]);
Vertices[i].vLightmapCoord[0] = os::Byteswap::byteswap(Vertices[i].vLightmapCoord[0]);
Vertices[i].vLightmapCoord[1] = os::Byteswap::byteswap(Vertices[i].vLightmapCoord[1]);
Vertices[i].vNormal[0] = os::Byteswap::byteswap(Vertices[i].vNormal[0]);
Vertices[i].vNormal[1] = os::Byteswap::byteswap(Vertices[i].vNormal[1]);
Vertices[i].vNormal[2] = os::Byteswap::byteswap(Vertices[i].vNormal[2]);
}
}
/*!
*/
void CQ3LevelMesh::loadFaces(tBSPLump* l, io::IReadFile* file)
{
NumFaces = l->length / sizeof(tBSPFace);
Faces = new tBSPFace[NumFaces];
file->seek(l->offset);
file->read(Faces, l->length);
if ( LoadParam.swapHeader )
{
for ( s32 i=0;i<NumFaces;i++)
{
Faces[i].textureID = os::Byteswap::byteswap(Faces[i].textureID);
Faces[i].fogNum = os::Byteswap::byteswap(Faces[i].fogNum);
Faces[i].type = os::Byteswap::byteswap(Faces[i].type);
Faces[i].vertexIndex = os::Byteswap::byteswap(Faces[i].vertexIndex);
Faces[i].numOfVerts = os::Byteswap::byteswap(Faces[i].numOfVerts);
Faces[i].meshVertIndex = os::Byteswap::byteswap(Faces[i].meshVertIndex);
Faces[i].numMeshVerts = os::Byteswap::byteswap(Faces[i].numMeshVerts);
Faces[i].lightmapID = os::Byteswap::byteswap(Faces[i].lightmapID);
Faces[i].lMapCorner[0] = os::Byteswap::byteswap(Faces[i].lMapCorner[0]);
Faces[i].lMapCorner[1] = os::Byteswap::byteswap(Faces[i].lMapCorner[1]);
Faces[i].lMapSize[0] = os::Byteswap::byteswap(Faces[i].lMapSize[0]);
Faces[i].lMapSize[1] = os::Byteswap::byteswap(Faces[i].lMapSize[1]);
Faces[i].lMapPos[0] = os::Byteswap::byteswap(Faces[i].lMapPos[0]);
Faces[i].lMapPos[1] = os::Byteswap::byteswap(Faces[i].lMapPos[1]);
Faces[i].lMapPos[2] = os::Byteswap::byteswap(Faces[i].lMapPos[2]);
Faces[i].lMapBitsets[0][0] = os::Byteswap::byteswap(Faces[i].lMapBitsets[0][0]);
Faces[i].lMapBitsets[0][1] = os::Byteswap::byteswap(Faces[i].lMapBitsets[0][1]);
Faces[i].lMapBitsets[0][2] = os::Byteswap::byteswap(Faces[i].lMapBitsets[0][2]);
Faces[i].lMapBitsets[1][0] = os::Byteswap::byteswap(Faces[i].lMapBitsets[1][0]);
Faces[i].lMapBitsets[1][1] = os::Byteswap::byteswap(Faces[i].lMapBitsets[1][1]);
Faces[i].lMapBitsets[1][2] = os::Byteswap::byteswap(Faces[i].lMapBitsets[1][2]);
Faces[i].vNormal[0] = os::Byteswap::byteswap(Faces[i].vNormal[0]);
Faces[i].vNormal[1] = os::Byteswap::byteswap(Faces[i].vNormal[1]);
Faces[i].vNormal[2] = os::Byteswap::byteswap(Faces[i].vNormal[2]);
Faces[i].size[0] = os::Byteswap::byteswap(Faces[i].size[0]);
Faces[i].size[1] = os::Byteswap::byteswap(Faces[i].size[1]);
}
}
}
/*!
*/
void CQ3LevelMesh::loadPlanes(tBSPLump* l, io::IReadFile* file)
{
// ignore
}
/*!
*/
void CQ3LevelMesh::loadNodes(tBSPLump* l, io::IReadFile* file)
{
// ignore
}
/*!
*/
void CQ3LevelMesh::loadLeafs(tBSPLump* l, io::IReadFile* file)
{
// ignore
}
/*!
*/
void CQ3LevelMesh::loadLeafFaces(tBSPLump* l, io::IReadFile* file)
{
// ignore
}
/*!
*/
void CQ3LevelMesh::loadVisData(tBSPLump* l, io::IReadFile* file)
{
// ignore
}
/*!
*/
void CQ3LevelMesh::loadEntities(tBSPLump* l, io::IReadFile* file)
{
core::array<u8> entity;
entity.set_used( l->length + 2 );
entity[l->length + 1 ] = 0;
file->seek(l->offset);
file->read( entity.pointer(), l->length);
parser_parse( entity.pointer(), l->length, &CQ3LevelMesh::scriptcallback_entity );
}
/*!
load fog brushes
*/
void CQ3LevelMesh::loadFogs(tBSPLump* l, io::IReadFile* file)
{
u32 files = l->length / sizeof(tBSPFog);
file->seek( l->offset );
tBSPFog fog;
const IShader *shader;
STexShader t;
for ( u32 i = 0; i!= files; ++i )
{
file->read( &fog, sizeof( fog ) );
shader = getShader( fog.shader );
t.Texture = 0;
t.ShaderID = shader ? shader->ID : -1;
FogMap.push_back ( t );
}
}
/*!
load models named in bsp
*/
void CQ3LevelMesh::loadModels(tBSPLump* l, io::IReadFile* file)
{
u32 files = l->length / sizeof(tBSPModel);
file->seek( l->offset );
tBSPModel def;
for ( u32 i = 0; i!= files; ++i )
{
file->read( &def, sizeof( def ) );
}
}
/*!
*/
void CQ3LevelMesh::loadMeshVerts(tBSPLump* l, io::IReadFile* file)
{
NumMeshVerts = l->length / sizeof(s32);
MeshVerts = new s32[NumMeshVerts];
file->seek(l->offset);
file->read(MeshVerts, l->length);
if ( LoadParam.swapHeader )
{
for (int i=0;i<NumMeshVerts;i++)
MeshVerts[i] = os::Byteswap::byteswap(MeshVerts[i]);
}
}
/*!
*/
void CQ3LevelMesh::loadBrushes(tBSPLump* l, io::IReadFile* file)
{
// ignore
}
/*!
*/
void CQ3LevelMesh::loadBrushSides(tBSPLump* l, io::IReadFile* file)
{
// ignore
}
/*!
*/
void CQ3LevelMesh::loadLeafBrushes(tBSPLump* l, io::IReadFile* file)
{
// ignore
}
/*!
*/
inline bool isQ3WhiteSpace( const u8 symbol )
{
return symbol == ' ' || symbol == '\t' || symbol == '\r';
}
/*!
*/
inline bool isQ3ValidName( const u8 symbol )
{
return (symbol >= 'a' && symbol <= 'z' ) ||
(symbol >= 'A' && symbol <= 'Z' ) ||
(symbol >= '0' && symbol <= '9' ) ||
(symbol == '/' || symbol == '_' || symbol == '.' );
}
/*!
*/
void CQ3LevelMesh::parser_nextToken()
{
u8 symbol;
Parser.token = "";
Parser.tokenresult = Q3_TOKEN_UNRESOLVED;
// skip white space
do
{
if ( Parser.index >= Parser.sourcesize )
{
Parser.tokenresult = Q3_TOKEN_EOF;
return;
}
symbol = Parser.source [ Parser.index ];
Parser.index += 1;
} while ( isQ3WhiteSpace( symbol ) );
// first symbol, one symbol
switch ( symbol )
{
case 0:
Parser.tokenresult = Q3_TOKEN_EOF;
return;
case '/':
// comment or divide
if ( Parser.index >= Parser.sourcesize )
{
Parser.tokenresult = Q3_TOKEN_EOF;
return;
}
symbol = Parser.source [ Parser.index ];
Parser.index += 1;
if ( isQ3WhiteSpace( symbol ) )
{
Parser.tokenresult = Q3_TOKEN_MATH_DIVIDE;
return;
}
else
if ( symbol == '*' )
{
// C-style comment in quake?
}
else
if ( symbol == '/' )
{
// skip to eol
do
{
if ( Parser.index >= Parser.sourcesize )
{
Parser.tokenresult = Q3_TOKEN_EOF;
return;
}
symbol = Parser.source [ Parser.index ];
Parser.index += 1;
} while ( symbol != '\n' );
Parser.tokenresult = Q3_TOKEN_COMMENT;
return;
}
// take /[name] as valid token..?!?!?. mhmm, maybe
break;
case '\n':
Parser.tokenresult = Q3_TOKEN_EOL;
return;
case '{':
Parser.tokenresult = Q3_TOKEN_START_LIST;
return;
case '}':
Parser.tokenresult = Q3_TOKEN_END_LIST;
return;
case '"':
// string literal
do
{
if ( Parser.index >= Parser.sourcesize )
{
Parser.tokenresult = Q3_TOKEN_EOF;
return;
}
symbol = Parser.source [ Parser.index ];
Parser.index += 1;
if ( symbol != '"' )
Parser.token.append( symbol );
} while ( symbol != '"' );
Parser.tokenresult = Q3_TOKEN_ENTITY;
return;
}
// user identity
Parser.token.append( symbol );
// continue till whitespace
bool validName = true;
do
{
if ( Parser.index >= Parser.sourcesize )
{
Parser.tokenresult = Q3_TOKEN_EOF;
return;
}
symbol = Parser.source [ Parser.index ];
validName = isQ3ValidName( symbol );
if ( validName )
{
Parser.token.append( symbol );
Parser.index += 1;
}
} while ( validName );
Parser.tokenresult = Q3_TOKEN_TOKEN;
return;
}
/*
parse entity & shader
calls callback on content in {}
*/
void CQ3LevelMesh::parser_parse( const void * data, const u32 size, CQ3LevelMesh::tParserCallback callback )
{
Parser.source = static_cast<const c8*>(data);
Parser.sourcesize = size;
Parser.index = 0;
SVarGroupList *groupList;
s32 active;
s32 last;
SVariable entity ( "" );
groupList = new SVarGroupList();
groupList->VariableGroup.push_back( SVarGroup() );
active = last = 0;
do
{
parser_nextToken();
switch ( Parser.tokenresult )
{
case Q3_TOKEN_START_LIST:
{
//stack = core::min_( stack + 1, 7 );
groupList->VariableGroup.push_back( SVarGroup() );
last = active;
active = groupList->VariableGroup.size() - 1;
entity.clear();
} break;
// a unregisterd variable is finished
case Q3_TOKEN_EOL:
{
if ( entity.isValid() )
{
groupList->VariableGroup[active].Variable.push_back( entity );
entity.clear();
}
} break;
case Q3_TOKEN_TOKEN:
case Q3_TOKEN_ENTITY:
{
Parser.token.make_lower();
// store content based on line-delemiter
if ( 0 == entity.isValid() )
{
entity.name = Parser.token;
entity.content = "";
}
else
{
if ( entity.content.size() )
{
entity.content += " ";
}
entity.content += Parser.token;
}
} break;
case Q3_TOKEN_END_LIST:
{
//stack = core::max_( stack - 1, 0 );
// close tag for first
if ( active == 1 )
{
(this->*callback)( groupList, Q3_TOKEN_END_LIST );
// new group
groupList->drop();
groupList = new SVarGroupList();
groupList->VariableGroup.push_back( SVarGroup() );
last = 0;
}
active = last;
entity.clear();
} break;
}
} while ( Parser.tokenresult != Q3_TOKEN_EOF );
(this->*callback)( groupList, Q3_TOKEN_EOF );
groupList->drop();
}
/*
this loader applies only textures for stage 1 & 2
*/
s32 CQ3LevelMesh::setShaderFogMaterial( video::SMaterial &material, const tBSPFace * face ) const
{
material.MaterialType = video::EMT_SOLID;
material.Wireframe = false;
material.Lighting = false;
material.BackfaceCulling = false;
material.setTexture(0, 0);
material.setTexture(1, 0);
material.setTexture(2, 0);
material.setTexture(3, 0);
material.ZBuffer = video::ECFN_LESSEQUAL;
material.ZWriteEnable = false;
material.MaterialTypeParam = 0.f;
s32 shaderState = -1;
if ( (u32) face->fogNum < FogMap.size() )
{
material.setTexture(0, FogMap [ face->fogNum ].Texture);
shaderState = FogMap [ face->fogNum ].ShaderID;
}
return shaderState;
}
/*
this loader applies only textures for stage 1 & 2
*/
s32 CQ3LevelMesh::setShaderMaterial( video::SMaterial &material, const tBSPFace * face ) const
{
material.MaterialType = video::EMT_SOLID;
material.Wireframe = false;
material.Lighting = false;
material.BackfaceCulling = true;
material.setTexture(0, 0);
material.setTexture(1, 0);
material.setTexture(2, 0);
material.setTexture(3, 0);
material.ZBuffer = video::ECFN_LESSEQUAL;
material.ZWriteEnable = true;
material.MaterialTypeParam = 0.f;
s32 shaderState = -1;
if ( face->textureID >= 0 )
{
material.setTexture(0, Tex [ face->textureID ].Texture);
shaderState = Tex [ face->textureID ].ShaderID;
}
if ( face->lightmapID >= 0 )
{
material.setTexture(1, Lightmap [ face->lightmapID ]);
material.MaterialType = LoadParam.defaultLightMapMaterial;
}
// store shader ID
material.MaterialTypeParam2 = (f32) shaderState;
const IShader *shader = getShader(shaderState);
if ( 0 == shader )
return shaderState;
return shaderState;
#if 0
const SVarGroup *group;
// generic
group = shader->getGroup( 1 );
if ( group )
{
material.BackfaceCulling = getCullingFunction( group->get( "cull" ) );
if ( group->isDefined( "surfaceparm", "nolightmap" ) )
{
material.MaterialType = video::EMT_SOLID;
material.setTexture(1, 0);
}
}
// try to get the best of the 8 texture stages..
// texture 1, texture 2
u32 startPos;
for ( s32 g = 2; g <= 3; ++g )
{
group = shader->getGroup( g );
if ( 0 == group )
continue;
startPos = 0;
if ( group->isDefined( "depthwrite" ) )
{
material.ZWriteEnable = true;
}
SBlendFunc blendfunc ( LoadParam.defaultModulate );
getBlendFunc( group->get( "blendfunc" ), blendfunc );
getBlendFunc( group->get( "alphafunc" ), blendfunc );
if ( 0 == LoadParam.alpharef &&
( blendfunc.type == video::EMT_TRANSPARENT_ALPHA_CHANNEL ||
blendfunc.type == video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF
)
)
{
blendfunc.type = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
blendfunc.param0 = 0.f;
}
material.MaterialType = blendfunc.type;
material.MaterialTypeParam = blendfunc.param0;
// try if we can match better
shaderState |= (material.MaterialType == video::EMT_SOLID ) ? 0x00020000 : 0;
}
//material.BackfaceCulling = false;
if ( shader->VarGroup->VariableGroup.size() <= 4 )
{
shaderState |= 0x00010000;
}
material.MaterialTypeParam2 = (f32) shaderState;
return shaderState;
#endif
}
/*!
*/
void CQ3LevelMesh::solveTJunction()
{
}
/*!
constructs a mesh from the quake 3 level file.
*/
void CQ3LevelMesh::constructMesh()
{
if ( LoadParam.verbose > 0 )
{
LoadParam.startTime = os::Timer::getRealTime();
if ( LoadParam.verbose > 1 )
{
snprintf( buf, sizeof ( buf ),
"quake3::constructMesh start to create %d faces, %d vertices,%d mesh vertices",
NumFaces,
NumVertices,
NumMeshVerts
);
os::Printer::log(buf, ELL_INFORMATION);
}
}
s32 i, j, k,s;
s32 *index;
video::S3DVertex2TCoords temp[3];
video::SMaterial material;
video::SMaterial material2;
SToBuffer item [ E_Q3_MESH_SIZE ];
u32 itemSize;
for ( i=0; i < NumFaces; ++i)
{
const tBSPFace * face = Faces + i;
s32 shaderState = setShaderMaterial( material, face );
itemSize = 0;
const IShader *shader = getShader(shaderState);
if ( face->fogNum >= 0 )
{
setShaderFogMaterial ( material2, face );
item[itemSize].index = E_Q3_MESH_FOG;
item[itemSize].takeVertexColor = 1;
itemSize += 1;
}
switch( face->type )
{
case 1: // normal polygons
case 2: // patches
case 3: // meshes
{
if ( 0 == shader )
{
if ( LoadParam.cleanUnResolvedMeshes || material.getTexture(0) )
{
item[itemSize].takeVertexColor = 1;
item[itemSize].index = E_Q3_MESH_GEOMETRY;
itemSize += 1;
}
else
{
item[itemSize].takeVertexColor = 1;
item[itemSize].index = E_Q3_MESH_UNRESOLVED;
itemSize += 1;
}
}
else
{
item[itemSize].takeVertexColor = 1;
item[itemSize].index = E_Q3_MESH_ITEMS;
itemSize += 1;
}
} break;
case 4: // billboards
//item[itemSize].takeVertexColor = 1;
//item[itemSize].index = E_Q3_MESH_ITEMS;
//itemSize += 1;
break;
}
for ( u32 g = 0; g != itemSize; ++g )
{
scene::SMeshBufferLightMap* buffer = 0;
if ( item[g].index == E_Q3_MESH_GEOMETRY )
{
if ( 0 == item[g].takeVertexColor )
{
item[g].takeVertexColor = material.getTexture(0) == 0 || material.getTexture(1) == 0;
}
if (Faces[i].lightmapID < -1 || Faces[i].lightmapID > NumLightMaps-1)
{
Faces[i].lightmapID = -1;
}
#if 0
// there are lightmapsids and textureid with -1
const s32 tmp_index = ((Faces[i].lightmapID+1) * (NumTextures+1)) + (Faces[i].textureID+1);
buffer = (SMeshBufferLightMap*) Mesh[E_Q3_MESH_GEOMETRY]->getMeshBuffer(tmp_index);
buffer->setHardwareMappingHint ( EHM_STATIC );
buffer->getMaterial() = material;
#endif
}
// Construct a unique mesh for each shader or combine meshbuffers for same shader
if ( 0 == buffer )
{
if ( LoadParam.mergeShaderBuffer == 1 )
{
// combine
buffer = (SMeshBufferLightMap*) Mesh[ item[g].index ]->getMeshBuffer(
item[g].index != E_Q3_MESH_FOG ? material : material2 );
}
// create a seperate mesh buffer
if ( 0 == buffer )
{
buffer = new scene::SMeshBufferLightMap();
Mesh[ item[g].index ]->addMeshBuffer( buffer );
buffer->drop();
buffer->getMaterial() = item[g].index != E_Q3_MESH_FOG ? material : material2;
if ( item[g].index == E_Q3_MESH_GEOMETRY )
buffer->setHardwareMappingHint ( EHM_STATIC );
}
}
switch(Faces[i].type)
{
case 4: // billboards
break;
case 2: // patches
createCurvedSurface_bezier( buffer, i,
LoadParam.patchTesselation,
item[g].takeVertexColor
);
break;
case 1: // normal polygons
case 3: // mesh vertices
index = MeshVerts + face->meshVertIndex;
k = buffer->getVertexCount();
// reallocate better if many small meshes are used
s = buffer->getIndexCount()+face->numMeshVerts;
if ( buffer->Indices.allocated_size () < (u32) s )
{
if ( buffer->Indices.allocated_size () > 0 &&
face->numMeshVerts < 20 && NumFaces > 1000
)
{
s = buffer->getIndexCount() + (NumFaces >> 3 * face->numMeshVerts );
}
buffer->Indices.reallocate( s);
}
for ( j = 0; j < face->numMeshVerts; ++j )
{
buffer->Indices.push_back( k + index [j] );
}
s = k+face->numOfVerts;
if ( buffer->Vertices.allocated_size () < (u32) s )
{
if ( buffer->Indices.allocated_size () > 0 &&
face->numOfVerts < 20 && NumFaces > 1000
)
{
s = buffer->getIndexCount() + (NumFaces >> 3 * face->numOfVerts );
}
buffer->Vertices.reallocate( s);
}
for ( j = 0; j != face->numOfVerts; ++j )
{
copy( &temp[0], &Vertices[ j + face->vertexIndex ], item[g].takeVertexColor );
buffer->Vertices.push_back( temp[0] );
}
break;
} // end switch
}
}
if ( LoadParam.verbose > 0 )
{
LoadParam.endTime = os::Timer::getRealTime();
snprintf( buf, sizeof ( buf ),
"quake3::constructMesh needed %04d ms to create %d faces, %d vertices,%d mesh vertices",
LoadParam.endTime - LoadParam.startTime,
NumFaces,
NumVertices,
NumMeshVerts
);
os::Printer::log(buf, ELL_INFORMATION);
}
}
void CQ3LevelMesh::S3DVertex2TCoords_64::copy( video::S3DVertex2TCoords &dest ) const
{
#if defined (TJUNCTION_SOLVER_ROUND)
dest.Pos.X = core::round_( (f32) Pos.X );
dest.Pos.Y = core::round_( (f32) Pos.Y );
dest.Pos.Z = core::round_( (f32) Pos.Z );
#elif defined (TJUNCTION_SOLVER_0125)
dest.Pos.X = (f32) ( floor ( Pos.X * 8.f + 0.5 ) * 0.125 );
dest.Pos.Y = (f32) ( floor ( Pos.Y * 8.f + 0.5 ) * 0.125 );
dest.Pos.Z = (f32) ( floor ( Pos.Z * 8.f + 0.5 ) * 0.125 );
#else
dest.Pos.X = (f32) Pos.X;
dest.Pos.Y = (f32) Pos.Y;
dest.Pos.Z = (f32) Pos.Z;
#endif
dest.Normal.X = (f32) Normal.X;
dest.Normal.Y = (f32) Normal.Y;
dest.Normal.Z = (f32) Normal.Z;
dest.Normal.normalize();
dest.Color = Color.toSColor();
dest.TCoords.X = (f32) TCoords.X;
dest.TCoords.Y = (f32) TCoords.Y;
dest.TCoords2.X = (f32) TCoords2.X;
dest.TCoords2.Y = (f32) TCoords2.Y;
}
void CQ3LevelMesh::copy( S3DVertex2TCoords_64 * dest, const tBSPVertex * source, s32 vertexcolor ) const
{
#if defined (TJUNCTION_SOLVER_ROUND)
dest->Pos.X = core::round_( source->vPosition[0] );
dest->Pos.Y = core::round_( source->vPosition[2] );
dest->Pos.Z = core::round_( source->vPosition[1] );
#elif defined (TJUNCTION_SOLVER_0125)
dest->Pos.X = (f32) ( floor ( source->vPosition[0] * 8.f + 0.5 ) * 0.125 );
dest->Pos.Y = (f32) ( floor ( source->vPosition[2] * 8.f + 0.5 ) * 0.125 );
dest->Pos.Z = (f32) ( floor ( source->vPosition[1] * 8.f + 0.5 ) * 0.125 );
#else
dest->Pos.X = source->vPosition[0];
dest->Pos.Y = source->vPosition[2];
dest->Pos.Z = source->vPosition[1];
#endif
dest->Normal.X = source->vNormal[0];
dest->Normal.Y = source->vNormal[2];
dest->Normal.Z = source->vNormal[1];
dest->Normal.normalize();
dest->TCoords.X = source->vTextureCoord[0];
dest->TCoords.Y = source->vTextureCoord[1];
dest->TCoords2.X = source->vLightmapCoord[0];
dest->TCoords2.Y = source->vLightmapCoord[1];
if ( vertexcolor )
{
//u32 a = core::s32_min( source->color[3] * LoadParam.defaultModulate, 255 );
u32 a = source->color[3];
u32 r = core::s32_min( source->color[0] * LoadParam.defaultModulate, 255 );
u32 g = core::s32_min( source->color[1] * LoadParam.defaultModulate, 255 );
u32 b = core::s32_min( source->color[2] * LoadParam.defaultModulate, 255 );
dest->Color.set(a * 1.f/255.f, r * 1.f/255.f,
g * 1.f/255.f, b * 1.f/255.f);
}
else
{
dest->Color.set( 1.f, 1.f, 1.f, 1.f );
}
}
inline void CQ3LevelMesh::copy( video::S3DVertex2TCoords * dest, const tBSPVertex * source, s32 vertexcolor ) const
{
#if defined (TJUNCTION_SOLVER_ROUND)
dest->Pos.X = core::round_( source->vPosition[0] );
dest->Pos.Y = core::round_( source->vPosition[2] );
dest->Pos.Z = core::round_( source->vPosition[1] );
#elif defined (TJUNCTION_SOLVER_0125)
dest->Pos.X = (f32) ( floor ( source->vPosition[0] * 8.f + 0.5 ) * 0.125 );
dest->Pos.Y = (f32) ( floor ( source->vPosition[2] * 8.f + 0.5 ) * 0.125 );
dest->Pos.Z = (f32) ( floor ( source->vPosition[1] * 8.f + 0.5 ) * 0.125 );
#else
dest->Pos.X = source->vPosition[0];
dest->Pos.Y = source->vPosition[2];
dest->Pos.Z = source->vPosition[1];
#endif
dest->Normal.X = source->vNormal[0];
dest->Normal.Y = source->vNormal[2];
dest->Normal.Z = source->vNormal[1];
dest->Normal.normalize();
dest->TCoords.X = source->vTextureCoord[0];
dest->TCoords.Y = source->vTextureCoord[1];
dest->TCoords2.X = source->vLightmapCoord[0];
dest->TCoords2.Y = source->vLightmapCoord[1];
if ( vertexcolor )
{
//u32 a = core::s32_min( source->color[3] * LoadParam.defaultModulate, 255 );
u32 a = source->color[3];
u32 r = core::s32_min( source->color[0] * LoadParam.defaultModulate, 255 );
u32 g = core::s32_min( source->color[1] * LoadParam.defaultModulate, 255 );
u32 b = core::s32_min( source->color[2] * LoadParam.defaultModulate, 255 );
dest->Color.color = a << 24 | r << 16 | g << 8 | b;
}
else
{
dest->Color.color = 0xFFFFFFFF;
}
}
void CQ3LevelMesh::SBezier::tesselate( s32 level )
{
//Calculate how many vertices across/down there are
s32 j, k;
column[0].set_used( level + 1 );
column[1].set_used( level + 1 );
column[2].set_used( level + 1 );
const f64 w = 0.0 + (1.0 / (f64) level );
//Tesselate along the columns
for( j = 0; j <= level; ++j)
{
const f64 f = w * (f64) j;
column[0][j] = control[0].getInterpolated_quadratic(control[3], control[6], f );
column[1][j] = control[1].getInterpolated_quadratic(control[4], control[7], f );
column[2][j] = control[2].getInterpolated_quadratic(control[5], control[8], f );
}
const u32 idx = Patch->Vertices.size();
Patch->Vertices.reallocate(idx+level*level);
//Tesselate across the rows to get final vertices
video::S3DVertex2TCoords v;
S3DVertex2TCoords_64 f;
for( j = 0; j <= level; ++j)
{
for( k = 0; k <= level; ++k)
{
f = column[0][j].getInterpolated_quadratic(column[1][j], column[2][j], w * (f64) k);
f.copy( v );
Patch->Vertices.push_back( v );
}
}
Patch->Indices.reallocate(Patch->Indices.size()+6*level*level);
// connect
for( j = 0; j < level; ++j)
{
for( k = 0; k < level; ++k)
{
const s32 inx = idx + ( k * ( level + 1 ) ) + j;
Patch->Indices.push_back( inx + 0 );
Patch->Indices.push_back( inx + (level + 1 ) + 0 );
Patch->Indices.push_back( inx + (level + 1 ) + 1 );
Patch->Indices.push_back( inx + 0 );
Patch->Indices.push_back( inx + (level + 1 ) + 1 );
Patch->Indices.push_back( inx + 1 );
}
}
}
/*!
no subdivision
*/
void CQ3LevelMesh::createCurvedSurface_nosubdivision(SMeshBufferLightMap* meshBuffer,
s32 faceIndex,
s32 patchTesselation,
s32 storevertexcolor)
{
tBSPFace * face = &Faces[faceIndex];
u32 j,k,m;
// number of control points across & up
const u32 controlWidth = face->size[0];
const u32 controlHeight = face->size[1];
if ( 0 == controlWidth || 0 == controlHeight )
return;
video::S3DVertex2TCoords v;
m = meshBuffer->Vertices.size();
meshBuffer->Vertices.reallocate(m+controlHeight * controlWidth);
for ( j = 0; j!= controlHeight * controlWidth; ++j )
{
copy( &v, &Vertices [ face->vertexIndex + j ], storevertexcolor );
meshBuffer->Vertices.push_back( v );
}
meshBuffer->Indices.reallocate(meshBuffer->Indices.size()+6*(controlHeight-1) * (controlWidth-1));
for ( j = 0; j!= controlHeight - 1; ++j )
{
for ( k = 0; k!= controlWidth - 1; ++k )
{
meshBuffer->Indices.push_back( m + k + 0 );
meshBuffer->Indices.push_back( m + k + controlWidth + 0 );
meshBuffer->Indices.push_back( m + k + controlWidth + 1 );
meshBuffer->Indices.push_back( m + k + 0 );
meshBuffer->Indices.push_back( m + k + controlWidth + 1 );
meshBuffer->Indices.push_back( m + k + 1 );
}
m += controlWidth;
}
}
/*!
*/
void CQ3LevelMesh::createCurvedSurface_bezier(SMeshBufferLightMap* meshBuffer,
s32 faceIndex,
s32 patchTesselation,
s32 storevertexcolor)
{
tBSPFace * face = &Faces[faceIndex];
u32 j,k;
// number of control points across & up
const u32 controlWidth = face->size[0];
const u32 controlHeight = face->size[1];
if ( 0 == controlWidth || 0 == controlHeight )
return;
// number of biquadratic patches
const u32 biquadWidth = (controlWidth - 1)/2;
const u32 biquadHeight = (controlHeight -1)/2;
if ( LoadParam.verbose > 1 )
{
LoadParam.startTime = os::Timer::getRealTime();
}
// Create space for a temporary array of the patch's control points
core::array<S3DVertex2TCoords_64> controlPoint;
controlPoint.set_used( controlWidth * controlHeight );
for( j = 0; j < controlPoint.size(); ++j)
{
copy( &controlPoint[j], &Vertices [ face->vertexIndex + j ], storevertexcolor );
}
// create a temporary patch
Bezier.Patch = new scene::SMeshBufferLightMap();
//Loop through the biquadratic patches
for( j = 0; j < biquadHeight; ++j)
{
for( k = 0; k < biquadWidth; ++k)
{
// set up this patch
const s32 inx = j*controlWidth*2 + k*2;
// setup bezier control points for this patch
Bezier.control[0] = controlPoint[ inx + 0];
Bezier.control[1] = controlPoint[ inx + 1];
Bezier.control[2] = controlPoint[ inx + 2];
Bezier.control[3] = controlPoint[ inx + controlWidth + 0 ];
Bezier.control[4] = controlPoint[ inx + controlWidth + 1 ];
Bezier.control[5] = controlPoint[ inx + controlWidth + 2 ];
Bezier.control[6] = controlPoint[ inx + controlWidth * 2 + 0];
Bezier.control[7] = controlPoint[ inx + controlWidth * 2 + 1];
Bezier.control[8] = controlPoint[ inx + controlWidth * 2 + 2];
Bezier.tesselate( patchTesselation );
}
}
// stitch together with existing geometry
// TODO: only border needs to be checked
const u32 bsize = Bezier.Patch->getVertexCount();
const u32 msize = meshBuffer->getVertexCount();
/*
for ( j = 0; j!= bsize; ++j )
{
const core::vector3df &v = Bezier.Patch->Vertices[j].Pos;
for ( k = 0; k!= msize; ++k )
{
const core::vector3df &m = meshBuffer->Vertices[k].Pos;
if ( !v.equals( m, tolerance ) )
continue;
meshBuffer->Vertices[k].Pos = v;
//Bezier.Patch->Vertices[j].Pos = m;
}
}
*/
// add Patch to meshbuffer
meshBuffer->Vertices.reallocate(msize+bsize);
for ( j = 0; j!= bsize; ++j )
{
meshBuffer->Vertices.push_back( Bezier.Patch->Vertices[j] );
}
// add indices to meshbuffer
meshBuffer->Indices.reallocate(meshBuffer->getIndexCount()+Bezier.Patch->getIndexCount());
for ( j = 0; j!= Bezier.Patch->getIndexCount(); ++j )
{
meshBuffer->Indices.push_back( msize + Bezier.Patch->Indices[j] );
}
delete Bezier.Patch;
if ( LoadParam.verbose > 1 )
{
LoadParam.endTime = os::Timer::getRealTime();
snprintf( buf, sizeof ( buf ),
"quake3::createCurvedSurface_bezier needed %04d ms to create bezier patch.(%dx%d)",
LoadParam.endTime - LoadParam.startTime,
biquadWidth,
biquadHeight
);
os::Printer::log(buf, ELL_INFORMATION);
}
}
/*!
Loads entities from file
*/
void CQ3LevelMesh::getConfiguration( io::IReadFile* file )
{
tBSPLump l;
l.offset = file->getPos();
l.length = file->getSize ();
core::array<u8> entity;
entity.set_used( l.length + 2 );
entity[l.length + 1 ] = 0;
file->seek(l.offset);
file->read( entity.pointer(), l.length);
parser_parse( entity.pointer(), l.length, &CQ3LevelMesh::scriptcallback_config );
if ( Entity.size () )
Entity.getLast().name = file->getFileName();
}
//! get's an interface to the entities
tQ3EntityList & CQ3LevelMesh::getEntityList()
{
// Entity.sort();
return Entity;
}
/*!
*/
const IShader * CQ3LevelMesh::getShader(u32 index) const
{
index &= 0xFFFF;
if ( index < Shader.size() )
{
return &Shader[index];
}
return 0;
}
/*!
loads the shader definition
*/
const IShader* CQ3LevelMesh::getShader( const c8 * filename, bool fileNameIsValid )
{
core::stringc searchName ( filename );
IShader search;
search.name = searchName;
search.name.replace( '\\', '/' );
search.name.make_lower();
core::stringc message;
s32 index;
//! is Shader already in cache?
index = Shader.linear_search( search );
if ( index >= 0 )
{
if ( LoadParam.verbose > 1 )
{
message = searchName + " found " + Shader[index].name;
os::Printer::log("quake3:getShader", message.c_str(), ELL_INFORMATION);
}
return &Shader[index];
}
core::string<c16> loadFile;
if ( !fileNameIsValid )
{
// extract the shader name from the last path component in filename
// "scripts/[name].shader"
core::stringc cut( search.name );
s32 end = cut.findLast( '/' );
s32 start = cut.findLast( '/', end - 1 );
loadFile = LoadParam.scriptDir;
loadFile.append( cut.subString( start, end - start ) );
loadFile.append( ".shader" );
}
else
{
loadFile = search.name;
}
// already loaded the file ?
index = ShaderFile.binary_search( loadFile );
if ( index >= 0 )
return 0;
// add file to loaded files
ShaderFile.push_back( loadFile );
if ( !FileSystem->existFile( loadFile.c_str() ) )
{
if ( LoadParam.verbose > 1 )
{
message = loadFile + " for " + searchName + " failed ";
os::Printer::log("quake3:getShader", message.c_str(), ELL_INFORMATION);
}
return 0;
}
if ( LoadParam.verbose )
{
message = loadFile + " for " + searchName;
os::Printer::log("quake3:getShader Load shader", message.c_str(), ELL_INFORMATION);
}
io::IReadFile *file = FileSystem->createAndOpenFile( loadFile.c_str() );
if ( file )
{
getShader ( file );
file->drop ();
}
// search again
index = Shader.linear_search( search );
return index >= 0 ? &Shader[index] : 0;
}
/*!
loads the shader definition
*/
void CQ3LevelMesh::getShader( io::IReadFile* file )
{
if ( 0 == file )
return;
// load script
core::array<u8> script;
const long len = file->getSize();
script.set_used( len + 2 );
file->seek( 0 );
file->read( script.pointer(), len );
script[ len + 1 ] = 0;
// start a parser instance
parser_parse( script.pointer(), len, &CQ3LevelMesh::scriptcallback_shader );
}
//! adding default shaders
void CQ3LevelMesh::InitShader()
{
ReleaseShader();
IShader element;
SVarGroup group;
SVariable variable ( "noshader" );
group.Variable.push_back( variable );
element.VarGroup = new SVarGroupList();
element.VarGroup->VariableGroup.push_back( group );
element.VarGroup->VariableGroup.push_back( SVarGroup() );
element.name = element.VarGroup->VariableGroup[0].Variable[0].name;
element.ID = Shader.size();
Shader.push_back( element );
if ( LoadParam.loadAllShaders )
{
io::eFileSystemType current = FileSystem->setFileListSystem ( io::FILESYSTEM_VIRTUAL );
core::string<c16> save = FileSystem->getWorkingDirectory();
core::string<c16> newDir;
newDir = "/";
newDir += LoadParam.scriptDir;
newDir += "/";
FileSystem->changeWorkingDirectoryTo ( newDir.c_str() );
core::stringc s;
io::IFileList *fileList = FileSystem->createFileList ();
for (u32 i=0; i< fileList->getFileCount(); ++i)
{
s = fileList->getFullFileName(i);
if ( s.find ( ".shader" ) >= 0 )
{
if ( 0 == LoadParam.loadSkyShader && s.find ( "sky.shader" ) >= 0 )
{
}
else
{
getShader ( s.c_str () );
}
}
}
fileList->drop ();
FileSystem->changeWorkingDirectoryTo ( save );
FileSystem->setFileListSystem ( current );
}
}
//! script callback for shaders
//! i'm having troubles with the reference counting, during callback.. resorting..
void CQ3LevelMesh::ReleaseShader()
{
for ( u32 i = 0; i!= Shader.size(); ++i )
{
Shader[i].VarGroup->drop();
}
Shader.clear();
ShaderFile.clear();
}
/*!
*/
void CQ3LevelMesh::ReleaseEntity()
{
for ( u32 i = 0; i!= Entity.size(); ++i )
{
Entity[i].VarGroup->drop();
}
Entity.clear();
}
// config in simple (quake3) and advanced style
void CQ3LevelMesh::scriptcallback_config( SVarGroupList *& grouplist, eToken token )
{
IShader element;
if ( token == Q3_TOKEN_END_LIST )
{
if ( 0 == grouplist->VariableGroup[0].Variable.size() )
return;
element.name = grouplist->VariableGroup[0].Variable[0].name;
}
else
{
if ( grouplist->VariableGroup.size() != 2 )
return;
element.name = "configuration";
}
grouplist->grab();
element.VarGroup = grouplist;
element.ID = Entity.size();
Entity.push_back( element );
}
// entity only has only one valid level.. and no assoziative name..
void CQ3LevelMesh::scriptcallback_entity( SVarGroupList *& grouplist, eToken token )
{
if ( token != Q3_TOKEN_END_LIST || grouplist->VariableGroup.size() != 2 )
return;
grouplist->grab();
IEntity element;
element.VarGroup = grouplist;
element.ID = Entity.size();
element.name = grouplist->VariableGroup[1].get( "classname" );
Entity.push_back( element );
}
//!. script callback for shaders
void CQ3LevelMesh::scriptcallback_shader( SVarGroupList *& grouplist,eToken token )
{
if ( token != Q3_TOKEN_END_LIST || grouplist->VariableGroup[0].Variable.size()==0)
return;
IShader element;
grouplist->grab();
element.VarGroup = grouplist;
element.name = element.VarGroup->VariableGroup[0].Variable[0].name;
element.ID = Shader.size();
/*
core::stringc s;
dumpShader ( s, &element );
printf ( s.c_str () );
*/
Shader.push_back( element );
}
/*!
delete all buffers without geometry in it.
*/
void CQ3LevelMesh::cleanMeshes()
{
if ( 0 == LoadParam.cleanUnResolvedMeshes )
return;
// delete all buffers without geometry in it.
u32 run = 0;
u32 remove = 0;
irr::scene::SMesh *m;
IMeshBuffer *b;
for ( u32 g = 0; g < E_Q3_MESH_SIZE; ++g )
{
bool texture0important = ( g == 0 );
run = 0;
remove = 0;
m = Mesh[g];
if ( LoadParam.verbose > 0 )
{
LoadParam.startTime = os::Timer::getRealTime();
if ( LoadParam.verbose > 1 )
{
snprintf( buf, sizeof ( buf ),
"quake3::cleanMeshes%d start for %d meshes",
g,
m->MeshBuffers.size()
);
os::Printer::log(buf, ELL_INFORMATION);
}
}
u32 i = 0;
s32 blockstart = -1;
s32 blockcount;
while( i < m->MeshBuffers.size())
{
run += 1;
b = m->MeshBuffers[i];
if ( b->getVertexCount() == 0 || b->getIndexCount() == 0 ||
( texture0important && b->getMaterial().getTexture(0) == 0 )
)
{
if ( blockstart < 0 )
{
blockstart = i;
blockcount = 0;
}
blockcount += 1;
i += 1;
// delete Meshbuffer
i -= 1;
remove += 1;
b->drop();
m->MeshBuffers.erase(i);
}
else
{
// clean blockwise
if ( blockstart >= 0 )
{
if ( LoadParam.verbose > 1 )
{
snprintf( buf, sizeof ( buf ),
"quake3::cleanMeshes%d cleaning mesh %d %d size",
g,
blockstart,
blockcount
);
os::Printer::log(buf, ELL_INFORMATION);
}
blockstart = -1;
}
i += 1;
}
}
if ( LoadParam.verbose > 0 )
{
LoadParam.endTime = os::Timer::getRealTime();
snprintf( buf, sizeof ( buf ),
"quake3::cleanMeshes%d needed %04d ms to clean %d of %d meshes",
g,
LoadParam.endTime - LoadParam.startTime,
remove,
run
);
os::Printer::log(buf, ELL_INFORMATION);
}
}
}
// recalculate bounding boxes
void CQ3LevelMesh::calcBoundingBoxes()
{
if ( LoadParam.verbose > 0 )
{
LoadParam.startTime = os::Timer::getRealTime();
if ( LoadParam.verbose > 1 )
{
snprintf( buf, sizeof ( buf ),
"quake3::calcBoundingBoxes start create %d textures and %d lightmaps",
NumTextures,
NumLightMaps
);
os::Printer::log(buf, ELL_INFORMATION);
}
}
// create bounding box
for ( u32 g = 0; g != E_Q3_MESH_SIZE; ++g )
{
for ( u32 j=0; j < Mesh[g]->MeshBuffers.size(); ++j)
{
((SMeshBufferLightMap*)Mesh[g]->MeshBuffers[j])->recalculateBoundingBox();
}
Mesh[g]->recalculateBoundingBox();
}
if ( LoadParam.verbose > 0 )
{
LoadParam.endTime = os::Timer::getRealTime();
snprintf( buf, sizeof ( buf ),
"quake3::calcBoundingBoxes needed %04d ms to create %d textures and %d lightmaps",
LoadParam.endTime - LoadParam.startTime,
NumTextures,
NumLightMaps
);
os::Printer::log( buf, ELL_INFORMATION);
}
}
//! loads the textures
void CQ3LevelMesh::loadTextures()
{
if (!Driver)
return;
if ( LoadParam.verbose > 0 )
{
LoadParam.startTime = os::Timer::getRealTime();
if ( LoadParam.verbose > 1 )
{
snprintf( buf, sizeof ( buf ),
"quake3::loadTextures start create %d textures and %d lightmaps",
NumTextures,
NumLightMaps
);
os::Printer::log( buf, ELL_INFORMATION);
}
}
c8 lightmapname[255];
s32 t;
// load lightmaps.
Lightmap.set_used(NumLightMaps+1);
/*
bool oldMipMapState = Driver->getTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS);
Driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);
*/
core::dimension2d<u32> lmapsize(128,128);
video::IImage* lmapImg;
for ( t = 0; t < NumLightMaps ; ++t)
{
sprintf(lightmapname, "%s.lightmap.%d", LevelName.c_str(), t);
// lightmap is a CTexture::R8G8B8 format
lmapImg = Driver->createImageFromData(
video::ECF_R8G8B8,
lmapsize,
LightMaps[t].imageBits, false, true );
Lightmap[t] = Driver->addTexture( lightmapname, lmapImg );
lmapImg->drop();
}
// Driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, oldMipMapState);
// load textures
Tex.set_used( NumTextures+1 );
const IShader * shader;
core::stringc list;
core::stringc check;
tTexArray textureArray;
// pre-load shaders
for ( t=0; t< NumTextures; ++t)
{
shader = getShader(Textures[t].strName, false);
}
for ( t=0; t< NumTextures; ++t)
{
Tex[t].ShaderID = -1;
Tex[t].Texture = 0;
list = "";
// get a shader ( if one exists )
shader = getShader( Textures[t].strName, false);
if ( shader )
{
Tex[t].ShaderID = shader->ID;
// if texture name == stage1 Texture map
const SVarGroup * group;
group = shader->getGroup( 2 );
if ( group )
{
if ( core::cutFilenameExtension( check, group->get( "map" ) ) == Textures[t].strName )
{
list += check;
}
else
if ( check == "$lightmap" )
{
// we check if lightmap is in stage 1 and texture in stage 2
group = shader->getGroup( 3 );
if ( group )
list += group->get( "map" );
}
}
}
else
{
// no shader, take it
list += Textures[t].strName;
}
u32 pos = 0;
getTextures( textureArray, list, pos, FileSystem, Driver );
Tex[t].Texture = textureArray[0];
}
if ( LoadParam.verbose > 0 )
{
LoadParam.endTime = os::Timer::getRealTime();
snprintf( buf, sizeof ( buf ),
"quake3::loadTextures needed %04d ms to create %d textures and %d lightmaps",
LoadParam.endTime - LoadParam.startTime,
NumTextures,
NumLightMaps
);
os::Printer::log( buf, ELL_INFORMATION);
}
}
//! Returns an axis aligned bounding box of the mesh.
//! \return A bounding box of this mesh is returned.
const core::aabbox3d<f32>& CQ3LevelMesh::getBoundingBox() const
{
return Mesh[0]->getBoundingBox();
}
void CQ3LevelMesh::setBoundingBox( const core::aabbox3df& box)
{
Mesh[0]->setBoundingBox(box); //?
}
//! Returns the type of the animated mesh.
E_ANIMATED_MESH_TYPE CQ3LevelMesh::getMeshType() const
{
return scene::EAMT_BSP;
}
} // end namespace scene
} // end namespace irr
#endif // _IRR_COMPILE_WITH_BSP_LOADER_
......@@ -83,7 +83,7 @@ namespace irr
{
public:
CCursorControl(const core::dimension2d<s32>& wsize, CIrrDeviceMacOSX *device) : WindowSize(wsize), IsVisible(true), InvWindowSize(0.0f, 0.0f), _device(device), UseReferenceRect(false)
CCursorControl(const core::dimension2d<u32>& wsize, CIrrDeviceMacOSX *device) : WindowSize(wsize), IsVisible(true), InvWindowSize(0.0f, 0.0f), _device(device), UseReferenceRect(false)
{
CursorPos.X = CursorPos.Y = 0;
if (WindowSize.Width!=0) InvWindowSize.Width = 1.0f / WindowSize.Width;
......
......@@ -432,30 +432,31 @@ bool CIrrDeviceMacOSX::createWindow()
_screenWidth = (int) CGDisplayPixelsWide(display);
_screenHeight = (int) CGDisplayPixelsHigh(display);
VideoModeList.setDesktop(CreationParams.Bits,core::dimension2d<s32>(_screenWidth, _screenHeight));
VideoModeList.setDesktop(CreationParams.Bits, core::dimension2d<u32>(_screenWidth, _screenHeight));
if (!CreationParams.Fullscreen)
{
_window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0,0,CreationParams.WindowSize.Width,CreationParams.WindowSize.Height) styleMask:NSTitledWindowMask+NSClosableWindowMask+NSResizableWindowMask backing:NSBackingStoreBuffered defer:FALSE];
if (_window != NULL)
{
NSOpenGLPixelFormatAttribute windowattribs[] = {
NSOpenGLPixelFormatAttribute windowattribs[] =
{
NSOpenGLPFANoRecovery,
NSOpenGLPFAAccelerated,
NSOpenGLPFADepthSize, depthSize,
NSOpenGLPFAColorSize, CreationParams.Bits,
NSOpenGLPFAAlphaSize, alphaSize,
NSOpenGLPFASampleBuffers, 1,
NSOpenGLPFASamples, CreationParams.AntiAlias,
NSOpenGLPFAStencilSize, CreationParams.Stencilbuffer?1:0,
NSOpenGLPFADepthSize, (NSOpenGLPixelFormatAttribute)depthSize,
NSOpenGLPFAColorSize, (NSOpenGLPixelFormatAttribute)CreationParams.Bits,
NSOpenGLPFAAlphaSize, (NSOpenGLPixelFormatAttribute)alphaSize,
NSOpenGLPFASampleBuffers, (NSOpenGLPixelFormatAttribute)1,
NSOpenGLPFASamples, (NSOpenGLPixelFormatAttribute)CreationParams.AntiAlias,
NSOpenGLPFAStencilSize, (NSOpenGLPixelFormatAttribute)(CreationParams.Stencilbuffer?1:0),
NSOpenGLPFADoubleBuffer,
(NSOpenGLPixelFormatAttribute)nil
};
if (CreationParams.AntiAlias<2)
{
windowattribs[9] = 0;
windowattribs[11] = 0;
windowattribs[9] = (NSOpenGLPixelFormatAttribute)0;
windowattribs[11] = (NSOpenGLPixelFormatAttribute)0;
}
NSOpenGLPixelFormat *format;
......@@ -466,7 +467,7 @@ bool CIrrDeviceMacOSX::createWindow()
// Second try without stencilbuffer
if (CreationParams.Stencilbuffer)
{
windowattribs[13]=0;
windowattribs[13]=(NSOpenGLPixelFormatAttribute)0;
}
else
continue;
......@@ -485,19 +486,19 @@ bool CIrrDeviceMacOSX::createWindow()
{
while (!format && windowattribs[12]>1)
{
windowattribs -= 1;
windowattribs[12] = (NSOpenGLPixelFormatAttribute)((int)windowattribs[12]-1);
format = [[NSOpenGLPixelFormat alloc] initWithAttributes:windowattribs];
}
if (!format)
{
windowattribs[9] = 0;
windowattribs[11] = 0;
windowattribs[9] = (NSOpenGLPixelFormatAttribute)0;
windowattribs[11] = (NSOpenGLPixelFormatAttribute)0;
format = [[NSOpenGLPixelFormat alloc] initWithAttributes:windowattribs];
if (!format)
{
// reset values for next try
windowattribs[9] = 1;
windowattribs[11] = CreationParams.AntiAlias;
windowattribs[9] = (NSOpenGLPixelFormatAttribute)1;
windowattribs[11] = (NSOpenGLPixelFormatAttribute)CreationParams.AntiAlias;
}
else
{
......@@ -626,7 +627,7 @@ void CIrrDeviceMacOSX::setResize(int width, int height)
// resize the driver to the inner pane size
NSRect driverFrame = [(NSWindow*)_window contentRectForFrameRect:[(NSWindow*)_window frame]];
getVideoDriver()->OnResize(core::dimension2d<s32>( (s32)driverFrame.size.width, (s32)driverFrame.size.height));
getVideoDriver()->OnResize(core::dimension2d<u32>( (s32)driverFrame.size.width, (s32)driverFrame.size.height));
}
void CIrrDeviceMacOSX::createDriver()
......@@ -1308,7 +1309,7 @@ video::IVideoModeList* CIrrDeviceMacOSX::getVideoModeList()
long width = GetDictionaryLong(mode, kCGDisplayWidth);
long height = GetDictionaryLong(mode, kCGDisplayHeight);
// long refresh = GetDictionaryLong((mode), kCGDisplayRefreshRate);
VideoModeList.addMode(core::dimension2d<s32>(width, height),
VideoModeList.addMode(core::dimension2d<u32>(width, height),
bitsPerPixel);
}
}
......
......@@ -221,6 +221,12 @@
09F649600D2CE206001E0599 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C53E26D0A4850D60014E966 /* Cocoa.framework */; };
09F649610D2CE206001E0599 /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4C53E26E0A4850D60014E966 /* OpenGL.framework */; };
09F649740D2CE2D0001E0599 /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 09F649730D2CE2D0001E0599 /* main.cpp */; };
3484C4E10F48D1B000C81F60 /* CGUIImageList.h in Headers */ = {isa = PBXBuildFile; fileRef = 3484C4DF0F48D1B000C81F60 /* CGUIImageList.h */; };
3484C4E20F48D1B000C81F60 /* CGUIImageList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3484C4E00F48D1B000C81F60 /* CGUIImageList.cpp */; };
3484C4EE0F48D3A100C81F60 /* CGUITreeView.h in Headers */ = {isa = PBXBuildFile; fileRef = 3484C4EC0F48D3A100C81F60 /* CGUITreeView.h */; };
3484C4EF0F48D3A100C81F60 /* CGUITreeView.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3484C4ED0F48D3A100C81F60 /* CGUITreeView.cpp */; };
3484C4FD0F48D4CB00C81F60 /* CMemoryFile.h in Headers */ = {isa = PBXBuildFile; fileRef = 3484C4FB0F48D4CB00C81F60 /* CMemoryFile.h */; };
3484C4FE0F48D4CB00C81F60 /* CMemoryFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3484C4FC0F48D4CB00C81F60 /* CMemoryFile.cpp */; };
4C364EA40A6C6DC2004CFBB4 /* COBJMeshFileLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C364EA20A6C6DC2004CFBB4 /* COBJMeshFileLoader.cpp */; };
4C43EEC00A74A5C800F942FC /* CPakReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C43EEBE0A74A5C800F942FC /* CPakReader.cpp */; };
4C53E2500A48504D0014E966 /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C0054770A48470500C844C2 /* main.cpp */; };
......@@ -332,7 +338,6 @@
4C53E4610A4856B30014E966 /* CReadFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DFA70A484C240014E966 /* CReadFile.cpp */; };
4C53E4620A4856B30014E966 /* COpenGLTexture.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF970A484C240014E966 /* COpenGLTexture.cpp */; };
4C53E4640A4856B30014E966 /* COSOperator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF990A484C240014E966 /* COSOperator.cpp */; };
4C53E4650A4856B30014E966 /* CMemoryReadFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF740A484C230014E966 /* CMemoryReadFile.cpp */; };
4C53E4660A4856B30014E966 /* CColladaFileLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DEFA0A484C220014E966 /* CColladaFileLoader.cpp */; };
4C53E4670A4856B30014E966 /* CCameraSceneNode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DEF80A484C220014E966 /* CCameraSceneNode.cpp */; };
4C53E4680A4856B30014E966 /* CMetaTriangleSelector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4C53DF7C0A484C230014E966 /* CMetaTriangleSelector.cpp */; };
......@@ -816,7 +821,7 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
09022C620EA0E97F00CD54EE /* GUIEditor.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GUIEditor.app; sourceTree = BUILT_PRODUCTS_DIR; };
09022C620EA0E97F00CD54EE /* GUIEditor_dbg.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GUIEditor_dbg.app; sourceTree = BUILT_PRODUCTS_DIR; };
09022C680EA0EA9D00CD54EE /* CGUIAttribute.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CGUIAttribute.h; path = ../../../tools/GUIEditor/CGUIAttribute.h; sourceTree = SOURCE_ROOT; };
09022C690EA0EA9D00CD54EE /* CGUIAttributeEditor.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = CGUIAttributeEditor.cpp; path = ../../../tools/GUIEditor/CGUIAttributeEditor.cpp; sourceTree = SOURCE_ROOT; };
09022C6A0EA0EA9D00CD54EE /* CGUIAttributeEditor.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CGUIAttributeEditor.h; path = ../../../tools/GUIEditor/CGUIAttributeEditor.h; sourceTree = SOURCE_ROOT; };
......@@ -907,7 +912,7 @@
093973BD0E0458B200E0C987 /* CSceneNodeAnimatorCameraFPS.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = CSceneNodeAnimatorCameraFPS.h; sourceTree = "<group>"; };
093973BE0E0458B200E0C987 /* CSceneNodeAnimatorCameraMaya.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = CSceneNodeAnimatorCameraMaya.cpp; sourceTree = "<group>"; };
093973BF0E0458B200E0C987 /* CSceneNodeAnimatorCameraMaya.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = CSceneNodeAnimatorCameraMaya.h; sourceTree = "<group>"; };
0946CCB40EC99BBE00D945A5 /* MouseAndJoystick.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MouseAndJoystick.app; sourceTree = BUILT_PRODUCTS_DIR; };
0946CCB40EC99BBE00D945A5 /* MouseAndJoystick_dbg.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MouseAndJoystick_dbg.app; sourceTree = BUILT_PRODUCTS_DIR; };
0946CCCA0EC99C6E00D945A5 /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = main.cpp; path = 19.MouseAndJoystick/main.cpp; sourceTree = "<group>"; };
0968401E0D0F1A2300333EFD /* CB3DMeshFileLoader.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = CB3DMeshFileLoader.cpp; sourceTree = "<group>"; };
0968401F0D0F1A2300333EFD /* CB3DMeshFileLoader.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = CB3DMeshFileLoader.h; sourceTree = "<group>"; };
......@@ -953,11 +958,17 @@
096F8E3C0EA2EFBA00907EC5 /* COBJMeshWriter.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = COBJMeshWriter.h; sourceTree = "<group>"; };
09C638700D4F1A69000B6A18 /* CLWOMeshFileLoader.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = CLWOMeshFileLoader.cpp; sourceTree = "<group>"; };
09C638710D4F1A69000B6A18 /* CLWOMeshFileLoader.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = CLWOMeshFileLoader.h; sourceTree = "<group>"; };
09F649030D2CDED9001E0599 /* HelloWorld.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HelloWorld.app; sourceTree = BUILT_PRODUCTS_DIR; };
09F649030D2CDED9001E0599 /* HelloWorld_dbg.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HelloWorld_dbg.app; sourceTree = BUILT_PRODUCTS_DIR; };
09F6492E0D2CE038001E0599 /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = main.cpp; path = 15.LoadIrrFile/main.cpp; sourceTree = "<group>"; };
09F6493E0D2CE03E001E0599 /* LoadIrrFile.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LoadIrrFile.app; sourceTree = BUILT_PRODUCTS_DIR; };
09F649650D2CE206001E0599 /* Quake3Shader.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Quake3Shader.app; sourceTree = BUILT_PRODUCTS_DIR; };
09F6493E0D2CE03E001E0599 /* LoadIrrFile_dbg.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LoadIrrFile_dbg.app; sourceTree = BUILT_PRODUCTS_DIR; };
09F649650D2CE206001E0599 /* Quake3Shader_dbg.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Quake3Shader_dbg.app; sourceTree = BUILT_PRODUCTS_DIR; };
09F649730D2CE2D0001E0599 /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = main.cpp; path = 16.Quake3MapShader/main.cpp; sourceTree = "<group>"; };
3484C4DF0F48D1B000C81F60 /* CGUIImageList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CGUIImageList.h; sourceTree = "<group>"; };
3484C4E00F48D1B000C81F60 /* CGUIImageList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CGUIImageList.cpp; sourceTree = "<group>"; };
3484C4EC0F48D3A100C81F60 /* CGUITreeView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CGUITreeView.h; sourceTree = "<group>"; };
3484C4ED0F48D3A100C81F60 /* CGUITreeView.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CGUITreeView.cpp; sourceTree = "<group>"; };
3484C4FB0F48D4CB00C81F60 /* CMemoryFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CMemoryFile.h; sourceTree = "<group>"; };
3484C4FC0F48D4CB00C81F60 /* CMemoryFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CMemoryFile.cpp; sourceTree = "<group>"; };
4C0054710A48470500C844C2 /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = "<group>"; };
4C0054770A48470500C844C2 /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = "<group>"; };
4C00547D0A48470500C844C2 /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = "<group>"; };
......@@ -1112,8 +1123,6 @@
4C53DF710A484C230014E966 /* CLMTSMeshFileLoader.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = CLMTSMeshFileLoader.h; sourceTree = "<group>"; };
4C53DF720A484C230014E966 /* CLogger.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = CLogger.cpp; sourceTree = "<group>"; };
4C53DF730A484C230014E966 /* CLogger.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = CLogger.h; sourceTree = "<group>"; };
4C53DF740A484C230014E966 /* CMemoryReadFile.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 2; path = CMemoryReadFile.cpp; sourceTree = "<group>"; };
4C53DF750A484C230014E966 /* CMemoryReadFile.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; lineEnding = 2; path = CMemoryReadFile.h; sourceTree = "<group>"; };
4C53DF760A484C230014E966 /* CMeshCache.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = CMeshCache.cpp; sourceTree = "<group>"; };
4C53DF770A484C230014E966 /* CMeshCache.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = CMeshCache.h; sourceTree = "<group>"; };
4C53DF780A484C230014E966 /* CMeshManipulator.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = CMeshManipulator.cpp; sourceTree = "<group>"; };
......@@ -1281,7 +1290,7 @@
4C53E18D0A484C2C0014E966 /* uncompr.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = uncompr.c; sourceTree = "<group>"; };
4C53E1920A484C2C0014E966 /* zutil.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = zutil.c; sourceTree = "<group>"; };
4C53E24D0A4850120014E966 /* libIrrlicht.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libIrrlicht.a; sourceTree = BUILT_PRODUCTS_DIR; };
4C53E2520A4850550014E966 /* Quake3Map.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Quake3Map.app; sourceTree = BUILT_PRODUCTS_DIR; };
4C53E2520A4850550014E966 /* Quake3Map_dbg.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Quake3Map_dbg.app; sourceTree = BUILT_PRODUCTS_DIR; };
4C53E26D0A4850D60014E966 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; };
4C53E26E0A4850D60014E966 /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = /System/Library/Frameworks/OpenGL.framework; sourceTree = "<absolute>"; };
4C53E38E0A4855BA0014E966 /* DemoApp-Info.plist */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text.plist.xml; path = "DemoApp-Info.plist"; sourceTree = "<group>"; };
......@@ -1345,18 +1354,18 @@
4C53E76F0A485CD90014E966 /* wrrle.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = wrrle.c; sourceTree = "<group>"; };
4C53E7700A485CD90014E966 /* wrtarga.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = wrtarga.c; sourceTree = "<group>"; };
4C6DC9B60A48715A0017A6E5 /* inflate.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = inflate.c; sourceTree = "<group>"; };
4CA25B980A485D9800B4E469 /* CustomSceneNode.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CustomSceneNode.app; sourceTree = BUILT_PRODUCTS_DIR; };
4CA25B9A0A485D9800B4E469 /* MeshViewer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MeshViewer.app; sourceTree = BUILT_PRODUCTS_DIR; };
4CA25B9C0A485D9800B4E469 /* RenderToTexture.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RenderToTexture.app; sourceTree = BUILT_PRODUCTS_DIR; };
4CA25B9E0A485D9800B4E469 /* UserInterface.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = UserInterface.app; sourceTree = BUILT_PRODUCTS_DIR; };
4CA25BA00A485D9800B4E469 /* PerPixelLighting.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PerPixelLighting.app; sourceTree = BUILT_PRODUCTS_DIR; };
4CA25BA20A485D9800B4E469 /* Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Demo.app; sourceTree = BUILT_PRODUCTS_DIR; };
4CA25BA40A485D9800B4E469 /* Movement.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Movement.app; sourceTree = BUILT_PRODUCTS_DIR; };
4CA25BA60A485D9800B4E469 /* Shaders.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Shaders.app; sourceTree = BUILT_PRODUCTS_DIR; };
4CA25BA80A485D9800B4E469 /* SpecialFx.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SpecialFx.app; sourceTree = BUILT_PRODUCTS_DIR; };
4CA25BAA0A485D9800B4E469 /* TerrainRendering.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TerrainRendering.app; sourceTree = BUILT_PRODUCTS_DIR; };
4CA25BAC0A485D9800B4E469 /* 2DGraphics.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = 2DGraphics.app; sourceTree = BUILT_PRODUCTS_DIR; };
4CA25BAE0A485D9800B4E469 /* Collision.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Collision.app; sourceTree = BUILT_PRODUCTS_DIR; };
4CA25B980A485D9800B4E469 /* CustomSceneNode_dbg.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CustomSceneNode_dbg.app; sourceTree = BUILT_PRODUCTS_DIR; };
4CA25B9A0A485D9800B4E469 /* MeshViewer_dbg.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MeshViewer_dbg.app; sourceTree = BUILT_PRODUCTS_DIR; };
4CA25B9C0A485D9800B4E469 /* RenderToTexture_dbg.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RenderToTexture_dbg.app; sourceTree = BUILT_PRODUCTS_DIR; };
4CA25B9E0A485D9800B4E469 /* UserInterface_dbg.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = UserInterface_dbg.app; sourceTree = BUILT_PRODUCTS_DIR; };
4CA25BA00A485D9800B4E469 /* PerPixelLighting_dbg.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PerPixelLighting_dbg.app; sourceTree = BUILT_PRODUCTS_DIR; };
4CA25BA20A485D9800B4E469 /* Demo_dbg.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Demo_dbg.app; sourceTree = BUILT_PRODUCTS_DIR; };
4CA25BA40A485D9800B4E469 /* Movement_dbg.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Movement_dbg.app; sourceTree = BUILT_PRODUCTS_DIR; };
4CA25BA60A485D9800B4E469 /* Shaders_dbg.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Shaders_dbg.app; sourceTree = BUILT_PRODUCTS_DIR; };
4CA25BA80A485D9800B4E469 /* SpecialFx_dbg.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SpecialFx_dbg.app; sourceTree = BUILT_PRODUCTS_DIR; };
4CA25BAA0A485D9800B4E469 /* TerrainRendering_dbg.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TerrainRendering_dbg.app; sourceTree = BUILT_PRODUCTS_DIR; };
4CA25BAC0A485D9800B4E469 /* 2DGraphics_dbg.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = 2DGraphics_dbg.app; sourceTree = BUILT_PRODUCTS_DIR; };
4CA25BAE0A485D9800B4E469 /* Collision_dbg.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Collision_dbg.app; sourceTree = BUILT_PRODUCTS_DIR; };
4CC36B0D0A6B61DB0076C4B2 /* CSphereSceneNode.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = CSphereSceneNode.cpp; sourceTree = "<group>"; };
4CC36B0E0A6B61DB0076C4B2 /* CSphereSceneNode.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = CSphereSceneNode.h; sourceTree = "<group>"; };
4CFA7BDC0A88735900B03626 /* CImageLoaderBMP.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 2; path = CImageLoaderBMP.cpp; sourceTree = "<group>"; };
......@@ -2005,6 +2014,8 @@
4C53DF3D0A484C230014E966 /* CGUIFont.h */,
4C53DF3E0A484C230014E966 /* CGUIImage.cpp */,
4C53DF3F0A484C230014E966 /* CGUIImage.h */,
3484C4DF0F48D1B000C81F60 /* CGUIImageList.h */,
3484C4E00F48D1B000C81F60 /* CGUIImageList.cpp */,
4C53DF400A484C230014E966 /* CGUIInOutFader.cpp */,
4C53DF410A484C230014E966 /* CGUIInOutFader.h */,
4C53DF420A484C230014E966 /* CGUIListBox.cpp */,
......@@ -2033,6 +2044,8 @@
0910B9DB0D1F5D4100D46B04 /* CGUITable.h */,
4C53DF540A484C230014E966 /* CGUIToolBar.cpp */,
4C53DF550A484C230014E966 /* CGUIToolBar.h */,
3484C4EC0F48D3A100C81F60 /* CGUITreeView.h */,
3484C4ED0F48D3A100C81F60 /* CGUITreeView.cpp */,
4C53DF560A484C230014E966 /* CGUIWindow.cpp */,
4C53DF570A484C230014E966 /* CGUIWindow.h */,
);
......@@ -2053,8 +2066,8 @@
4C53DF290A484C230014E966 /* CFileSystem.h */,
4C53DF6E0A484C230014E966 /* CLimitReadFile.cpp */,
4C53DF6F0A484C230014E966 /* CLimitReadFile.h */,
4C53DF740A484C230014E966 /* CMemoryReadFile.cpp */,
4C53DF750A484C230014E966 /* CMemoryReadFile.h */,
3484C4FB0F48D4CB00C81F60 /* CMemoryFile.h */,
3484C4FC0F48D4CB00C81F60 /* CMemoryFile.cpp */,
4C43EEBE0A74A5C800F942FC /* CPakReader.cpp */,
4C43EEBF0A74A5C800F942FC /* CPakReader.h */,
4C53DFA70A484C240014E966 /* CReadFile.cpp */,
......@@ -2835,25 +2848,25 @@
4C53E24C0A484FED0014E966 /* Products */ = {
isa = PBXGroup;
children = (
0946CCB40EC99BBE00D945A5 /* MouseAndJoystick.app */,
09022C620EA0E97F00CD54EE /* GUIEditor.app */,
0946CCB40EC99BBE00D945A5 /* MouseAndJoystick_dbg.app */,
09022C620EA0E97F00CD54EE /* GUIEditor_dbg.app */,
4C53E24D0A4850120014E966 /* libIrrlicht.a */,
4C53E2520A4850550014E966 /* Quake3Map.app */,
4CA25B980A485D9800B4E469 /* CustomSceneNode.app */,
4CA25BA40A485D9800B4E469 /* Movement.app */,
4CA25B9E0A485D9800B4E469 /* UserInterface.app */,
4CA25BAC0A485D9800B4E469 /* 2DGraphics.app */,
4CA25BAE0A485D9800B4E469 /* Collision.app */,
4CA25BA80A485D9800B4E469 /* SpecialFx.app */,
4CA25B9A0A485D9800B4E469 /* MeshViewer.app */,
4CA25BA60A485D9800B4E469 /* Shaders.app */,
4CA25BA00A485D9800B4E469 /* PerPixelLighting.app */,
4CA25B9C0A485D9800B4E469 /* RenderToTexture.app */,
4CA25BAA0A485D9800B4E469 /* TerrainRendering.app */,
4CA25BA20A485D9800B4E469 /* Demo.app */,
09F6493E0D2CE03E001E0599 /* LoadIrrFile.app */,
09F649650D2CE206001E0599 /* Quake3Shader.app */,
09F649030D2CDED9001E0599 /* HelloWorld.app */,
4C53E2520A4850550014E966 /* Quake3Map_dbg.app */,
4CA25B980A485D9800B4E469 /* CustomSceneNode_dbg.app */,
4CA25BA40A485D9800B4E469 /* Movement_dbg.app */,
4CA25B9E0A485D9800B4E469 /* UserInterface_dbg.app */,
4CA25BAC0A485D9800B4E469 /* 2DGraphics_dbg.app */,
4CA25BAE0A485D9800B4E469 /* Collision_dbg.app */,
4CA25BA80A485D9800B4E469 /* SpecialFx_dbg.app */,
4CA25B9A0A485D9800B4E469 /* MeshViewer_dbg.app */,
4CA25BA60A485D9800B4E469 /* Shaders_dbg.app */,
4CA25BA00A485D9800B4E469 /* PerPixelLighting_dbg.app */,
4CA25B9C0A485D9800B4E469 /* RenderToTexture_dbg.app */,
4CA25BAA0A485D9800B4E469 /* TerrainRendering_dbg.app */,
4CA25BA20A485D9800B4E469 /* Demo_dbg.app */,
09F6493E0D2CE03E001E0599 /* LoadIrrFile_dbg.app */,
09F649650D2CE206001E0599 /* Quake3Shader_dbg.app */,
09F649030D2CDED9001E0599 /* HelloWorld_dbg.app */,
);
name = Products;
sourceTree = "<group>";
......@@ -3011,6 +3024,9 @@
093973C30E0458B200E0C987 /* CSceneNodeAnimatorCameraMaya.h in Headers */,
096F8E3E0EA2EFBA00907EC5 /* COBJMeshWriter.h in Headers */,
096CC0E10ECE65B500C81DC7 /* CParticleScaleAffector.h in Headers */,
3484C4E10F48D1B000C81F60 /* CGUIImageList.h in Headers */,
3484C4EE0F48D3A100C81F60 /* CGUITreeView.h in Headers */,
3484C4FD0F48D4CB00C81F60 /* CMemoryFile.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
......@@ -3032,7 +3048,7 @@
);
name = GUIEditor;
productName = DemoApp;
productReference = 09022C620EA0E97F00CD54EE /* GUIEditor.app */;
productReference = 09022C620EA0E97F00CD54EE /* GUIEditor_dbg.app */;
productType = "com.apple.product-type.application";
};
0946CCA30EC99BBE00D945A5 /* MouseAndJoystick */ = {
......@@ -3050,7 +3066,7 @@
);
name = MouseAndJoystick;
productName = DemoApp;
productReference = 0946CCB40EC99BBE00D945A5 /* MouseAndJoystick.app */;
productReference = 0946CCB40EC99BBE00D945A5 /* MouseAndJoystick_dbg.app */;
productType = "com.apple.product-type.application";
};
09F648F40D2CDED9001E0599 /* HelloWorld */ = {
......@@ -3068,7 +3084,7 @@
);
name = HelloWorld;
productName = DemoApp;
productReference = 09F649030D2CDED9001E0599 /* HelloWorld.app */;
productReference = 09F649030D2CDED9001E0599 /* HelloWorld_dbg.app */;
productType = "com.apple.product-type.application";
};
09F6492F0D2CE03E001E0599 /* LoadIrrFile */ = {
......@@ -3086,7 +3102,7 @@
);
name = LoadIrrFile;
productName = DemoApp;
productReference = 09F6493E0D2CE03E001E0599 /* LoadIrrFile.app */;
productReference = 09F6493E0D2CE03E001E0599 /* LoadIrrFile_dbg.app */;
productType = "com.apple.product-type.application";
};
09F649560D2CE206001E0599 /* Quake3Shader */ = {
......@@ -3104,7 +3120,7 @@
);
name = Quake3Shader;
productName = DemoApp;
productReference = 09F649650D2CE206001E0599 /* Quake3Shader.app */;
productReference = 09F649650D2CE206001E0599 /* Quake3Shader_dbg.app */;
productType = "com.apple.product-type.application";
};
B81CFDFE097FD9F50057C06F /* 2DGraphics */ = {
......@@ -3122,7 +3138,7 @@
);
name = 2DGraphics;
productName = DemoApp;
productReference = 4CA25BAC0A485D9800B4E469 /* 2DGraphics.app */;
productReference = 4CA25BAC0A485D9800B4E469 /* 2DGraphics_dbg.app */;
productType = "com.apple.product-type.application";
};
B81CFE82097FDDE20057C06F /* Collision */ = {
......@@ -3140,7 +3156,7 @@
);
name = Collision;
productName = DemoApp;
productReference = 4CA25BAE0A485D9800B4E469 /* Collision.app */;
productReference = 4CA25BAE0A485D9800B4E469 /* Collision_dbg.app */;
productType = "com.apple.product-type.application";
};
B81CFEA4097FDE900057C06F /* PerPixelLightning */ = {
......@@ -3158,7 +3174,7 @@
);
name = PerPixelLightning;
productName = DemoApp;
productReference = 4CA25BA00A485D9800B4E469 /* PerPixelLighting.app */;
productReference = 4CA25BA00A485D9800B4E469 /* PerPixelLighting_dbg.app */;
productType = "com.apple.product-type.application";
};
B81CFEC2097FDF020057C06F /* TerrainRendering */ = {
......@@ -3176,7 +3192,7 @@
);
name = TerrainRendering;
productName = DemoApp;
productReference = 4CA25BAA0A485D9800B4E469 /* TerrainRendering.app */;
productReference = 4CA25BAA0A485D9800B4E469 /* TerrainRendering_dbg.app */;
productType = "com.apple.product-type.application";
};
B81CFEE8097FE05F0057C06F /* SpecialFx */ = {
......@@ -3194,7 +3210,7 @@
);
name = SpecialFx;
productName = DemoApp;
productReference = 4CA25BA80A485D9800B4E469 /* SpecialFx.app */;
productReference = 4CA25BA80A485D9800B4E469 /* SpecialFx_dbg.app */;
productType = "com.apple.product-type.application";
};
B81CFF07097FE13E0057C06F /* UserInterface */ = {
......@@ -3212,7 +3228,7 @@
);
name = UserInterface;
productName = DemoApp;
productReference = 4CA25B9E0A485D9800B4E469 /* UserInterface.app */;
productReference = 4CA25B9E0A485D9800B4E469 /* UserInterface_dbg.app */;
productType = "com.apple.product-type.application";
};
B81CFF1E097FE1E00057C06F /* CustomSceneNode */ = {
......@@ -3230,7 +3246,7 @@
);
name = CustomSceneNode;
productName = DemoApp;
productReference = 4CA25B980A485D9800B4E469 /* CustomSceneNode.app */;
productReference = 4CA25B980A485D9800B4E469 /* CustomSceneNode_dbg.app */;
productType = "com.apple.product-type.application";
};
B81CFF33097FE25F0057C06F /* Quake3Map */ = {
......@@ -3248,7 +3264,7 @@
);
name = Quake3Map;
productName = DemoApp;
productReference = 4C53E2520A4850550014E966 /* Quake3Map.app */;
productReference = 4C53E2520A4850550014E966 /* Quake3Map_dbg.app */;
productType = "com.apple.product-type.application";
};
B81CFF4A097FE3050057C06F /* Shaders */ = {
......@@ -3266,7 +3282,7 @@
);
name = Shaders;
productName = DemoApp;
productReference = 4CA25BA60A485D9800B4E469 /* Shaders.app */;
productReference = 4CA25BA60A485D9800B4E469 /* Shaders_dbg.app */;
productType = "com.apple.product-type.application";
};
B81CFF78097FE3DC0057C06F /* Movement */ = {
......@@ -3284,7 +3300,7 @@
);
name = Movement;
productName = DemoApp;
productReference = 4CA25BA40A485D9800B4E469 /* Movement.app */;
productReference = 4CA25BA40A485D9800B4E469 /* Movement_dbg.app */;
productType = "com.apple.product-type.application";
};
B81CFF91097FE45E0057C06F /* MeshViewer */ = {
......@@ -3302,7 +3318,7 @@
);
name = MeshViewer;
productName = DemoApp;
productReference = 4CA25B9A0A485D9800B4E469 /* MeshViewer.app */;
productReference = 4CA25B9A0A485D9800B4E469 /* MeshViewer_dbg.app */;
productType = "com.apple.product-type.application";
};
B81CFFAF097FE5F80057C06F /* RenderToTexture */ = {
......@@ -3320,7 +3336,7 @@
);
name = RenderToTexture;
productName = DemoApp;
productReference = 4CA25B9C0A485D9800B4E469 /* RenderToTexture.app */;
productReference = 4CA25B9C0A485D9800B4E469 /* RenderToTexture_dbg.app */;
productType = "com.apple.product-type.application";
};
B8DEF35C0950229200FDEA7E /* Demo */ = {
......@@ -3338,7 +3354,7 @@
);
name = Demo;
productName = DemoApp;
productReference = 4CA25BA20A485D9800B4E469 /* Demo.app */;
productReference = 4CA25BA20A485D9800B4E469 /* Demo_dbg.app */;
productType = "com.apple.product-type.application";
};
D2AAC07D0554694100DB518D /* libIrrlicht.a */ = {
......@@ -3844,7 +3860,6 @@
4C53E4610A4856B30014E966 /* CReadFile.cpp in Sources */,
4C53E4620A4856B30014E966 /* COpenGLTexture.cpp in Sources */,
4C53E4640A4856B30014E966 /* COSOperator.cpp in Sources */,
4C53E4650A4856B30014E966 /* CMemoryReadFile.cpp in Sources */,
4C53E4660A4856B30014E966 /* CColladaFileLoader.cpp in Sources */,
4C53E4670A4856B30014E966 /* CCameraSceneNode.cpp in Sources */,
4C53E4680A4856B30014E966 /* CMetaTriangleSelector.cpp in Sources */,
......@@ -4006,6 +4021,9 @@
09293C4D0ED32029003B8C9C /* pngwrite.c in Sources */,
09293C4E0ED32029003B8C9C /* pngwtran.c in Sources */,
09293C4F0ED32029003B8C9C /* pngwutil.c in Sources */,
3484C4E20F48D1B000C81F60 /* CGUIImageList.cpp in Sources */,
3484C4EF0F48D3A100C81F60 /* CGUITreeView.cpp in Sources */,
3484C4FE0F48D4CB00C81F60 /* CMemoryFile.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment