diff --git a/tools/urt/libs/.cvsignore b/tools/urt/libs/.cvsignore new file mode 100644 index 00000000..62555de1 --- /dev/null +++ b/tools/urt/libs/.cvsignore @@ -0,0 +1 @@ +.consign diff --git a/tools/urt/libs/archivelib.cpp b/tools/urt/libs/archivelib.cpp new file mode 100644 index 00000000..d2ab0dfa --- /dev/null +++ b/tools/urt/libs/archivelib.cpp @@ -0,0 +1,2 @@ + +#include "archivelib.h" diff --git a/tools/urt/libs/archivelib.h b/tools/urt/libs/archivelib.h new file mode 100644 index 00000000..8224bd47 --- /dev/null +++ b/tools/urt/libs/archivelib.h @@ -0,0 +1,237 @@ + +#if !defined (INCLUDED_ARCHIVELIB_H) +#define INCLUDED_ARCHIVELIB_H + +#include "debugging/debugging.h" +#include "iarchive.h" +#include "stream/filestream.h" +#include "stream/textfilestream.h" +#include "memory/allocator.h" +#include "string/string.h" + +/// \brief A single-byte-reader wrapper around an InputStream. +/// Optimised for reading one byte at a time. +/// Uses a buffer to reduce the number of times the wrapped stream must be read. +template +class SingleByteInputStream +{ + typedef typename InputStreamType::byte_type byte_type; + enum { c_bufferSize = SIZE }; + + InputStreamType& m_inputStream; + byte_type m_buffer[c_bufferSize]; + byte_type* m_cur; + byte_type* m_end; + +public: + + SingleByteInputStream(InputStreamType& inputStream) : m_inputStream(inputStream), m_cur(m_buffer + c_bufferSize), m_end(m_cur) + { + } + bool readByte(byte_type& b) + { + if(m_cur == m_end) + { + if(m_end != m_buffer + c_bufferSize) + { + return false; + } + + m_end = m_buffer + m_inputStream.read(m_buffer, c_bufferSize); + m_cur = m_buffer; + + if(m_end == m_buffer) + { + return false; + } + } + + b = *m_cur++; + + return true; + } +}; + +/// \brief A binary-to-text wrapper around an InputStream. +/// Converts CRLF or LFCR line-endings to LF line-endings. +template +class BinaryToTextInputStream : public TextInputStream +{ + SingleByteInputStream m_inputStream; +public: + BinaryToTextInputStream(BinaryInputStreamType& inputStream) : m_inputStream(inputStream) + { + } + std::size_t read(char* buffer, std::size_t length) + { + char* p = buffer; + for(;;) + { + if(length != 0 && m_inputStream.readByte(*reinterpret_cast(p))) + { + if(*p != '\r') + { + ++p; + --length; + } + } + else + { + return p - buffer; + } + } + } +}; + +/// \brief An ArchiveFile which is stored uncompressed as part of a larger archive file. +class StoredArchiveFile : public ArchiveFile +{ + CopiedString m_name; + FileInputStream m_filestream; + SubFileInputStream m_substream; + FileInputStream::size_type m_size; +public: + typedef FileInputStream::size_type size_type; + typedef FileInputStream::position_type position_type; + + StoredArchiveFile(const char* name, const char* archiveName, position_type position, size_type stream_size, size_type file_size) + : m_name(name), m_filestream(archiveName), m_substream(m_filestream, position, stream_size), m_size(file_size) + { + } + + static StoredArchiveFile* create(const char* name, const char* archiveName, position_type position, size_type stream_size, size_type file_size) + { + return New().scalar(name, archiveName, position, stream_size, file_size); + } + + void release() + { + Delete().scalar(this); + } + size_type size() const + { + return m_size; + } + const char* getName() const + { + return m_name.c_str(); + } + InputStream& getInputStream() + { + return m_substream; + } +}; + +/// \brief An ArchiveTextFile which is stored uncompressed as part of a larger archive file. +class StoredArchiveTextFile : public ArchiveTextFile +{ + CopiedString m_name; + FileInputStream m_filestream; + SubFileInputStream m_substream; + BinaryToTextInputStream m_textStream; +public: + typedef FileInputStream::size_type size_type; + typedef FileInputStream::position_type position_type; + + StoredArchiveTextFile(const char* name, const char* archiveName, position_type position, size_type stream_size) + : m_name(name), m_filestream(archiveName), m_substream(m_filestream, position, stream_size), m_textStream(m_substream) + { + } + + static StoredArchiveTextFile* create(const char* name, const char* archiveName, position_type position, size_type stream_size) + { + return New().scalar(name, archiveName, position, stream_size); + } + + void release() + { + Delete().scalar(this); + } + const char* getName() const + { + return m_name.c_str(); + } + TextInputStream& getInputStream() + { + return m_textStream; + } +}; + +/// \brief An ArchiveFile which is stored as a single file on disk. +class DirectoryArchiveFile : public ArchiveFile +{ + CopiedString m_name; + FileInputStream m_istream; + FileInputStream::size_type m_size; +public: + typedef FileInputStream::size_type size_type; + + DirectoryArchiveFile(const char* name, const char* filename) + : m_name(name), m_istream(filename) + { + if(!failed()) + { + m_istream.seek(0, FileInputStream::end); + m_size = m_istream.tell(); + m_istream.seek(0); + } + else + { + m_size = 0; + } + } + bool failed() const + { + return m_istream.failed(); + } + + void release() + { + delete this; + } + size_type size() const + { + return m_size; + } + const char* getName() const + { + return m_name.c_str(); + } + InputStream& getInputStream() + { + return m_istream; + } +}; + +/// \brief An ArchiveTextFile which is stored as a single file on disk. +class DirectoryArchiveTextFile : public ArchiveTextFile +{ + CopiedString m_name; + TextFileInputStream m_inputStream; +public: + + DirectoryArchiveTextFile(const char* name, const char* filename) + : m_name(name), m_inputStream(filename) + { + } + bool failed() const + { + return m_inputStream.failed(); + } + + void release() + { + delete this; + } + const char* getName() const + { + return m_name.c_str(); + } + TextInputStream& getInputStream() + { + return m_inputStream; + } +}; + + +#endif diff --git a/tools/urt/libs/bytebool.cpp b/tools/urt/libs/bytebool.cpp new file mode 100644 index 00000000..d66010de --- /dev/null +++ b/tools/urt/libs/bytebool.cpp @@ -0,0 +1,2 @@ + +#include "bytebool.h" diff --git a/tools/urt/libs/bytebool.h b/tools/urt/libs/bytebool.h new file mode 100644 index 00000000..63874d05 --- /dev/null +++ b/tools/urt/libs/bytebool.h @@ -0,0 +1,11 @@ +#ifndef __BYTEBOOL__ +#define __BYTEBOOL__ + +// defines boolean and byte types usable in both c and c++ code +// this header is not really meant for direct inclusion, +// it is used by mathlib and cmdlib + +typedef enum { qfalse, qtrue } qboolean; +typedef unsigned char byte; + +#endif diff --git a/tools/urt/libs/bytestreamutils.cpp b/tools/urt/libs/bytestreamutils.cpp new file mode 100644 index 00000000..a4606ef1 --- /dev/null +++ b/tools/urt/libs/bytestreamutils.cpp @@ -0,0 +1,2 @@ + +#include "bytestreamutils.h" diff --git a/tools/urt/libs/bytestreamutils.h b/tools/urt/libs/bytestreamutils.h new file mode 100644 index 00000000..d01fd22e --- /dev/null +++ b/tools/urt/libs/bytestreamutils.h @@ -0,0 +1,100 @@ + +#if !defined(INCLUDED_BYTESTREAMUTILS_H) +#define INCLUDED_BYTESTREAMUTILS_H + +#include + +#if defined(_MSC_VER) + +typedef signed short int16_t; +typedef unsigned short uint16_t; +typedef signed int int32_t; +typedef unsigned int uint32_t; + +#else + +#define _ISOC9X_SOURCE 1 +#define _ISOC99_SOURCE 1 + +#define __USE_ISOC9X 1 +#define __USE_ISOC99 1 + +#include + +#endif + + +template +inline void istream_read_little_endian(InputStreamType& istream, Type& value) +{ + istream.read(reinterpret_cast(&value), sizeof(Type)); +#if defined(__BIG_ENDIAN__) + std::reverse(reinterpret_cast(&value), reinterpret_cast(&value) + sizeof(Type)); +#endif +} + +template +inline void istream_read_big_endian(InputStreamType& istream, Type& value) +{ + istream.read(reinterpret_cast(&value), sizeof(Type)); +#if !defined(__BIG_ENDIAN__) + std::reverse(reinterpret_cast(&value), reinterpret_cast(&value) + sizeof(Type)); +#endif +} + +template +inline void istream_read_byte(InputStreamType& istream, typename InputStreamType::byte_type& b) +{ + istream.read(&b, 1); +} + + +template +inline int16_t istream_read_int16_le(InputStreamType& istream) +{ + int16_t value; + istream_read_little_endian(istream, value); + return value; +} + +template +inline uint16_t istream_read_uint16_le(InputStreamType& istream) +{ + uint16_t value; + istream_read_little_endian(istream, value); + return value; +} + +template +inline int32_t istream_read_int32_le(InputStreamType& istream) +{ + int32_t value; + istream_read_little_endian(istream, value); + return value; +} + +template +inline uint32_t istream_read_uint32_le(InputStreamType& istream) +{ + uint32_t value; + istream_read_little_endian(istream, value); + return value; +} + +template +inline float istream_read_float32_le(InputStreamType& istream) +{ + float value; + istream_read_little_endian(istream, value); + return value; +} + +template +inline typename InputStreamType::byte_type istream_read_byte(InputStreamType& istream) +{ + typename InputStreamType::byte_type b; + istream.read(&b, sizeof(typename InputStreamType::byte_type)); + return b; +} + +#endif diff --git a/tools/urt/libs/character.cpp b/tools/urt/libs/character.cpp new file mode 100644 index 00000000..a83d71f3 --- /dev/null +++ b/tools/urt/libs/character.cpp @@ -0,0 +1,3 @@ + +#include "character.h" + diff --git a/tools/urt/libs/character.h b/tools/urt/libs/character.h new file mode 100644 index 00000000..457928bd --- /dev/null +++ b/tools/urt/libs/character.h @@ -0,0 +1,27 @@ + +#if !defined(INCLUDED_CHARACTER_H) +#define INCLUDED_CHARACTER_H + +/// \file +/// \brief Character encoding. + +/// \brief Returns true if \p c is an ASCII character that can be represented with 7 bits. +inline bool char_is_ascii(char c) +{ + return (c & 0x80) == 0; +} + +/// \brief Returns true if \p string consists entirely of ASCII characters. +inline bool string_is_ascii(const char* string) +{ + while(*string != '\0') + { + if(!char_is_ascii(*string++)) + { + return false; + } + } + return true; +} + +#endif diff --git a/tools/urt/libs/cmdlib.h b/tools/urt/libs/cmdlib.h new file mode 100644 index 00000000..be41c13e --- /dev/null +++ b/tools/urt/libs/cmdlib.h @@ -0,0 +1,106 @@ +/* +This code is based on source provided under the terms of the Id Software +LIMITED USE SOFTWARE LICENSE AGREEMENT, a copy of which is included with the +GtkRadiant sources (see LICENSE_ID). If you did not receive a copy of +LICENSE_ID, please contact Id Software immediately at info@idsoftware.com. + +All changes and additions to the original source which have been developed by +other contributors (see CONTRIBUTORS) are provided under the terms of the +license the contributors choose (see LICENSE), to the extent permitted by the +LICENSE_ID. If you did not receive a copy of the contributor license, +please contact the GtkRadiant maintainers at info@gtkradiant.com immediately. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// +// start of shared cmdlib stuff +// + +#ifndef __CMDLIB__ +#define __CMDLIB__ + +#include + + +// TTimo started adding portability code: +// return true if spawning was successful, false otherwise +// on win32 we have a bCreateConsole flag to create a new console or run inside the current one +//boolean Q_Exec(const char* pCmd, boolean bCreateConsole); +// execute a system command: +// cmd: the command to run +// cmdline: the command line +// NOTE TTimo following are win32 specific: +// execdir: the directory to execute in +// bCreateConsole: spawn a new console or not +// return values; +// if the spawn was fine +// TODO TTimo add functionality to track the process until it dies + +bool Q_Exec(const char *cmd, char *cmdline, const char *execdir, bool bCreateConsole); + +// some easy portability crap + + +#define access_owner_read 0400 +#define access_owner_write 0200 +#define access_owner_execute 0100 +#define access_owner_rw_ 0600 +#define access_owner_r_x 0500 +#define access_owner__wx 0300 +#define access_owner_rwx 0700 + +#define access_group_read 0040 +#define access_group_write 0020 +#define access_group_execute 0010 +#define access_group_rw_ 0060 +#define access_group_r_x 0050 +#define access_group__wx 0030 +#define access_group_rwx 0070 + +#define access_others_read 0004 +#define access_others_write 0002 +#define access_others_execute 0001 +#define access_others_rw_ 0006 +#define access_others_r_x 0005 +#define access_others__wx 0003 +#define access_others_rwx 0007 + + +#define access_rwxrwxr_x (access_owner_rwx | access_group_rwx | access_others_r_x) +#define access_rwxrwxrwx (access_owner_rwx | access_group_rwx | access_others_rwx) + +// Q_mkdir +// returns true if succeeded in creating directory +#ifdef WIN32 +#include +inline bool Q_mkdir(const char* name) +{ + return _mkdir(name) != -1; +} +#else +#include +inline bool Q_mkdir(const char* name) +{ + return mkdir(name, access_rwxrwxr_x) != -1; +} +#endif + + +inline double Sys_DoubleTime(void) +{ + return clock()/ 1000.0; +} + + + +#endif diff --git a/tools/urt/libs/cmdlib/.cvsignore b/tools/urt/libs/cmdlib/.cvsignore new file mode 100644 index 00000000..b5ee5ae1 --- /dev/null +++ b/tools/urt/libs/cmdlib/.cvsignore @@ -0,0 +1 @@ +Debug Release *.ncb *.opt *.plg *.001 *.BAK \ No newline at end of file diff --git a/tools/urt/libs/cmdlib/.cvswrappers b/tools/urt/libs/cmdlib/.cvswrappers new file mode 100644 index 00000000..cdfd6d4a --- /dev/null +++ b/tools/urt/libs/cmdlib/.cvswrappers @@ -0,0 +1,3 @@ +*.dsp -m 'COPY' -k 'b' +*.dsw -m 'COPY' -k 'b' +*.scc -m 'COPY' -k 'b' diff --git a/tools/urt/libs/cmdlib/cmdlib.cpp b/tools/urt/libs/cmdlib/cmdlib.cpp new file mode 100644 index 00000000..1bcaafa2 --- /dev/null +++ b/tools/urt/libs/cmdlib/cmdlib.cpp @@ -0,0 +1,133 @@ +/* +This code is based on source provided under the terms of the Id Software +LIMITED USE SOFTWARE LICENSE AGREEMENT, a copy of which is included with the +GtkRadiant sources (see LICENSE_ID). If you did not receive a copy of +LICENSE_ID, please contact Id Software immediately at info@idsoftware.com. + +All changes and additions to the original source which have been developed by +other contributors (see CONTRIBUTORS) are provided under the terms of the +license the contributors choose (see LICENSE), to the extent permitted by the +LICENSE_ID. If you did not receive a copy of the contributor license, +please contact the GtkRadiant maintainers at info@gtkradiant.com immediately. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// +// start of shared cmdlib stuff +// + +#include "cmdlib.h" + +#include +#include + +#include "string/string.h" +#include "os/path.h" +#include "container/array.h" + +#ifdef WIN32 + #include +#endif +#if defined (__linux__) || defined (__APPLE__) + #include +#endif + + +#if defined (__linux__) || defined (__APPLE__) +bool Q_Exec(const char *cmd, char *cmdline, const char *, bool) +{ + char fullcmd[2048]; + char *pCmd; +#ifdef _DEBUG + printf("Q_Exec damnit\n"); +#endif + switch (fork()) + { + case -1: + return true; + break; + case 0: + // always concat the command on linux + if (cmd) + { + strcpy(fullcmd, cmd); + } + else + fullcmd[0] = '\0'; + if (cmdline) + { + strcat(fullcmd, " "); + strcat(fullcmd, cmdline); + } + pCmd = fullcmd; + while (*pCmd == ' ') + pCmd++; +#ifdef _DEBUG + printf("Running system...\n"); + printf("Command: %s\n", pCmd); +#endif + system( pCmd ); +#ifdef _DEBUG + printf ("system() returned\n"); +#endif + _exit (0); + break; + } + return true; +} +#endif + +#ifdef WIN32 +// NOTE TTimo windows is VERY nitpicky about the syntax in CreateProcess +bool Q_Exec(const char *cmd, char *cmdline, const char *execdir, bool bCreateConsole) +{ + PROCESS_INFORMATION ProcessInformation; + STARTUPINFO startupinfo = {0}; + DWORD dwCreationFlags; + GetStartupInfo (&startupinfo); + if (bCreateConsole) + dwCreationFlags = CREATE_NEW_CONSOLE | NORMAL_PRIORITY_CLASS; + else + dwCreationFlags = DETACHED_PROCESS | NORMAL_PRIORITY_CLASS; + const char *pCmd; + char *pCmdline; + pCmd = cmd; + if (pCmd) + { + while (*pCmd == ' ') + pCmd++; + } + pCmdline = cmdline; + if (pCmdline) + { + while (*pCmdline == ' ') + pCmdline++; + } + + if (CreateProcess( + pCmd, + pCmdline, + NULL, + NULL, + FALSE, + dwCreationFlags, + NULL, + execdir, + &startupinfo, + &ProcessInformation + )) + return true; + return false; +} +#endif + diff --git a/tools/urt/libs/cmdlib/cmdlib.dsp b/tools/urt/libs/cmdlib/cmdlib.dsp new file mode 100644 index 00000000..ee229aa5 --- /dev/null +++ b/tools/urt/libs/cmdlib/cmdlib.dsp @@ -0,0 +1,101 @@ +# Microsoft Developer Studio Project File - Name="cmdlib" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=cmdlib - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "cmdlib.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "cmdlib.mak" CFG="cmdlib - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "cmdlib - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "cmdlib - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "cmdlib" +# PROP Scc_LocalPath ".." +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "cmdlib - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +MTL=midl.exe +F90=df.exe +# ADD BASE F90 /include:"Release/" +# ADD F90 /include:"Release/" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /O2 /I "..\\" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 +# ADD RSC /l 0x409 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "cmdlib - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +MTL=midl.exe +F90=df.exe +# ADD BASE F90 /include:"Debug/" +# ADD F90 /include:"Debug/" +# ADD BASE CPP /nologo /W3 /GX /Z7 /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /MDd /W3 /Z7 /Od /I "..\\" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c +# ADD BASE RSC /l 0x409 +# ADD RSC /l 0x409 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "cmdlib - Win32 Release" +# Name "cmdlib - Win32 Debug" +# Begin Source File + +SOURCE=.\CMDLIB.cpp +# End Source File +# Begin Source File + +SOURCE=..\cmdlib.h +# End Source File +# End Target +# End Project diff --git a/tools/urt/libs/cmdlib/cmdlib.vcproj b/tools/urt/libs/cmdlib/cmdlib.vcproj new file mode 100644 index 00000000..2bd36f28 --- /dev/null +++ b/tools/urt/libs/cmdlib/cmdlib.vcproj @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/urt/libs/container/array.cpp b/tools/urt/libs/container/array.cpp new file mode 100644 index 00000000..a34630ff --- /dev/null +++ b/tools/urt/libs/container/array.cpp @@ -0,0 +1,19 @@ + +#include "array.h" + +namespace +{ + class Bleh + { + Array m_array; + public: + Bleh() : m_array(16) + { + } + }; + + void testAutoArray() + { + Array array(32); + } +} \ No newline at end of file diff --git a/tools/urt/libs/container/array.h b/tools/urt/libs/container/array.h new file mode 100644 index 00000000..08236fac --- /dev/null +++ b/tools/urt/libs/container/array.h @@ -0,0 +1,170 @@ + +#if !defined(INCLUDED_CONTAINER_ARRAY_H) +#define INCLUDED_CONTAINER_ARRAY_H + +#include +#include + +#include "memory/allocator.h" + +/// \brief An array whose size is variable at run-time. +/// +/// - Resizing the array destroys all the existing elements and invalidates all iterators. +/// - Default-Constructible, Copyable, Assignable. +/// - Compatible with the containers and algorithms in the Standard Template Library (STL) - http://www.sgi.com/tech/stl/ +/// +/// \param Element The type to be stored in the array. Must provide a default-constructor and a copy-constructor. +/// \param Allocator A custom memory-allocator, conforming to the std::allocator interface. +template > +class Array : public Allocator +{ + std::size_t m_size; + Element* m_data; + + Element* construct(std::size_t size) + { +#if 1 + return New(*this).vector(size); +#else + return new Element[size]; +#endif + } + template + Element* construct(std::size_t size, const T1& value) + { + return New(*this).vector(size, value); + } + void destroy(Element* data, std::size_t size) + { +#if 1 + Delete(*this).vector(data, size); +#else + delete[] data; +#endif + } + +public: + typedef Element value_type; + typedef value_type* iterator; + typedef const value_type* const_iterator; + + Array() + : m_size(0), m_data(0) + { + } + Array(std::size_t size) + : m_size(size), m_data(construct(size)) + { + } + template + Array(std::size_t size, const T1& value) + : m_size(size), m_data(construct(size, value)) + { + } + Array(const Array& other) + : Allocator(other), m_size(other.size()), m_data(construct(m_size)) + { + std::copy(other.begin(), other.end(), begin()); + } + template + Array(Iterator start, Iterator finish) + : m_size(std::distance(start, finish)), m_data(construct(m_size)) + { + std::copy(start, finish, begin()); + } + ~Array() + { + destroy(m_data, m_size); + } + + Array& operator=(const Array& other) + { + Array temp(other); + temp.swap(*this); + return *this; + } + + void swap(Array& other) + { + std::swap(m_size, other.m_size); + std::swap(m_data, other.m_data); + } + + iterator begin() + { + return m_data; + } + const_iterator begin() const + { + return m_data; + } + iterator end() + { + return m_data + m_size; + } + const_iterator end() const + { + return m_data + m_size; + } + + value_type& operator[](std::size_t index) + { +#if defined(_DEBUG) + ASSERT_MESSAGE(index < size(), "array index out of bounds"); +#endif + return m_data[index]; + } + const value_type& operator[](std::size_t index) const + { +#if defined(_DEBUG) + ASSERT_MESSAGE(index < size(), "array index out of bounds"); +#endif + return m_data[index]; + } + value_type* data() + { + return m_data; + } + const value_type* data() const + { + return m_data; + } + std::size_t size() const + { + return m_size; + } + bool empty() const + { + return m_size == 0; + } + + void resize(std::size_t count) + { + if(count != size()) + { + Array temp(count); + temp.swap(*this); + } + } + void resize(std::size_t count, const value_type& value) + { + if(count != size()) + { + Array temp(count, value); + temp.swap(*this); + } + } +}; + +namespace std +{ + /// \brief Swaps the values of \p self and \p other. + /// Overloads std::swap. + template + inline void swap(Array& self, Array& other) + { + self.swap(other); + } +} + +#endif diff --git a/tools/urt/libs/container/cache.cpp b/tools/urt/libs/container/cache.cpp new file mode 100644 index 00000000..e7fb2cc5 --- /dev/null +++ b/tools/urt/libs/container/cache.cpp @@ -0,0 +1,2 @@ + +#include "cache.h" diff --git a/tools/urt/libs/container/cache.h b/tools/urt/libs/container/cache.h new file mode 100644 index 00000000..f2d35245 --- /dev/null +++ b/tools/urt/libs/container/cache.h @@ -0,0 +1,177 @@ + +#if !defined(INCLUDED_CONTAINER_CACHE_H) +#define INCLUDED_CONTAINER_CACHE_H + +#include +#include "container/hashtable.h" +#include "memory/allocator.h" + +template +class DefaultCreationPolicy +{ +public: + Type* construct(const Parameter& parameter) + { + return New().scalar(parameter); + } + void destroy(Type* p) + { + Delete().scalar(p); + } +}; + +template +class SharedValue +{ + typedef Type value_type; + typedef value_type* pointer; + typedef value_type& reference; + + std::size_t m_count; + pointer m_value; + +public: + SharedValue() + : m_count(0), m_value(0) + { + } + ~SharedValue() + { + ASSERT_MESSAGE(m_count == 0 , "destroying a referenced object\n"); + } + void set(pointer value) + { + m_value = value; + } + pointer get() + { + return m_value; + } + std::size_t increment() + { + return ++m_count; + } + std::size_t decrement() + { + ASSERT_MESSAGE(!empty(), "destroying a non-existent object\n"); + return --m_count; + } + std::size_t count() + { + return m_count; + } + bool empty() + { + return m_count == 0; + } + reference operator*() const + { + ASSERT_NOTNULL(m_value); + return *m_value; + } + pointer operator->() const + { + return &(operator*()); + } +}; + + + +/// \brief Caches values that are uniquely identified by a key. +/// +/// - Automatically removes objects that are no longer referenced. +/// +/// \param Key Uniquely identifies each element. +/// \param Cached The type to be cached. Must define a constructor that accepts \c Key. +template, typename CreationPolicy = DefaultCreationPolicy > +class HashedCache : public CreationPolicy +{ + typedef SharedValue Element; + typedef HashTable map_type; + + map_type m_map; + +public: + explicit HashedCache(const CreationPolicy& creation = CreationPolicy()) + : CreationPolicy(creation), m_map(256) + { + } + ~HashedCache() + { + ASSERT_MESSAGE(empty(), "HashedCache::~HashedCache: not empty"); + } + + typedef typename map_type::iterator iterator; + typedef typename map_type::value_type value_type; + + iterator begin() + { + return m_map.begin(); + } + iterator end() + { + return m_map.end(); + } + + bool empty() const + { + return m_map.empty(); + } + + iterator find(const Key& key) + { + return m_map.find(key); + } + + void capture(iterator i) + { + (*i).value.increment(); + } + void release(iterator i) + { + if((*i).value.decrement() == 0) + { + CreationPolicy::destroy((*i).value.get()); + m_map.erase(i); + } + } + +#if 1 + Element& capture(const Key& key) + { + Element& elem = m_map[key]; + if(elem.increment() == 1) + { + elem.set(CreationPolicy::construct(key)); + } + return elem; + } +#else + value_type& capture(const Key& key) + { + iterator i = m_map.find(key); + if(i == m_map.end()) + { + Element element; + element.set(CreationPolicy::construct(key)); + i = m_map.insert(key, element); + } + (*i).value.increment(); + return (*i); + } +#endif + void release(const Key& key) + { + iterator i = m_map.find(key); + ASSERT_MESSAGE(i != m_map.end(), "releasing a non-existent object\n"); + release(i); + } + + void clear() + { + m_map.clear(); + } +}; + + +#endif diff --git a/tools/urt/libs/container/container.cpp b/tools/urt/libs/container/container.cpp new file mode 100644 index 00000000..6cf60ed4 --- /dev/null +++ b/tools/urt/libs/container/container.cpp @@ -0,0 +1,3 @@ + +#include "container.h" + diff --git a/tools/urt/libs/container/container.h b/tools/urt/libs/container/container.h new file mode 100644 index 00000000..73ebc0a2 --- /dev/null +++ b/tools/urt/libs/container/container.h @@ -0,0 +1,326 @@ + +#if !defined(INCLUDED_CONTAINER_CONTAINER_H) +#define INCLUDED_CONTAINER_CONTAINER_H + +#include +#include + +#include "generic/static.h" + +/// \brief A single-value container, which can either be empty or full. +template +class Single +{ + Type* m_value; +public: + Single() : m_value(0) + { + } + bool empty() + { + return m_value == 0; + } + Type* insert(const Type& other) + { + m_value = new Type(other); + return m_value; + } + void clear() + { + delete m_value; + m_value = 0; + } + Type& get() + { + //ASSERT_MESSAGE(!empty(), "Single: must be initialised before being accessed"); + return *m_value; + } + const Type& get() const + { + //ASSERT_MESSAGE(!empty(), "Single: must be initialised before being accessed"); + return *m_value; + } +}; + + +/// \brief An adaptor to make std::list into a Unique Sequence - which cannot contain the same value more than once. +/// \param Value Uniquely identifies itself. Must provide a copy-constructor and an equality operator. +template +class UnsortedSet +{ + typedef typename std::list Values; + Values m_values; +public: + typedef typename Values::iterator iterator; + typedef typename Values::const_iterator const_iterator; + typedef typename Values::reverse_iterator reverse_iterator; + typedef typename Values::const_reverse_iterator const_reverse_iterator; + + iterator begin() + { + return m_values.begin(); + } + const_iterator begin() const + { + return m_values.begin(); + } + iterator end() + { + return m_values.end(); + } + const_iterator end() const + { + return m_values.end(); + } + reverse_iterator rbegin() + { + return m_values.rbegin(); + } + const_reverse_iterator rbegin() const + { + return m_values.rbegin(); + } + reverse_iterator rend() + { + return m_values.rend(); + } + const_reverse_iterator rend() const + { + return m_values.rend(); + } + + bool empty() const + { + return m_values.empty(); + } + std::size_t size() const + { + return m_values.size(); + } + void clear() + { + m_values.clear(); + } + + void swap(UnsortedSet& other) + { + std::swap(m_values, other.m_values); + } + iterator insert(const Value& value) + { + ASSERT_MESSAGE(find(value) == end(), "UnsortedSet::insert: already added"); + m_values.push_back(value); + return --end(); + } + void erase(const Value& value) + { + iterator i = find(value); + ASSERT_MESSAGE(i != end(), "UnsortedSet::erase: not found"); + m_values.erase(i); + } + iterator find(const Value& value) + { + return std::find(begin(), end(), value); + } +}; + +namespace std +{ + /// \brief Swaps the values of \p self and \p other. + /// Overloads std::swap. + template + inline void swap(UnsortedSet& self, UnsortedSet& other) + { + self.swap(other); + } +} + +/// An adaptor to make std::list into a Unique Associative Sequence - which cannot contain the same value more than once. +/// Key: Uniquely identifies a value. Must provide a copy-constructor and an equality operator. +/// Value: Must provide a copy-constructor. +template +class UnsortedMap +{ + typedef typename std::list< std::pair > Values; + Values m_values; +public: + typedef typename Values::value_type value_type; + typedef typename Values::iterator iterator; + typedef typename Values::const_iterator const_iterator; + + iterator begin() + { + return m_values.begin(); + } + const_iterator begin() const + { + return m_values.begin(); + } + iterator end() + { + return m_values.end(); + } + const_iterator end() const + { + return m_values.end(); + } + + bool empty() const + { + return m_values.empty(); + } + std::size_t size() const + { + return m_values.size(); + } + void clear() + { + m_values.clear(); + } + + iterator insert(const value_type& value) + { + ASSERT_MESSAGE(find(value.first) == end(), "UnsortedMap::insert: already added"); + m_values.push_back(value); + return --m_values.end(); + } + void erase(const Key& key) + { + iterator i = find(key); + ASSERT_MESSAGE(i != end(), "UnsortedMap::erase: not found"); + erase(i); + } + void erase(iterator i) + { + m_values.erase(i); + } + iterator find(const Key& key) + { + for(iterator i = m_values.begin(); i != m_values.end(); ++i) + { + if((*i).first == key) + { + return i; + } + } + return m_values.end(); + } + const_iterator find(const Key& key) const + { + for(const_iterator i = m_values.begin(); i != m_values.end(); ++i) + { + if((*i).first == key) + { + return i; + } + } + return m_values.end(); + } + + Value& operator[](const Key& key) + { + iterator i = find(key); + if(i != end()) + { + return (*i).second; + } + + m_values.push_back(Values::value_type(key, Value())); + return m_values.back().second; + } +}; + +/// An adaptor to assert when duplicate values are added, or non-existent values removed from a std::set. +template +class UniqueSet +{ + typedef std::set Values; + Values m_values; +public: + typedef typename Values::iterator iterator; + typedef typename Values::const_iterator const_iterator; + typedef typename Values::reverse_iterator reverse_iterator; + typedef typename Values::const_reverse_iterator const_reverse_iterator; + + + iterator begin() + { + return m_values.begin(); + } + const_iterator begin() const + { + return m_values.begin(); + } + iterator end() + { + return m_values.end(); + } + const_iterator end() const + { + return m_values.end(); + } + reverse_iterator rbegin() + { + return m_values.rbegin(); + } + const_reverse_iterator rbegin() const + { + return m_values.rbegin(); + } + reverse_iterator rend() + { + return m_values.rend(); + } + const_reverse_iterator rend() const + { + return m_values.rend(); + } + + bool empty() const + { + return m_values.empty(); + } + std::size_t size() const + { + return m_values.size(); + } + void clear() + { + m_values.clear(); + } + + void swap(UniqueSet& other) + { + std::swap(m_values, other.m_values); + } + iterator insert(const Value& value) + { + std::pair result = m_values.insert(value); + ASSERT_MESSAGE(result.second, "UniqueSet::insert: already added"); + return result.first; + } + void erase(const Value& value) + { + iterator i = find(value); + ASSERT_MESSAGE(i != end(), "UniqueSet::erase: not found"); + m_values.erase(i); + } + iterator find(const Value& value) + { + return std::find(begin(), end(), value); + } +}; + +namespace std +{ + /// \brief Swaps the values of \p self and \p other. + /// Overloads std::swap. + template + inline void swap(UniqueSet& self, UniqueSet& other) + { + self.swap(other); + } +} + + +#endif diff --git a/tools/urt/libs/container/hashfunc.cpp b/tools/urt/libs/container/hashfunc.cpp new file mode 100644 index 00000000..712de39a --- /dev/null +++ b/tools/urt/libs/container/hashfunc.cpp @@ -0,0 +1,2 @@ + +#include "hashfunc.h" diff --git a/tools/urt/libs/container/hashfunc.h b/tools/urt/libs/container/hashfunc.h new file mode 100644 index 00000000..37f18d6b --- /dev/null +++ b/tools/urt/libs/container/hashfunc.h @@ -0,0 +1,413 @@ + +#if !defined(INCLUDED_CONTAINER_HASHFUNC_H) +#define INCLUDED_CONTAINER_HASHFUNC_H + +/* +-------------------------------------------------------------------- +lookup2.c, by Bob Jenkins, December 1996, Public Domain. +hash(), hash2(), hash3, and mix() are externally useful functions. +Routines to test the hash are included if SELF_TEST is defined. +You can use this free for any purpose. It has no warranty. +-------------------------------------------------------------------- +*/ + +#include +#include "string/string.h" +#include "container/array.h" +typedef unsigned long int ub4; /* unsigned 4-byte quantities */ +typedef unsigned char ub1; + +inline ub1 ub1_as_ub1_nocase(ub1 byte) +{ + return std::tolower(byte); +} + +inline ub4 ub1x4_as_ub4_nocase(const ub1 bytes[4]) +{ + ub4 result; + reinterpret_cast(&result)[0] = ub1_as_ub1_nocase(bytes[0]); + reinterpret_cast(&result)[1] = ub1_as_ub1_nocase(bytes[1]); + reinterpret_cast(&result)[2] = ub1_as_ub1_nocase(bytes[2]); + reinterpret_cast(&result)[3] = ub1_as_ub1_nocase(bytes[3]); + return result; +} + +class ub1_default_traits +{ +public: + static ub1 as_ub1(ub1 byte) + { + return byte; + } +}; + +class ub1_nocase_traits +{ +public: + static ub1 as_ub1(ub1 byte) + { + return ub1_as_ub1_nocase(byte); + } +}; + +class ub1x4_default_traits +{ +public: + static ub4 as_ub4(const ub1 bytes[4]) + { + return *reinterpret_cast(bytes); + } +}; + +class ub1x4_nocase_traits +{ +public: + static ub4 as_ub4(const ub1 bytes[4]) + { + return ub1x4_as_ub4_nocase(bytes); + } +}; + +class ub4_default_traits +{ +public: + static ub4 as_ub4(ub4 i) + { + return i; + } +}; + +class ub4_nocase_traits +{ +public: + static ub4 as_ub4(ub4 i) + { + return ub1x4_as_ub4_nocase(reinterpret_cast(&i)); + } +}; + +#define hashsize(n) ((ub4)1<<(n)) +#define hashmask(n) (hashsize(n)-1) + +/* +-------------------------------------------------------------------- +mix -- mix 3 32-bit values reversibly. +For every delta with one or two bit set, and the deltas of all three + high bits or all three low bits, whether the original value of a,b,c + is almost all zero or is uniformly distributed, +* If mix() is run forward or backward, at least 32 bits in a,b,c + have at least 1/4 probability of changing. +* If mix() is run forward, every bit of c will change between 1/3 and + 2/3 of the time. (Well, 22/100 and 78/100 for some 2-bit deltas.) +mix() was built out of 36 single-cycle latency instructions in a + structure that could supported 2x parallelism, like so: + a -= b; + a -= c; x = (c>>13); + b -= c; a ^= x; + b -= a; x = (a<<8); + c -= a; b ^= x; + c -= b; x = (b>>13); + ... + Unfortunately, superscalar Pentiums and Sparcs can't take advantage + of that parallelism. They've also turned some of those single-cycle + latency instructions into multi-cycle latency instructions. Still, + this is the fastest good hash I could find. There were about 2^^68 + to choose from. I only looked at a billion or so. +-------------------------------------------------------------------- +*/ +#define mix(a,b,c) \ +{ \ + a -= b; a -= c; a ^= (c>>13); \ + b -= c; b -= a; b ^= (a<<8); \ + c -= a; c -= b; c ^= (b>>13); \ + a -= b; a -= c; a ^= (c>>12); \ + b -= c; b -= a; b ^= (a<<16); \ + c -= a; c -= b; c ^= (b>>5); \ + a -= b; a -= c; a ^= (c>>3); \ + b -= c; b -= a; b ^= (a<<10); \ + c -= a; c -= b; c ^= (b>>15); \ +} + +/* same, but slower, works on systems that might have 8 byte ub4's */ +#define mix2(a,b,c) \ +{ \ + a -= b; a -= c; a ^= (c>>13); \ + b -= c; b -= a; b ^= (a<< 8); \ + c -= a; c -= b; c ^= ((b&0xffffffff)>>13); \ + a -= b; a -= c; a ^= ((c&0xffffffff)>>12); \ + b -= c; b -= a; b = (b ^ (a<<16)) & 0xffffffff; \ + c -= a; c -= b; c = (c ^ (b>> 5)) & 0xffffffff; \ + a -= b; a -= c; a = (a ^ (c>> 3)) & 0xffffffff; \ + b -= c; b -= a; b = (b ^ (a<<10)) & 0xffffffff; \ + c -= a; c -= b; c = (c ^ (b>>15)) & 0xffffffff; \ +} + +/* +-------------------------------------------------------------------- +hash() -- hash a variable-length key into a 32-bit value + k : the key (the unaligned variable-length array of bytes) + len : the length of the key, counting by bytes + level : can be any 4-byte value +Returns a 32-bit value. Every bit of the key affects every bit of +the return value. Every 1-bit and 2-bit delta achieves avalanche. +About 36+6len instructions. + +The best hash table sizes are powers of 2. There is no need to do +mod a prime (mod is sooo slow!). If you need less than 32 bits, +use a bitmask. For example, if you need only 10 bits, do + h = (h & hashmask(10)); +In which case, the hash table should have hashsize(10) elements. + +If you are hashing n strings (ub1 **)k, do it like this: + for (i=0, h=0; i +inline ub4 hash( +const ub1 *k, /* the key */ +ub4 length, /* the length of the key */ +ub4 initval, /* the previous hash, or an arbitrary value */ +const UB1Traits& ub1traits, +const UB4x1Traits& ub4x1traits +) +{ + register ub4 a,b,c,len; + + /* Set up the internal state */ + len = length; + a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */ + c = initval; /* the previous hash value */ + + /*---------------------------------------- handle most of the key */ + while (len >= 12) + { + a += (k[0] +((ub4)UB1Traits::as_ub1(k[1])<<8) +((ub4)UB1Traits::as_ub1(k[2])<<16) +((ub4)UB1Traits::as_ub1(k[3])<<24)); + b += (k[4] +((ub4)UB1Traits::as_ub1(k[5])<<8) +((ub4)UB1Traits::as_ub1(k[6])<<16) +((ub4)UB1Traits::as_ub1(k[7])<<24)); + c += (k[8] +((ub4)UB1Traits::as_ub1(k[9])<<8) +((ub4)UB1Traits::as_ub1(k[10])<<16)+((ub4)UB1Traits::as_ub1(k[11])<<24)); + mix(a,b,c); + k += 12; len -= 12; + } + + /*------------------------------------- handle the last 11 bytes */ + c += length; + switch(len) /* all the case statements fall through */ + { + case 11: c += ((ub4)UB1Traits::as_ub1(k[10]) << 24); + case 10: c += ((ub4)UB1Traits::as_ub1(k[9]) << 16); + case 9 : c += ((ub4)UB1Traits::as_ub1(k[8]) << 8); + /* the first byte of c is reserved for the length */ + case 8 : b += ((ub4)UB1Traits::as_ub1(k[7]) << 24); + case 7 : b += ((ub4)UB1Traits::as_ub1(k[6]) << 16); + case 6 : b += ((ub4)UB1Traits::as_ub1(k[5]) << 8); + case 5 : b += UB1Traits::as_ub1(k[4]); + case 4 : a += ((ub4)UB1Traits::as_ub1(k[3]) << 24); + case 3 : a += ((ub4)UB1Traits::as_ub1(k[2]) << 16); + case 2 : a += ((ub4)UB1Traits::as_ub1(k[1]) << 8); + case 1 : a += UB1Traits::as_ub1(k[0]); + /* case 0: nothing left to add */ + } + mix(a,b,c); + /*-------------------------------------------- report the result */ + return c; +} + +/* +-------------------------------------------------------------------- + This works on all machines. hash2() is identical to hash() on + little-endian machines, except that the length has to be measured + in ub4s instead of bytes. It is much faster than hash(). It + requires + -- that the key be an array of ub4's, and + -- that all your machines have the same endianness, and + -- that the length be the number of ub4's in the key +-------------------------------------------------------------------- +*/ +template +inline ub4 hash2( +const ub4 *k, /* the key */ +ub4 length, /* the length of the key, in ub4s */ +ub4 initval, /* the previous hash, or an arbitrary value */ +const UB4Traits& ub4traits +) +{ + register ub4 a,b,c,len; + + /* Set up the internal state */ + len = length; + a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */ + c = initval; /* the previous hash value */ + + /*---------------------------------------- handle most of the key */ + while (len >= 3) + { + a += UB4Traits::as_ub4(k[0]); + b += UB4Traits::as_ub4(k[1]); + c += UB4Traits::as_ub4(k[2]); + mix(a,b,c); + k += 3; len -= 3; + } + + /*-------------------------------------- handle the last 2 ub4's */ + c += length; + switch(len) /* all the case statements fall through */ + { + /* c is reserved for the length */ + case 2 : b += UB4Traits::as_ub4(k[1]); + case 1 : a += UB4Traits::as_ub4(k[0]); + /* case 0: nothing left to add */ + } + mix(a,b,c); + /*-------------------------------------------- report the result */ + return c; +} + +typedef ub4 hash_t; + +inline hash_t hash_ub1(const ub1* key, std::size_t len, hash_t previous = 0) +{ + return hash(key, ub4(len), previous, ub1_default_traits(), ub1x4_default_traits()); +} + +inline hash_t hash_ub1_nocase(const ub1* key, std::size_t len, hash_t previous = 0) +{ + return hash(key, ub4(len), previous, ub1_nocase_traits(), ub1x4_nocase_traits()); +} + +template +inline hash_t hash_ub4(const ub4* key, std::size_t len, const UB4Traits& traits, hash_t previous = 0) +{ + return hash2(key,ub4(len), previous, traits); +} + +inline ub4 hash_combine(ub4 left, ub4 right) +{ + return hash_ub1(reinterpret_cast(&left), 4, right); +} + +template +inline hash_t pod_hash(const POD& pod) +{ + return hash_ub1(reinterpret_cast(&pod), sizeof(POD)); +} + +inline hash_t string_hash(const char* string, hash_t previous = 0) +{ + return hash_ub1(reinterpret_cast(string), string_length(string), previous); +} + +inline hash_t string_hash_nocase(const char* string, hash_t previous = 0) +{ + return hash_ub1_nocase(reinterpret_cast(string), string_length(string), previous); +} + +struct HashString +{ + typedef hash_t hash_type; + hash_type operator()(const CopiedString& string) const + { + return string_hash(string.c_str()); + } +}; + +struct HashStringNoCase +{ + typedef hash_t hash_type; + hash_type operator()(const CopiedString& string) const + { + return string_hash_nocase(string.c_str()); + } +}; + +/// \brief Length of a string in ub4. +/// "wibble" (6) gives 2, +/// "and" (3) gives 1, +/// "bleh" (4) gives 2 +inline std::size_t string_length_ub4(const char* string) +{ + return ((string_length(string)>>2)+1)<<2; +} + +/// \brief Hashable key type that stores a string as an array of ub4 - making hashing faster. +/// Also caches the 32-bit result of the hash to speed up comparison of keys. +template +class HashKey +{ + Array m_key; + hash_t m_hash; + + void copy(const HashKey& other) + { + std::copy(other.m_key.begin(), other.m_key.end(), m_key.begin()); + m_hash = other.m_hash; + } + void copy(const char* string) + { + strncpy(reinterpret_cast(m_key.data()), string, m_key.size()); + for(Array::iterator i = m_key.begin(); i != m_key.end(); ++i) + { + *i = UB4Traits::as_ub4(*i); + } + m_hash = hash_ub4(m_key.data(), m_key.size(), ub4_default_traits()); + } + bool equal(const HashKey& other) const + { + return m_hash == other.m_hash && m_key.size() == other.m_key.size() + && std::equal(m_key.begin(), m_key.end(), other.m_key.begin()); + } + +public: + HashKey(const HashKey& other) : m_key(other.m_key.size()) + { + copy(other); + } + HashKey(const char* string) : m_key(string_length_ub4(string)) + { + copy(string); + } + HashKey& operator=(const char* string) + { + m_key.resize(string_length_ub4(string)); + copy(string); + return *this; + } + bool operator==(const HashKey& other) const + { + return equal(other); + } + bool operator!=(const HashKey& other) const + { + return !equal(other); + } + hash_t hash() const + { + return m_hash; + } +#if 0 + const char* c_str() const + { + return reinterpret_cast(m_key.data()); + } +#endif +}; + +/// \brief Hash function to use with HashKey. +struct HashKeyHasher +{ + typedef hash_t hash_type; + hash_type operator()(const HashKey& key) const + { + return key.hash(); + } +}; + + + +#endif diff --git a/tools/urt/libs/container/hashtable.cpp b/tools/urt/libs/container/hashtable.cpp new file mode 100644 index 00000000..bcad750d --- /dev/null +++ b/tools/urt/libs/container/hashtable.cpp @@ -0,0 +1,45 @@ + +#include "hashtable.h" + +#if defined(_DEBUG) || defined(DOXYGEN) + +#include "hashfunc.h" + +namespace ExampleHashTable +{ + void testStuff() + { + // HashTable example + typedef HashTable MyHashTable; + MyHashTable hashtable; + hashtable["bleh"] = 5; + hashtable.insert("blah", 17); + hashtable["foo"] = 99; + hashtable.insert("bar", 23); + + int bleh = (*hashtable.find("bleh")).value; // 5 + int blah = hashtable["blah"]; // 17 + hashtable.erase("foo"); + MyHashTable::iterator barIter = hashtable.find("bar"); + hashtable.erase(barIter); + + for(MyHashTable::iterator i = hashtable.begin(); i != hashtable.end(); ++i) + { + if((*i).key != "bleh") + { + ++hashtable["count"]; // insertion does not invalidate iterators + } + } + // end example + } + + struct Always + { + Always() + { + testStuff(); + } + } always; +} + +#endif diff --git a/tools/urt/libs/container/hashtable.h b/tools/urt/libs/container/hashtable.h new file mode 100644 index 00000000..7b5cc15a --- /dev/null +++ b/tools/urt/libs/container/hashtable.h @@ -0,0 +1,455 @@ + +#if !defined(INCLUDED_CONTAINER_HASHTABLE_H) +#define INCLUDED_CONTAINER_HASHTABLE_H + +#include +#include +#include +#include "debugging/debugging.h" + + +namespace HashTableDetail +{ + inline std::size_t next_power_of_two(std::size_t size) + { + std::size_t result = 1; + while(result < size) + { + result <<= 1; + } + return result; + } + + struct BucketNodeBase + { + BucketNodeBase* next; + BucketNodeBase* prev; + }; + + inline void list_initialise(BucketNodeBase& self) + { + self.next = self.prev = &self; + } + + inline void list_swap(BucketNodeBase& self, BucketNodeBase& other) + { + BucketNodeBase tmp(self); + if(other.next == &other) + { + list_initialise(self); + } + else + { + self = other; + self.next->prev = self.prev->next = &self; + } + if(tmp.next == &self) + { + list_initialise(other); + } + else + { + other = tmp; + other.next->prev = other.prev->next = &other; + } + } + + inline void node_link(BucketNodeBase* node, BucketNodeBase* next) + { + node->next = next; + node->prev = next->prev; + next->prev = node; + node->prev->next = node; + } + inline void node_unlink(BucketNodeBase* node) + { + node->prev->next = node->next; + node->next->prev = node->prev; + } + + template + struct KeyValue + { + const Key key; + Value value; + + KeyValue(const Key& key_, const Value& value_) + : key(key_), value(value_) + { + } + }; + + template + struct BucketNode : public BucketNodeBase + { + Hash m_hash; + KeyValue m_value; + + BucketNode(Hash hash, const Key& key, const Value& value) + : m_hash(hash), m_value(key, value) + { + } + BucketNode* getNext() + { + return static_cast(next); + } + BucketNode* getPrev() + { + return static_cast(prev); + } + }; + + template + class BucketIterator + { + typedef BucketNode Node; + Node* m_node; + + void increment() + { + m_node = m_node->getNext(); + } + + public: + typedef std::forward_iterator_tag iterator_category; + typedef std::ptrdiff_t difference_type; + typedef difference_type distance_type; + typedef KeyValue value_type; + typedef value_type* pointer; + typedef value_type& reference; + + BucketIterator(Node* node) : m_node(node) + { + } + + Node* node() + { + return m_node; + } + + bool operator==(const BucketIterator& other) const + { + return m_node == other.m_node; + } + bool operator!=(const BucketIterator& other) const + { + return !operator==(other); + } + BucketIterator& operator++() + { + increment(); + return *this; + } + BucketIterator operator++(int) + { + BucketIterator tmp = *this; + increment(); + return tmp; + } + value_type& operator*() + { + return m_node->m_value; + } + value_type* operator->() + { + return &(operator*()); + } + }; +} + + +/// A hash-table container which maps keys to values. +/// +/// - Inserting or removing elements does not invalidate iterators. +/// - Inserting or retrieving an element for a given key takes O(1) time on average. +/// - Elements are stored in no particular order. +/// +/// \param Key Uniquely identifies a value. Must provide a copy-constructor. +/// \param Value The value to be stored . Must provide a default-constructor and a copy-constructor. +/// \param Hasher Must provide 'std::size_t operator()(const Key&) const' which always returns the same result if the same argument is given. +/// \param KeyEqual Must provide 'bool operator==(const Key&, const Key&) const' which returns true only if both arguments are equal. +/// +/// \dontinclude container/hashtable.cpp +/// \skipline HashTable example +/// \until end example +template > +class HashTable : private KeyEqual, private Hasher +{ + typedef typename Hasher::hash_type hash_type; + typedef HashTableDetail::KeyValue KeyValue; + typedef HashTableDetail::BucketNode BucketNode; + + inline BucketNode* node_create(hash_type hash, const Key& key, const Value& value) + { + return new BucketNode(hash, key, value); + } + inline void node_destroy(BucketNode* node) + { + delete node; + } + + typedef BucketNode* Bucket; + + static Bucket* buckets_new(std::size_t count) + { + Bucket* buckets = new Bucket[count]; + std::uninitialized_fill(buckets, buckets + count, Bucket(0)); + return buckets; + } + static void buckets_delete(Bucket* buckets) + { + delete[] buckets; + } + + std::size_t m_bucketCount; + Bucket* m_buckets; + std::size_t m_size; + HashTableDetail::BucketNodeBase m_list; + + BucketNode* getFirst() + { + return static_cast(m_list.next); + } + BucketNode* getLast() + { + return static_cast(&m_list); + } + +public: + + typedef KeyValue value_type; + typedef HashTableDetail::BucketIterator iterator; + +private: + + void initialise() + { + list_initialise(m_list); + } + hash_type hashKey(const Key& key) + { + return Hasher::operator()(key); + } + + std::size_t getBucketId(hash_type hash) const + { + return hash & (m_bucketCount - 1); + } + Bucket& getBucket(hash_type hash) + { + return m_buckets[getBucketId(hash)]; + } + BucketNode* bucket_find(Bucket bucket, hash_type hash, const Key& key) + { + std::size_t bucketId = getBucketId(hash); + for(iterator i(bucket); i != end(); ++i) + { + hash_type nodeHash = i.node()->m_hash; + + if(getBucketId(nodeHash) != bucketId) + { + return 0; + } + + if(nodeHash == hash && KeyEqual::operator()((*i).key, key)) + { + return i.node(); + } + } + return 0; + } + BucketNode* bucket_insert(Bucket& bucket, BucketNode* node) + { + // link node into list + node_link(node, bucket_next(bucket)); + bucket = node; + return node; + } + BucketNode* bucket_next(Bucket& bucket) + { + Bucket* end = m_buckets + m_bucketCount; + for(Bucket* i = &bucket; i != end; ++i) + { + if(*i != 0) + { + return *i; + } + } + return getLast(); + } + + void buckets_resize(std::size_t count) + { + BucketNode* first = getFirst(); + BucketNode* last = getLast(); + + buckets_delete(m_buckets); + + m_bucketCount = count; + + m_buckets = buckets_new(m_bucketCount); + initialise(); + + for(BucketNode* i = first; i != last;) + { + BucketNode* node = i; + i = i->getNext(); + bucket_insert(getBucket((*node).m_hash), node); + } + } + void size_increment() + { + if(m_size == m_bucketCount) + { + buckets_resize(m_bucketCount == 0 ? 8 : m_bucketCount << 1); + } + ++m_size; + } + void size_decrement() + { + --m_size; + } + + HashTable(const HashTable& other); + HashTable& operator=(const HashTable& other); +public: + HashTable() : m_bucketCount(0), m_buckets(0), m_size(0) + { + initialise(); + } + HashTable(std::size_t bucketCount) : m_bucketCount(HashTableDetail::next_power_of_two(bucketCount)), m_buckets(buckets_new(m_bucketCount)), m_size(0) + { + initialise(); + } + ~HashTable() + { + for(BucketNode* i = getFirst(); i != getLast();) + { + BucketNode* node = i; + i = i->getNext(); + node_destroy(node); + } + buckets_delete(m_buckets); + } + + iterator begin() + { + return iterator(getFirst()); + } + iterator end() + { + return iterator(getLast()); + } + + bool empty() const + { + return m_size == 0; + } + std::size_t size() const + { + return m_size; + } + + /// \brief Returns an iterator pointing to the value associated with \p key if it is contained by the hash-table, else \c end(). + iterator find(const Key& key) + { + hash_type hash = hashKey(key); + if(m_bucketCount != 0) + { + Bucket bucket = getBucket(hash); + if(bucket != 0) + { + BucketNode* node = bucket_find(bucket, hash, key); + if(node != 0) + { + return iterator(node); + } + } + } + + return end(); + } + /// \brief Adds \p value to the hash-table associated with \p key if it does not exist, or replaces the current value associated with \p key. + iterator insert(const Key& key, const Value& value) + { + hash_type hash = hashKey(key); + if(m_bucketCount != 0) + { + Bucket& bucket = getBucket(hash); + if(bucket != 0) + { + BucketNode* node = bucket_find(bucket, hash, key); + if(node != 0) + { + node->m_value.value = value; + return iterator(node); + } + } + } + + size_increment(); + return iterator(bucket_insert(getBucket(hash), node_create(hash, key, value))); + } + + /// \brief Removes the value pointed to by \p i from the hash-table. + /// + /// \p i must be a deferenceable iterator into the hash-table. + void erase(iterator i) + { + Bucket& bucket = getBucket(i.node()->m_hash); + BucketNode* node = i.node(); + + // if this was the last node in the bucket + if(bucket == node) + { + bucket = (node->getNext() == getLast() || &getBucket(node->getNext()->m_hash) != &bucket) ? 0 : node->getNext(); + } + + node_unlink(node); + ASSERT_MESSAGE(node != 0, "tried to erase a non-existent key/value"); + node_destroy(node); + + size_decrement(); + } + + /// \brief Returns the value identified by \p key if it is contained by the hash-table, else inserts and returns a new default-constructed value associated with \p key. + Value& operator[](const Key& key) + { + hash_type hash = hashKey(key); + if(m_bucketCount != 0) + { + Bucket& bucket = getBucket(hash); + if(bucket != 0) + { + BucketNode* node = bucket_find(bucket, hash, key); + if(node != 0) + { + return node->m_value.value; + } + } + } + size_increment(); + return bucket_insert(getBucket(hash), node_create(hash, key, Value()))->m_value.value; + } + /// \brief Removes the value associated with \p key from the hash-table. + void erase(const Key& key) + { + erase(find(key)); + } + /// \brief Swaps the contents of the hash-table with \p other. + void swap(HashTable& other) + { + std::swap(m_buckets, other.m_buckets); + std::swap(m_bucketCount, other.m_bucketCount); + std::swap(m_size, other.m_size); + HashTableDetail::list_swap(m_list, other.m_list); + } + /// \brief Removes all values from the hash-table. + void clear() + { + HashTable tmp; + tmp.swap(*this); + } +}; + +#endif diff --git a/tools/urt/libs/container/stack.cpp b/tools/urt/libs/container/stack.cpp new file mode 100644 index 00000000..8bfe65f7 --- /dev/null +++ b/tools/urt/libs/container/stack.cpp @@ -0,0 +1,3 @@ + +#include "stack.h" + diff --git a/tools/urt/libs/container/stack.h b/tools/urt/libs/container/stack.h new file mode 100644 index 00000000..73d9edb0 --- /dev/null +++ b/tools/urt/libs/container/stack.h @@ -0,0 +1,219 @@ + +#if !defined(INCLUDED_CONTAINER_STACK_H) +#define INCLUDED_CONTAINER_STACK_H + +#include "memory/allocator.h" +#include + +/// \brief A stack whose storage capacity is variable at run-time. Similar to std::vector. +/// +/// - Pushing or popping elements is a constant-time operation (on average). +/// - The storage capacity of the stack will grow when a new element is added beyond the current capacity. Iterators are invalidated when the storage capacity grows. +/// - DefaultConstructible, Copyable, Assignable. +/// - Compatible with the containers and algorithms in the Standard Template Library (STL) - http://www.sgi.com/tech/stl/ +/// +/// \param Type: The type to be stored in the stack. Must provide a copy-constructor. +template +class Stack : public DefaultAllocator +{ + typedef DefaultAllocator Allocator; + + enum + { + DEFAULT_CAPACITY = 4, + }; + + typedef Type* pointer; + typedef const Type* const_pointer; + +public: + typedef const_pointer const_iterator; +private: + + pointer m_data; + pointer m_end; + std::size_t m_capacity; + + + void insert(const Type& value) + { + Allocator::construct(m_end++, value); + } + void insert_overflow(const Type& value) + { + const std::size_t new_capacity = (m_capacity) ? m_capacity + m_capacity : std::size_t(DEFAULT_CAPACITY); + const pointer new_data = Allocator::allocate(new_capacity); + const pointer new_end = std::copy(m_data, m_end, new_data); + + destroy(); + Allocator::deallocate(m_data, m_capacity); + + m_capacity = new_capacity; + m_data = new_data; + m_end = new_end; + insert(value); + } + void destroy() + { + for(pointer p = m_data; p != m_end; ++p) + { + Allocator::destroy(p); + } + } + void construct(const Stack& other) + { + pointer p = m_data; + for(const_iterator i = other.begin(); i != other.end(); ++i) + { + Allocator::construct(p++, *i); + } + } + +public: + + Stack() : + m_data(0), + m_end(0), + m_capacity(0) + { + } + Stack(const Type& value) : + m_data(0), + m_end(0), + m_capacity(0) + { + push(value); + } + Stack(const Stack& other) : + DefaultAllocator(other) + { + m_capacity = other.m_capacity; + m_data = Allocator::allocate(m_capacity); + construct(other); + m_end = m_data + other.size(); + } + ~Stack() + { + destroy(); + Allocator::deallocate(m_data, m_capacity); + } + + const_iterator begin() const + { + return m_data; + } + const_iterator end() const + { + return m_end; + } + + bool empty() const + { + return end() == begin(); + } + void clear() + { + destroy(); + m_end = m_data; + } + + std::size_t size() const + { + return m_end - m_data; + } + Type operator[](const std::size_t i) const + { + return m_data[i]; + } + /// \brief Pushes \p value onto the stack at the top element. If reserved storage is insufficient for the new element, this will invalidate all iterators. + void push(const Type& value) + { + if(size() == m_capacity) + { + insert_overflow(value); + } + else + { + insert(value); + } + } + /// \brief Removes the top element of the stack. + void pop() + { + Allocator::destroy(--m_end); + } + /// \brief Returns the top element of the mutable stack. + Type& top() + { + return *(m_end-1); + } + /// \brief Returns the top element of the non-mutable stack. + const Type& top() const + { + return *(m_end-1); + } + /// \brief Returns the element below the top element of the mutable stack. + Type& parent() + { + return *(m_end-2); + } + /// \brief Returns the element below the top element of the non-mutable stack. + const Type& parent() const + { + return *(m_end-2); + } + /// \brief Swaps the values of this stack and \p other. + void swap(Stack& other) + { + std::swap(m_data, other.m_data); + std::swap(m_end, other.m_end); + std::swap(m_capacity, other.m_capacity); + } +#if 1 // use copy-swap technique + Stack& operator=(const Stack& other) + { + Stack temp(other); + temp.swap(*this); + return *this; + } +#else // avoids memory allocation if capacity is already sufficient. + Stack& operator=(const Stack& other) + { + if(&other != this) + { + destroy(); + + if(other.size() > m_capacity) + { + Allocator::deallocate(m_data, m_capacity); + m_capacity = other.m_capacity; + m_data = Allocator::allocate(m_capacity); + } + m_end = m_data + other.size(); + + construct(other); + } + return *this; + } +#endif +}; + +/// \brief Returns true if \p self is lexicographically less than \p other. +template +inline bool operator<(const Stack& self, const Stack& other) +{ + return std::lexicographical_compare(self.begin(), self.end(), other.begin(), other.end()); +} + +namespace std +{ + /// \brief Swaps the values of \p self and \p other. + /// Overloads std::swap(). + template + inline void swap(Stack& self, Stack& other) + { + self.swap(other); + } +} + +#endif diff --git a/tools/urt/libs/convert.cpp b/tools/urt/libs/convert.cpp new file mode 100644 index 00000000..73eacfea --- /dev/null +++ b/tools/urt/libs/convert.cpp @@ -0,0 +1,3 @@ + +#include "convert.h" + diff --git a/tools/urt/libs/convert.h b/tools/urt/libs/convert.h new file mode 100644 index 00000000..5e1ac8a8 --- /dev/null +++ b/tools/urt/libs/convert.h @@ -0,0 +1,285 @@ + +#if !defined(INCLUDED_CONVERT_H) +#define INCLUDED_CONVERT_H + +/// \file +/// \brief Character encoding conversion. + +#include "debugging/debugging.h" +#include +#include +#include + +#include "character.h" + +/// \brief Returns the number of bytes required to represent \p character in UTF-8 encoding. +inline std::size_t utf8_character_length(const char* character) +{ + if((*character & 0xE0) == 0xC0) // 110xxxxx + { + return 2; + } + else if((*character & 0xF0) == 0xE0) // 1110xxxx + { + return 3; + } + else if((*character & 0xF8) == 0xF0) // 11110xxx + { + return 4; + } + else if((*character & 0xFC) == 0xF8) // 111110xx + { + return 5; + } + else if((*character & 0xFE) == 0xFC) // 1111110x + { + return 6; + } + ERROR_MESSAGE(""); + return 0; +} + +struct UTF8Character +{ + const char* buffer; + std::size_t length; + UTF8Character() : buffer(0), length(0) + { + } + UTF8Character(const char* bytes) : buffer(bytes), length(utf8_character_length(bytes)) + { + } +}; + +inline bool operator<(const UTF8Character& self, const UTF8Character& other) +{ + return std::lexicographical_compare(self.buffer, self.buffer + self.length, other.buffer, other.buffer + other.length); +} + +/// \brief Writes \p c to \p ostream in Hex form. Useful for debugging. +template +inline TextOutputStreamType& ostream_write(TextOutputStreamType& ostream, const UTF8Character& c) +{ + for(const char* p = c.buffer; p != c.buffer + c.length; ++p) + { + ostream << HexChar(*p); + } + return ostream; +} + + + +/// \brief The character-set encoding for the current C locale. +/// +/// Obtain the global instance with globalCharacterSet(). +class CharacterSet +{ + const char* m_charSet; +public: + CharacterSet() + { + if(g_get_charset(&m_charSet) != FALSE) + { + m_charSet = 0; + } + } + bool isUTF8() const + { + return m_charSet == 0; + } + const char* get() const + { + return m_charSet; + } +}; + +typedef LazyStatic GlobalCharacterSet; + +/// \brief Returns the global instance of CharacterSet. +inline CharacterSet& globalCharacterSet() +{ + return GlobalCharacterSet::instance(); +} + + +class UTF8CharacterToExtendedASCII +{ +public: + UTF8Character m_utf8; + char m_c; + UTF8CharacterToExtendedASCII() : m_c('\0') + { + } + UTF8CharacterToExtendedASCII(const UTF8Character& utf8, char c) : m_utf8(utf8), m_c(c) + { + } +}; + +inline bool operator<(const UTF8CharacterToExtendedASCII& self, const UTF8CharacterToExtendedASCII& other) +{ + return self.m_utf8 < other.m_utf8; +} + +inline std::size_t extended_ascii_to_index(char c) +{ + return static_cast(c & 0x7F); +} + +inline char extended_ascii_for_index(std::size_t i) +{ + return static_cast(i | 0x80); +} + +/// \brief The active extended-ascii character set encoding. +/// Performs UTF-8 encoding and decoding of extended-ascii characters. +/// +/// Obtain the global instance with globalExtendedASCIICharacterSet(). +class ExtendedASCIICharacterSet +{ + typedef char UTF8CharBuffer[6]; + UTF8CharBuffer m_converted[128]; + UTF8Character m_decodeMap[128]; + UTF8CharacterToExtendedASCII m_encodeMap[128]; +public: + ExtendedASCIICharacterSet() + { + if(!globalCharacterSet().isUTF8()) + { + GIConv descriptor = g_iconv_open("UTF-8", globalCharacterSet().get()); + for(std::size_t i = 1; i < 128; ++i) + { + char c = extended_ascii_for_index(i); + char* inbuf = &c; + std::size_t inbytesleft = 1; + char* outbuf = m_converted[i]; + std::size_t outbytesleft = 6; + if(g_iconv(descriptor, &inbuf, &inbytesleft, &outbuf, &outbytesleft) != (size_t)(-1)) + { + UTF8Character utf8(m_converted[i]); + m_decodeMap[i] = utf8; + m_encodeMap[i] = UTF8CharacterToExtendedASCII(utf8, c); + } + } + g_iconv_close(descriptor); + std::sort(m_encodeMap, m_encodeMap + 128); + } + } + /// \brief Prints the (up to) 128 characters in the current extended-ascii character set. + /// Useful for debugging. + void print() const + { + globalOutputStream() << "UTF-8 conversion required from charset: " << globalCharacterSet().get() << "\n"; + for(std::size_t i = 1; i < 128; ++i) + { + if(m_decodeMap[i].buffer != 0) + { + globalOutputStream() << extended_ascii_for_index(i) << " = " << m_decodeMap[i] << "\n"; + } + } + } + /// \brief Returns \p c decoded from extended-ascii to UTF-8. + /// \p c must be an extended-ascii character. + const UTF8Character& decode(char c) const + { + ASSERT_MESSAGE(!globalCharacterSet().isUTF8(), "locale is utf8, no conversion required"); + ASSERT_MESSAGE(!char_is_ascii(c), "decode: ascii character"); + ASSERT_MESSAGE(m_decodeMap[extended_ascii_to_index(c)].buffer != 0, "decode: invalid character: " << HexChar(c)); + return m_decodeMap[extended_ascii_to_index(c)]; + } + /// \brief Returns \p c encoded to extended-ascii from UTF-8. + /// \p c must map to an extended-ascii character. + char encode(const UTF8Character& c) const + { + ASSERT_MESSAGE(!globalCharacterSet().isUTF8(), "locale is utf8, no conversion required"); + ASSERT_MESSAGE(!char_is_ascii(*c.buffer), "encode: ascii character"); + std::pair range + = std::equal_range(m_encodeMap, m_encodeMap + 128, UTF8CharacterToExtendedASCII(c, 0)); + ASSERT_MESSAGE(range.first != range.second, "encode: invalid character: " << c); + return (*range.first).m_c; + } +}; + +typedef LazyStatic GlobalExtendedASCIICharacterSet; + +/// \brief Returns the global instance of ExtendedASCIICharacterSet. +inline ExtendedASCIICharacterSet& globalExtendedASCIICharacterSet() +{ + return GlobalExtendedASCIICharacterSet::instance(); +} + +class ConvertUTF8ToLocale +{ +public: + StringRange m_range; + ConvertUTF8ToLocale(const char* string) : m_range(StringRange(string, string + strlen(string))) + { + } + ConvertUTF8ToLocale(const StringRange& range) : m_range(range) + { + } +}; + +/// \brief Writes \p convert to \p ostream after encoding each character to extended-ascii from UTF-8. +template +inline TextOutputStreamType& ostream_write(TextOutputStreamType& ostream, const ConvertUTF8ToLocale& convert) +{ + if(globalCharacterSet().isUTF8()) + { + return ostream << convert.m_range; + } + + for(const char* p = convert.m_range.begin; p != convert.m_range.end;) + { + if(!char_is_ascii(*p)) + { + UTF8Character c(p); + ostream << globalExtendedASCIICharacterSet().encode(c); + p += c.length; + } + else + { + ostream << *p++; + } + } + return ostream; +} + + +class ConvertLocaleToUTF8 +{ +public: + StringRange m_range; + ConvertLocaleToUTF8(const char* string) : m_range(StringRange(string, string + strlen(string))) + { + } + ConvertLocaleToUTF8(const StringRange& range) : m_range(range) + { + } +}; + +/// \brief Writes \p convert to \p ostream after decoding each character from extended-ascii to UTF-8. +template +inline TextOutputStreamType& ostream_write(TextOutputStreamType& ostream, const ConvertLocaleToUTF8& convert) +{ + if(globalCharacterSet().isUTF8()) + { + return ostream << convert.m_range; + } + + for(const char* p = convert.m_range.begin; p != convert.m_range.end; ++p) + { + if(!char_is_ascii(*p)) + { + UTF8Character c(globalExtendedASCIICharacterSet().decode(*p)); + ostream.write(c.buffer, c.length); + } + else + { + ostream << *p; + } + } + return ostream; +} + + +#endif diff --git a/tools/urt/libs/ddslib.h b/tools/urt/libs/ddslib.h new file mode 100644 index 00000000..246e31be --- /dev/null +++ b/tools/urt/libs/ddslib.h @@ -0,0 +1,250 @@ +/* ----------------------------------------------------------------------------- + +DDS Library + +Based on code from Nvidia's DDS example: +http://www.nvidia.com/object/dxtc_decompression_code.html + +Copyright (c) 2003 Randy Reddig +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list +of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +Neither the names of the copyright holders nor the names of its contributors may +be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +----------------------------------------------------------------------------- */ + + + +/* marker */ +#ifndef DDSLIB_H +#define DDSLIB_H + + + +/* dependencies */ +#include +#include + + + +/* c++ marker */ +#ifdef __cplusplus +extern "C" +{ +#endif + + + +/* dds definition */ +typedef enum +{ + DDS_PF_ARGB8888, + DDS_PF_DXT1, + DDS_PF_DXT2, + DDS_PF_DXT3, + DDS_PF_DXT4, + DDS_PF_DXT5, + DDS_PF_UNKNOWN +} +ddsPF_t; + + +/* 16bpp stuff */ +#define DDS_LOW_5 0x001F; +#define DDS_MID_6 0x07E0; +#define DDS_HIGH_5 0xF800; +#define DDS_MID_555 0x03E0; +#define DDS_HI_555 0x7C00; + + +/* structures */ +typedef struct ddsColorKey_s +{ + unsigned int colorSpaceLowValue; + unsigned int colorSpaceHighValue; +} +ddsColorKey_t; + + +typedef struct ddsCaps_s +{ + unsigned int caps1; + unsigned int caps2; + unsigned int caps3; + unsigned int caps4; +} +ddsCaps_t; + + +typedef struct ddsMultiSampleCaps_s +{ + unsigned short flipMSTypes; + unsigned short bltMSTypes; +} +ddsMultiSampleCaps_t; + + +typedef struct ddsPixelFormat_s +{ + unsigned int size; + unsigned int flags; + unsigned int fourCC; + union + { + unsigned int rgbBitCount; + unsigned int yuvBitCount; + unsigned int zBufferBitDepth; + unsigned int alphaBitDepth; + unsigned int luminanceBitCount; + unsigned int bumpBitCount; + unsigned int privateFormatBitCount; + }; + union + { + unsigned int rBitMask; + unsigned int yBitMask; + unsigned int stencilBitDepth; + unsigned int luminanceBitMask; + unsigned int bumpDuBitMask; + unsigned int operations; + }; + union + { + unsigned int gBitMask; + unsigned int uBitMask; + unsigned int zBitMask; + unsigned int bumpDvBitMask; + ddsMultiSampleCaps_t multiSampleCaps; + }; + union + { + unsigned int bBitMask; + unsigned int vBitMask; + unsigned int stencilBitMask; + unsigned int bumpLuminanceBitMask; + }; + union + { + unsigned int rgbAlphaBitMask; + unsigned int yuvAlphaBitMask; + unsigned int luminanceAlphaBitMask; + unsigned int rgbZBitMask; + unsigned int yuvZBitMask; + }; +} +ddsPixelFormat_t; + + +typedef struct ddsBuffer_s +{ + /* magic: 'dds ' */ + char magic[ 4 ]; + + /* directdraw surface */ + unsigned int size; + unsigned int flags; + unsigned int height; + unsigned int width; + union + { + int pitch; + unsigned int linearSize; + }; + unsigned int backBufferCount; + union + { + unsigned int mipMapCount; + unsigned int refreshRate; + unsigned int srcVBHandle; + }; + unsigned int alphaBitDepth; + unsigned int reserved; + void *surface; + union + { + ddsColorKey_t ckDestOverlay; + unsigned int emptyFaceColor; + }; + ddsColorKey_t ckDestBlt; + ddsColorKey_t ckSrcOverlay; + ddsColorKey_t ckSrcBlt; + union + { + ddsPixelFormat_t pixelFormat; + unsigned int fvf; + }; + ddsCaps_t ddsCaps; + unsigned int textureStage; + + /* data (Varying size) */ + unsigned char data[ 4 ]; +} +ddsBuffer_t; + + +typedef struct ddsColorBlock_s +{ + unsigned short colors[ 2 ]; + unsigned char row[ 4 ]; +} +ddsColorBlock_t; + + +typedef struct ddsAlphaBlockExplicit_s +{ + unsigned short row[ 4 ]; +} +ddsAlphaBlockExplicit_t; + + +typedef struct ddsAlphaBlock3BitLinear_s +{ + unsigned char alpha0; + unsigned char alpha1; + unsigned char stuff[ 6 ]; +} +ddsAlphaBlock3BitLinear_t; + + +typedef struct ddsColor_s +{ + unsigned char r, g, b, a; +} +ddsColor_t; + + + +/* public functions */ +int DDSGetInfo( ddsBuffer_t *dds, int *width, int *height, ddsPF_t *pf ); +int DDSDecompress( ddsBuffer_t *dds, unsigned char *pixels ); + + + +/* end marker */ +#ifdef __cplusplus +} +#endif + +#endif diff --git a/tools/urt/libs/ddslib/ddslib.c b/tools/urt/libs/ddslib/ddslib.c new file mode 100644 index 00000000..e7bfeb2b --- /dev/null +++ b/tools/urt/libs/ddslib/ddslib.c @@ -0,0 +1,781 @@ +/* ----------------------------------------------------------------------------- + +DDS Library + +Based on code from Nvidia's DDS example: +http://www.nvidia.com/object/dxtc_decompression_code.html + +Copyright (c) 2003 Randy Reddig +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list +of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +Neither the names of the copyright holders nor the names of its contributors may +be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +----------------------------------------------------------------------------- */ + + + +/* marker */ +#define DDSLIB_C + + + +/* dependencies */ +#include "ddslib.h" + + + +/* endian tomfoolery */ +typedef union +{ + float f; + char c[ 4 ]; +} +floatSwapUnion; + + +#ifndef __BIG_ENDIAN__ + #ifdef _SGI_SOURCE + #define __BIG_ENDIAN__ + #endif +#endif + + +#ifdef __BIG_ENDIAN__ + + int DDSBigLong( int src ) { return src; } + short DDSBigShort( short src ) { return src; } + float DDSBigFloat( float src ) { return src; } + + int DDSLittleLong( int src ) + { + return ((src & 0xFF000000) >> 24) | + ((src & 0x00FF0000) >> 8) | + ((src & 0x0000FF00) << 8) | + ((src & 0x000000FF) << 24); + } + + short DDSLittleShort( short src ) + { + return ((src & 0xFF00) >> 8) | + ((src & 0x00FF) << 8); + } + + float DDSLittleFloat( float src ) + { + floatSwapUnion in,out; + in.f = src; + out.c[ 0 ] = in.c[ 3 ]; + out.c[ 1 ] = in.c[ 2 ]; + out.c[ 2 ] = in.c[ 1 ]; + out.c[ 3 ] = in.c[ 0 ]; + return out.f; + } + +#else /*__BIG_ENDIAN__*/ + + int DDSLittleLong( int src ) { return src; } + short DDSLittleShort( short src ) { return src; } + float DDSLittleFloat( float src ) { return src; } + + int DDSBigLong( int src ) + { + return ((src & 0xFF000000) >> 24) | + ((src & 0x00FF0000) >> 8) | + ((src & 0x0000FF00) << 8) | + ((src & 0x000000FF) << 24); + } + + short DDSBigShort( short src ) + { + return ((src & 0xFF00) >> 8) | + ((src & 0x00FF) << 8); + } + + float DDSBigFloat( float src ) + { + floatSwapUnion in,out; + in.f = src; + out.c[ 0 ] = in.c[ 3 ]; + out.c[ 1 ] = in.c[ 2 ]; + out.c[ 2 ] = in.c[ 1 ]; + out.c[ 3 ] = in.c[ 0 ]; + return out.f; + } + +#endif /*__BIG_ENDIAN__*/ + + + +/* +DDSDecodePixelFormat() +determines which pixel format the dds texture is in +*/ + +static void DDSDecodePixelFormat( ddsBuffer_t *dds, ddsPF_t *pf ) +{ + unsigned int fourCC; + + + /* dummy check */ + if( dds == NULL || pf == NULL ) + return; + + /* extract fourCC */ + fourCC = dds->pixelFormat.fourCC; + + /* test it */ + if( fourCC == 0 ) + *pf = DDS_PF_ARGB8888; + else if( fourCC == *((unsigned int*) "DXT1") ) + *pf = DDS_PF_DXT1; + else if( fourCC == *((unsigned int*) "DXT2") ) + *pf = DDS_PF_DXT2; + else if( fourCC == *((unsigned int*) "DXT3") ) + *pf = DDS_PF_DXT3; + else if( fourCC == *((unsigned int*) "DXT4") ) + *pf = DDS_PF_DXT4; + else if( fourCC == *((unsigned int*) "DXT5") ) + *pf = DDS_PF_DXT5; + else + *pf = DDS_PF_UNKNOWN; +} + + + +/* +DDSGetInfo() +extracts relevant info from a dds texture, returns 0 on success +*/ + +int DDSGetInfo( ddsBuffer_t *dds, int *width, int *height, ddsPF_t *pf ) +{ + /* dummy test */ + if( dds == NULL ) + return -1; + + /* test dds header */ + if( *((int*) dds->magic) != *((int*) "DDS ") ) + return -1; + if( DDSLittleLong( dds->size ) != 124 ) + return -1; + + /* extract width and height */ + if( width != NULL ) + *width = DDSLittleLong( dds->width ); + if( height != NULL ) + *height = DDSLittleLong( dds->height ); + + /* get pixel format */ + DDSDecodePixelFormat( dds, pf ); + + /* return ok */ + return 0; +} + + + +/* +DDSGetColorBlockColors() +extracts colors from a dds color block +*/ + +static void DDSGetColorBlockColors( ddsColorBlock_t *block, ddsColor_t colors[ 4 ] ) +{ + unsigned short word; + + + /* color 0 */ + word = DDSLittleShort( block->colors[ 0 ] ); + colors[ 0 ].a = 0xff; + + /* extract rgb bits */ + colors[ 0 ].b = (unsigned char) word; + colors[ 0 ].b <<= 3; + colors[ 0 ].b |= (colors[ 0 ].b >> 5); + word >>= 5; + colors[ 0 ].g = (unsigned char) word; + colors[ 0 ].g <<= 2; + colors[ 0 ].g |= (colors[ 0 ].g >> 5); + word >>= 6; + colors[ 0 ].r = (unsigned char) word; + colors[ 0 ].r <<= 3; + colors[ 0 ].r |= (colors[ 0 ].r >> 5); + + /* same for color 1 */ + word = DDSLittleShort( block->colors[ 1 ] ); + colors[ 1 ].a = 0xff; + + /* extract rgb bits */ + colors[ 1 ].b = (unsigned char) word; + colors[ 1 ].b <<= 3; + colors[ 1 ].b |= (colors[ 1 ].b >> 5); + word >>= 5; + colors[ 1 ].g = (unsigned char) word; + colors[ 1 ].g <<= 2; + colors[ 1 ].g |= (colors[ 1 ].g >> 5); + word >>= 6; + colors[ 1 ].r = (unsigned char) word; + colors[ 1 ].r <<= 3; + colors[ 1 ].r |= (colors[ 1 ].r >> 5); + + /* use this for all but the super-freak math method */ + if( block->colors[ 0 ] > block->colors[ 1 ] ) + { + /* four-color block: derive the other two colors. + 00 = color 0, 01 = color 1, 10 = color 2, 11 = color 3 + these two bit codes correspond to the 2-bit fields + stored in the 64-bit block. */ + + word = ((unsigned short) colors[ 0 ].r * 2 + (unsigned short) colors[ 1 ].r ) / 3; + /* no +1 for rounding */ + /* as bits have been shifted to 888 */ + colors[ 2 ].r = (unsigned char) word; + word = ((unsigned short) colors[ 0 ].g * 2 + (unsigned short) colors[ 1 ].g) / 3; + colors[ 2 ].g = (unsigned char) word; + word = ((unsigned short) colors[ 0 ].b * 2 + (unsigned short) colors[ 1 ].b) / 3; + colors[ 2 ].b = (unsigned char) word; + colors[ 2 ].a = 0xff; + + word = ((unsigned short) colors[ 0 ].r + (unsigned short) colors[ 1 ].r * 2) / 3; + colors[ 3 ].r = (unsigned char) word; + word = ((unsigned short) colors[ 0 ].g + (unsigned short) colors[ 1 ].g * 2) / 3; + colors[ 3 ].g = (unsigned char) word; + word = ((unsigned short) colors[ 0 ].b + (unsigned short) colors[ 1 ].b * 2) / 3; + colors[ 3 ].b = (unsigned char) word; + colors[ 3 ].a = 0xff; + } + else + { + /* three-color block: derive the other color. + 00 = color 0, 01 = color 1, 10 = color 2, + 11 = transparent. + These two bit codes correspond to the 2-bit fields + stored in the 64-bit block */ + + word = ((unsigned short) colors[ 0 ].r + (unsigned short) colors[ 1 ].r) / 2; + colors[ 2 ].r = (unsigned char) word; + word = ((unsigned short) colors[ 0 ].g + (unsigned short) colors[ 1 ].g) / 2; + colors[ 2 ].g = (unsigned char) word; + word = ((unsigned short) colors[ 0 ].b + (unsigned short) colors[ 1 ].b) / 2; + colors[ 2 ].b = (unsigned char) word; + colors[ 2 ].a = 0xff; + + /* random color to indicate alpha */ + colors[ 3 ].r = 0x00; + colors[ 3 ].g = 0xff; + colors[ 3 ].b = 0xff; + colors[ 3 ].a = 0x00; + } +} + + + +/* +DDSDecodeColorBlock() +decodes a dds color block +fixme: make endian-safe +*/ + +static void DDSDecodeColorBlock( unsigned int *pixel, ddsColorBlock_t *block, int width, unsigned int colors[ 4 ] ) +{ + int r, n; + unsigned int bits; + unsigned int masks[] = { 3, 12, 3 << 4, 3 << 6 }; /* bit masks = 00000011, 00001100, 00110000, 11000000 */ + int shift[] = { 0, 2, 4, 6 }; + + + /* r steps through lines in y */ + for( r = 0; r < 4; r++, pixel += (width - 4) ) /* no width * 4 as unsigned int ptr inc will * 4 */ + { + /* width * 4 bytes per pixel per line, each j dxtc row is 4 lines of pixels */ + + /* n steps through pixels */ + for( n = 0; n < 4; n++ ) + { + bits = block->row[ r ] & masks[ n ]; + bits >>= shift[ n ]; + + switch( bits ) + { + case 0: + *pixel = colors[ 0 ]; + pixel++; + break; + + case 1: + *pixel = colors[ 1 ]; + pixel++; + break; + + case 2: + *pixel = colors[ 2 ]; + pixel++; + break; + + case 3: + *pixel = colors[ 3 ]; + pixel++; + break; + + default: + /* invalid */ + pixel++; + break; + } + } + } +} + + + +/* +DDSDecodeAlphaExplicit() +decodes a dds explicit alpha block +*/ + +static void DDSDecodeAlphaExplicit( unsigned int *pixel, ddsAlphaBlockExplicit_t *alphaBlock, int width, unsigned int alphaZero ) +{ + int row, pix; + unsigned short word; + ddsColor_t color; + + + /* clear color */ + color.r = 0; + color.g = 0; + color.b = 0; + + /* walk rows */ + for( row = 0; row < 4; row++, pixel += (width - 4) ) + { + word = DDSLittleShort( alphaBlock->row[ row ] ); + + /* walk pixels */ + for( pix = 0; pix < 4; pix++ ) + { + /* zero the alpha bits of image pixel */ + *pixel &= alphaZero; + color.a = word & 0x000F; + color.a = color.a | (color.a << 4); + *pixel |= *((unsigned int*) &color); + word >>= 4; /* move next bits to lowest 4 */ + pixel++; /* move to next pixel in the row */ + + } + } +} + + + +/* +DDSDecodeAlpha3BitLinear() +decodes interpolated alpha block +*/ + +static void DDSDecodeAlpha3BitLinear( unsigned int *pixel, ddsAlphaBlock3BitLinear_t *alphaBlock, int width, unsigned int alphaZero ) +{ + + int row, pix; + unsigned int stuff; + unsigned char bits[ 4 ][ 4 ]; + unsigned short alphas[ 8 ]; + ddsColor_t aColors[ 4 ][ 4 ]; + + + /* get initial alphas */ + alphas[ 0 ] = alphaBlock->alpha0; + alphas[ 1 ] = alphaBlock->alpha1; + + /* 8-alpha block */ + if( alphas[ 0 ] > alphas[ 1 ] ) + { + /* 000 = alpha_0, 001 = alpha_1, others are interpolated */ + alphas[ 2 ] = ( 6 * alphas[ 0 ] + alphas[ 1 ]) / 7; /* bit code 010 */ + alphas[ 3 ] = ( 5 * alphas[ 0 ] + 2 * alphas[ 1 ]) / 7; /* bit code 011 */ + alphas[ 4 ] = ( 4 * alphas[ 0 ] + 3 * alphas[ 1 ]) / 7; /* bit code 100 */ + alphas[ 5 ] = ( 3 * alphas[ 0 ] + 4 * alphas[ 1 ]) / 7; /* bit code 101 */ + alphas[ 6 ] = ( 2 * alphas[ 0 ] + 5 * alphas[ 1 ]) / 7; /* bit code 110 */ + alphas[ 7 ] = ( alphas[ 0 ] + 6 * alphas[ 1 ]) / 7; /* bit code 111 */ + } + + /* 6-alpha block */ + else + { + /* 000 = alpha_0, 001 = alpha_1, others are interpolated */ + alphas[ 2 ] = (4 * alphas[ 0 ] + alphas[ 1 ]) / 5; /* bit code 010 */ + alphas[ 3 ] = (3 * alphas[ 0 ] + 2 * alphas[ 1 ]) / 5; /* bit code 011 */ + alphas[ 4 ] = (2 * alphas[ 0 ] + 3 * alphas[ 1 ]) / 5; /* bit code 100 */ + alphas[ 5 ] = ( alphas[ 0 ] + 4 * alphas[ 1 ]) / 5; /* bit code 101 */ + alphas[ 6 ] = 0; /* bit code 110 */ + alphas[ 7 ] = 255; /* bit code 111 */ + } + + /* decode 3-bit fields into array of 16 bytes with same value */ + + /* first two rows of 4 pixels each */ + stuff = *((unsigned int*) &(alphaBlock->stuff[ 0 ])); + + bits[ 0 ][ 0 ] = (unsigned char) (stuff & 0x00000007); + stuff >>= 3; + bits[ 0 ][ 1 ] = (unsigned char) (stuff & 0x00000007); + stuff >>= 3; + bits[ 0 ][ 2 ] = (unsigned char) (stuff & 0x00000007); + stuff >>= 3; + bits[ 0 ][ 3 ] = (unsigned char) (stuff & 0x00000007); + stuff >>= 3; + bits[ 1 ][ 0 ] = (unsigned char) (stuff & 0x00000007); + stuff >>= 3; + bits[ 1 ][ 1 ] = (unsigned char) (stuff & 0x00000007); + stuff >>= 3; + bits[ 1 ][ 2 ] = (unsigned char) (stuff & 0x00000007); + stuff >>= 3; + bits[ 1 ][ 3 ] = (unsigned char) (stuff & 0x00000007); + + /* last two rows */ + stuff = *((unsigned int*) &(alphaBlock->stuff[ 3 ])); /* last 3 bytes */ + + bits[ 2 ][ 0 ] = (unsigned char) (stuff & 0x00000007); + stuff >>= 3; + bits[ 2 ][ 1 ] = (unsigned char) (stuff & 0x00000007); + stuff >>= 3; + bits[ 2 ][ 2 ] = (unsigned char) (stuff & 0x00000007); + stuff >>= 3; + bits[ 2 ][ 3 ] = (unsigned char) (stuff & 0x00000007); + stuff >>= 3; + bits[ 3 ][ 0 ] = (unsigned char) (stuff & 0x00000007); + stuff >>= 3; + bits[ 3 ][ 1 ] = (unsigned char) (stuff & 0x00000007); + stuff >>= 3; + bits[ 3 ][ 2 ] = (unsigned char) (stuff & 0x00000007); + stuff >>= 3; + bits[ 3 ][ 3 ] = (unsigned char) (stuff & 0x00000007); + + /* decode the codes into alpha values */ + for( row = 0; row < 4; row++ ) + { + for( pix=0; pix < 4; pix++ ) + { + aColors[ row ][ pix ].r = 0; + aColors[ row ][ pix ].g = 0; + aColors[ row ][ pix ].b = 0; + aColors[ row ][ pix ].a = (unsigned char) alphas[ bits[ row ][ pix ] ]; + } + } + + /* write out alpha values to the image bits */ + for( row = 0; row < 4; row++, pixel += width-4 ) + { + for( pix = 0; pix < 4; pix++ ) + { + /* zero the alpha bits of image pixel */ + *pixel &= alphaZero; + + /* or the bits into the prev. nulled alpha */ + *pixel |= *((unsigned int*) &(aColors[ row ][ pix ])); + pixel++; + } + } +} + + + +/* +DDSDecompressDXT1() +decompresses a dxt1 format texture +*/ + +static int DDSDecompressDXT1( ddsBuffer_t *dds, int width, int height, unsigned char *pixels ) +{ + int x, y, xBlocks, yBlocks; + unsigned int *pixel; + ddsColorBlock_t *block; + ddsColor_t colors[ 4 ]; + + + /* setup */ + xBlocks = width / 4; + yBlocks = height / 4; + + /* walk y */ + for( y = 0; y < yBlocks; y++ ) + { + /* 8 bytes per block */ + block = (ddsColorBlock_t*) ((unsigned int) dds->data + y * xBlocks * 8); + + /* walk x */ + for( x = 0; x < xBlocks; x++, block++ ) + { + DDSGetColorBlockColors( block, colors ); + pixel = (unsigned int*) (pixels + x * 16 + (y * 4) * width * 4); + DDSDecodeColorBlock( pixel, block, width, (unsigned int*) colors ); + } + } + + /* return ok */ + return 0; +} + + + +/* +DDSDecompressDXT3() +decompresses a dxt3 format texture +*/ + +static int DDSDecompressDXT3( ddsBuffer_t *dds, int width, int height, unsigned char *pixels ) +{ + int x, y, xBlocks, yBlocks; + unsigned int *pixel, alphaZero; + ddsColorBlock_t *block; + ddsAlphaBlockExplicit_t *alphaBlock; + ddsColor_t colors[ 4 ]; + + + /* setup */ + xBlocks = width / 4; + yBlocks = height / 4; + + /* create zero alpha */ + colors[ 0 ].a = 0; + colors[ 0 ].r = 0xFF; + colors[ 0 ].g = 0xFF; + colors[ 0 ].b = 0xFF; + alphaZero = *((unsigned int*) &colors[ 0 ]); + + /* walk y */ + for( y = 0; y < yBlocks; y++ ) + { + /* 8 bytes per block, 1 block for alpha, 1 block for color */ + block = (ddsColorBlock_t*) ((unsigned int) dds->data + y * xBlocks * 16); + + /* walk x */ + for( x = 0; x < xBlocks; x++, block++ ) + { + /* get alpha block */ + alphaBlock = (ddsAlphaBlockExplicit_t*) block; + + /* get color block */ + block++; + DDSGetColorBlockColors( block, colors ); + + /* decode color block */ + pixel = (unsigned int*) (pixels + x * 16 + (y * 4) * width * 4); + DDSDecodeColorBlock( pixel, block, width, (unsigned int*) colors ); + + /* overwrite alpha bits with alpha block */ + DDSDecodeAlphaExplicit( pixel, alphaBlock, width, alphaZero ); + } + } + + /* return ok */ + return 0; +} + + + +/* +DDSDecompressDXT5() +decompresses a dxt5 format texture +*/ + +static int DDSDecompressDXT5( ddsBuffer_t *dds, int width, int height, unsigned char *pixels ) +{ + int x, y, xBlocks, yBlocks; + unsigned int *pixel, alphaZero; + ddsColorBlock_t *block; + ddsAlphaBlock3BitLinear_t *alphaBlock; + ddsColor_t colors[ 4 ]; + + + /* setup */ + xBlocks = width / 4; + yBlocks = height / 4; + + /* create zero alpha */ + colors[ 0 ].a = 0; + colors[ 0 ].r = 0xFF; + colors[ 0 ].g = 0xFF; + colors[ 0 ].b = 0xFF; + alphaZero = *((unsigned int*) &colors[ 0 ]); + + /* walk y */ + for( y = 0; y < yBlocks; y++ ) + { + /* 8 bytes per block, 1 block for alpha, 1 block for color */ + block = (ddsColorBlock_t*) ((unsigned int) dds->data + y * xBlocks * 16); + + /* walk x */ + for( x = 0; x < xBlocks; x++, block++ ) + { + /* get alpha block */ + alphaBlock = (ddsAlphaBlock3BitLinear_t*) block; + + /* get color block */ + block++; + DDSGetColorBlockColors( block, colors ); + + /* decode color block */ + pixel = (unsigned int*) (pixels + x * 16 + (y * 4) * width * 4); + DDSDecodeColorBlock( pixel, block, width, (unsigned int*) colors ); + + /* overwrite alpha bits with alpha block */ + DDSDecodeAlpha3BitLinear( pixel, alphaBlock, width, alphaZero ); + } + } + + /* return ok */ + return 0; +} + + + +/* +DDSDecompressDXT2() +decompresses a dxt2 format texture (fixme: un-premultiply alpha) +*/ + +static int DDSDecompressDXT2( ddsBuffer_t *dds, int width, int height, unsigned char *pixels ) +{ + int r; + + + /* decompress dxt3 first */ + r = DDSDecompressDXT3( dds, width, height, pixels ); + + /* return to sender */ + return r; +} + + + +/* +DDSDecompressDXT4() +decompresses a dxt4 format texture (fixme: un-premultiply alpha) +*/ + +static int DDSDecompressDXT4( ddsBuffer_t *dds, int width, int height, unsigned char *pixels ) +{ + int r; + + + /* decompress dxt5 first */ + r = DDSDecompressDXT5( dds, width, height, pixels ); + + /* return to sender */ + return r; +} + + + +/* +DDSDecompressARGB8888() +decompresses an argb 8888 format texture +*/ + +static int DDSDecompressARGB8888( ddsBuffer_t *dds, int width, int height, unsigned char *pixels ) +{ + int x, y; + unsigned char *in, *out; + + + /* setup */ + in = dds->data; + out = pixels; + + /* walk y */ + for( y = 0; y < height; y++ ) + { + /* walk x */ + for( x = 0; x < width; x++ ) + { + *out++ = *in++; + *out++ = *in++; + *out++ = *in++; + *out++ = *in++; + } + } + + /* return ok */ + return 0; +} + + + +/* +DDSDecompress() +decompresses a dds texture into an rgba image buffer, returns 0 on success +*/ + +int DDSDecompress( ddsBuffer_t *dds, unsigned char *pixels ) +{ + int width, height, r; + ddsPF_t pf; + + + /* get dds info */ + r = DDSGetInfo( dds, &width, &height, &pf ); + if( r ) + return r; + + /* decompress */ + switch( pf ) + { + case DDS_PF_ARGB8888: + /* fixme: support other [a]rgb formats */ + r = DDSDecompressARGB8888( dds, width, height, pixels ); + break; + + case DDS_PF_DXT1: + r = DDSDecompressDXT1( dds, width, height, pixels ); + break; + + case DDS_PF_DXT2: + r = DDSDecompressDXT2( dds, width, height, pixels ); + break; + + case DDS_PF_DXT3: + r = DDSDecompressDXT3( dds, width, height, pixels ); + break; + + case DDS_PF_DXT4: + r = DDSDecompressDXT4( dds, width, height, pixels ); + break; + + case DDS_PF_DXT5: + r = DDSDecompressDXT5( dds, width, height, pixels ); + break; + + default: + case DDS_PF_UNKNOWN: + memset( pixels, 0xFF, width * height * 4 ); + r = -1; + break; + } + + /* return to sender */ + return r; +} + diff --git a/tools/urt/libs/ddslib/ddslib.dsp b/tools/urt/libs/ddslib/ddslib.dsp new file mode 100644 index 00000000..1b684d90 --- /dev/null +++ b/tools/urt/libs/ddslib/ddslib.dsp @@ -0,0 +1,106 @@ +# Microsoft Developer Studio Project File - Name="ddslib" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=ddslib - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "ddslib.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "ddslib.mak" CFG="ddslib - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ddslib - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "ddslib - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "Perforce Project" +# PROP Scc_LocalPath ".." +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ddslib - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +F90=df.exe +MTL=midl.exe +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /O2 /I ".." /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FR /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "ddslib - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +F90=df.exe +MTL=midl.exe +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /ZI /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FR /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "ddslib - Win32 Release" +# Name "ddslib - Win32 Debug" +# Begin Group "src" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\ddslib.c +# End Source File +# End Group +# Begin Group "include" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\ddslib.h +# End Source File +# End Group +# End Target +# End Project diff --git a/tools/urt/libs/ddslib/ddslib.plg b/tools/urt/libs/ddslib/ddslib.plg new file mode 100644 index 00000000..d6ec3b91 --- /dev/null +++ b/tools/urt/libs/ddslib/ddslib.plg @@ -0,0 +1,27 @@ + + +
+

Build Log

+

+--------------------Configuration: ddslib - Win32 Debug-------------------- +

+

Command Lines

+Creating temporary file "C:\DOCUME~1\TWENTY~1\LOCALS~1\Temp\RSP5F1.tmp" with contents +[ +/nologo /MDd /W3 /Gm /ZI /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FR"Debug/" /Fo"Debug/" /Fd"Debug/" /FD /GZ /c +"C:\Program Files\Subversion\GtkRadiant\libs\ddslib\ddslib.c" +] +Creating command line "cl.exe @C:\DOCUME~1\TWENTY~1\LOCALS~1\Temp\RSP5F1.tmp" +Creating command line "link.exe -lib /nologo /out:"Debug\ddslib.lib" ".\Debug\ddslib.obj" " +

Output Window

+Compiling... +ddslib.c +Creating library... + + + +

Results

+ddslib.lib - 0 error(s), 0 warning(s) +
+ + diff --git a/tools/urt/libs/ddslib/ddslib.vcproj b/tools/urt/libs/ddslib/ddslib.vcproj new file mode 100644 index 00000000..66683739 --- /dev/null +++ b/tools/urt/libs/ddslib/ddslib.vcproj @@ -0,0 +1,211 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/urt/libs/debugging/debugging.cpp b/tools/urt/libs/debugging/debugging.cpp new file mode 100644 index 00000000..b0bae9a1 --- /dev/null +++ b/tools/urt/libs/debugging/debugging.cpp @@ -0,0 +1,8 @@ + +#include "debugging.h" + +void TEST_ASSERT() +{ + ERROR_MESSAGE("test"); + ASSERT_NOTNULL(0); +} diff --git a/tools/urt/libs/debugging/debugging.h b/tools/urt/libs/debugging/debugging.h new file mode 100644 index 00000000..d39dd6df --- /dev/null +++ b/tools/urt/libs/debugging/debugging.h @@ -0,0 +1,115 @@ + +#if !defined(INCLUDED_DEBUGGING_DEBUGGING_H) +#define INCLUDED_DEBUGGING_DEBUGGING_H + +/// \file +/// \brief Debugging macros for fatal error/assert messages. + +#include "stream/textstream.h" +#include "warnings.h" +#include "generic/static.h" + +#if defined(_MSC_VER) && defined(_M_IX86) +#define DEBUGGER_BREAKPOINT() __asm { int 3 } +#elif defined (__i386__) && defined (__GNUC__) && __GNUC__ >= 2 +#define DEBUGGER_BREAKPOINT() __asm__ __volatile__ ("int $03") +#else +#include + +#define DEBUGGER_BREAKPOINT() raise(SIGTRAP); +#endif + + +#define FILE_LINE __FILE__ ":" << __LINE__ + +#if defined(_DEBUG) || 1 +#define DEBUG_ASSERTS +#endif + +class DebugMessageHandler +{ +public: + virtual TextOutputStream& getOutputStream() = 0; + virtual bool handleMessage() = 0; +}; + +class NullDebugMessageHandler : public NullOutputStream, public DebugMessageHandler +{ +public: + virtual TextOutputStream& getOutputStream() + { + return *this; + } + virtual bool handleMessage() + { + return false; + } +}; + +class DefaultDebugMessageHandler : public DebugMessageHandler +{ +public: + virtual TextOutputStream& getOutputStream() + { + return globalErrorStream(); + } + virtual bool handleMessage() + { +#if defined(_DEBUG) + return false; // send debug-break +#else + return true; +#endif + } +}; + +class DebugMessageHandlerRef : public DefaultDebugMessageHandler +{ + DebugMessageHandler* m_handler; +public: + DebugMessageHandlerRef() + : m_handler(this) + { + } + void setHandler(DebugMessageHandler& handler) + { + m_handler = &handler; + } + DebugMessageHandler& getHandler() + { + return *m_handler; + } +}; + +typedef Static GlobalDebugMessageHandler; + +inline DebugMessageHandler& globalDebugMessageHandler() +{ + return GlobalDebugMessageHandler::instance().getHandler(); +} + +#if defined(DEBUG_ASSERTS) + +/// \brief Sends a \p message to the current debug-message-handler text-output-stream if \p condition evaluates to false. +#define ASSERT_MESSAGE(condition, message)\ +if(!(condition))\ +{\ + globalDebugMessageHandler().getOutputStream() << FILE_LINE << "\nassertion failure: " << message << "\n";\ + if(!globalDebugMessageHandler().handleMessage()) { DEBUGGER_BREAKPOINT(); }\ +} else\ + +/// \brief Sends a \p message to the current debug-message-handler text-output-stream. +#define ERROR_MESSAGE(message)\ +globalDebugMessageHandler().getOutputStream() << FILE_LINE << "\nruntime error: " << message << "\n";\ +if(!globalDebugMessageHandler().handleMessage()) { DEBUGGER_BREAKPOINT(); } else\ + +#define ASSERT_NOTNULL(ptr) ASSERT_MESSAGE(ptr != 0, "pointer \"" #ptr "\" is null") + +#else + +#define ASSERT_MESSAGE(condition, message) +#define ASSERT_NOTNULL(ptr) + +#endif + +#endif diff --git a/tools/urt/libs/dragplanes.cpp b/tools/urt/libs/dragplanes.cpp new file mode 100644 index 00000000..0aed24ba --- /dev/null +++ b/tools/urt/libs/dragplanes.cpp @@ -0,0 +1,3 @@ + +#include "dragplanes.h" + diff --git a/tools/urt/libs/dragplanes.h b/tools/urt/libs/dragplanes.h new file mode 100644 index 00000000..94f1fbe4 --- /dev/null +++ b/tools/urt/libs/dragplanes.h @@ -0,0 +1,233 @@ + +#if !defined(INCLUDED_DRAGPLANES_H) +#define INCLUDED_DRAGPLANES_H + +#include "selectable.h" +#include "selectionlib.h" +#include "math/aabb.h" +#include "math/line.h" + +class DragPlanes +{ +public: + ObservedSelectable m_selectable_right; // +x + ObservedSelectable m_selectable_left; // -x + ObservedSelectable m_selectable_front; // +y + ObservedSelectable m_selectable_back; // -y + ObservedSelectable m_selectable_top; // +z + ObservedSelectable m_selectable_bottom; // -z + Vector3 m_dragPlanesMin; + Vector3 m_dragPlanesMax; + Vector3 m_dragPlanesOrigin; + Vector3 m_dragPlanesExtents; + + DragPlanes(const SelectionChangeCallback& onchanged) : + m_selectable_right(onchanged), + m_selectable_left(onchanged), + m_selectable_front(onchanged), + m_selectable_back(onchanged), + m_selectable_top(onchanged), + m_selectable_bottom(onchanged) + { + } + bool isSelected() const + { + return m_selectable_right.isSelected() + || m_selectable_left.isSelected() + || m_selectable_front.isSelected() + || m_selectable_back.isSelected() + || m_selectable_top.isSelected() + || m_selectable_bottom.isSelected(); + } + void setSelected(bool selected) + { + m_selectable_right.setSelected(selected); + m_selectable_left.setSelected(selected); + m_selectable_front.setSelected(selected); + m_selectable_back.setSelected(selected); + m_selectable_top.setSelected(selected); + m_selectable_bottom.setSelected(selected); + } + void selectPlanes(const AABB& aabb, Selector& selector, SelectionTest& test, const PlaneCallback& selectedPlaneCallback) + { + Line line(test.getNear(), test.getFar()); + Vector3 corners[8]; + aabb_corners(aabb, corners); + Plane3 planes[6]; + aabb_planes(aabb, planes); + + for(Vector3* i = corners; i != corners + 8; ++i) + { + *i = vector3_subtracted(line_closest_point(line, *i), *i); + } + + if(vector3_dot(planes[0].normal(), corners[1]) > 0 + && vector3_dot(planes[0].normal(), corners[2]) > 0 + && vector3_dot(planes[0].normal(), corners[5]) > 0 + && vector3_dot(planes[0].normal(), corners[6]) > 0) + { + Selector_add(selector, m_selectable_right); + selectedPlaneCallback(planes[0]); + //globalOutputStream() << "right\n"; + } + if(vector3_dot(planes[1].normal(), corners[0]) > 0 + && vector3_dot(planes[1].normal(), corners[3]) > 0 + && vector3_dot(planes[1].normal(), corners[4]) > 0 + && vector3_dot(planes[1].normal(), corners[7]) > 0) + { + Selector_add(selector, m_selectable_left); + selectedPlaneCallback(planes[1]); + //globalOutputStream() << "left\n"; + } + if(vector3_dot(planes[2].normal(), corners[0]) > 0 + && vector3_dot(planes[2].normal(), corners[1]) > 0 + && vector3_dot(planes[2].normal(), corners[4]) > 0 + && vector3_dot(planes[2].normal(), corners[5]) > 0) + { + Selector_add(selector, m_selectable_front); + selectedPlaneCallback(planes[2]); + //globalOutputStream() << "front\n"; + } + if(vector3_dot(planes[3].normal(), corners[2]) > 0 + && vector3_dot(planes[3].normal(), corners[3]) > 0 + && vector3_dot(planes[3].normal(), corners[6]) > 0 + && vector3_dot(planes[3].normal(), corners[7]) > 0) + { + Selector_add(selector, m_selectable_back); + selectedPlaneCallback(planes[3]); + //globalOutputStream() << "back\n"; + } + if(vector3_dot(planes[4].normal(), corners[0]) > 0 + && vector3_dot(planes[4].normal(), corners[1]) > 0 + && vector3_dot(planes[4].normal(), corners[2]) > 0 + && vector3_dot(planes[4].normal(), corners[3]) > 0) + { + Selector_add(selector, m_selectable_top); + selectedPlaneCallback(planes[4]); + //globalOutputStream() << "top\n"; + } + if(vector3_dot(planes[5].normal(), corners[4]) > 0 + && vector3_dot(planes[5].normal(), corners[5]) > 0 + && vector3_dot(planes[5].normal(), corners[6]) > 0 + && vector3_dot(planes[5].normal(), corners[7]) > 0) + { + Selector_add(selector, m_selectable_bottom); + //globalOutputStream() << "bottom\n"; + selectedPlaneCallback(planes[5]); + } + + m_dragPlanesMin = aabb.origin - aabb.extents; + m_dragPlanesMax = aabb.origin + aabb.extents; + m_dragPlanesOrigin = aabb.origin; + m_dragPlanesExtents = aabb.extents; + } + void selectReversedPlanes(const AABB& aabb, Selector& selector, const SelectedPlanes& selectedPlanes) + { + Plane3 planes[6]; + aabb_planes(aabb, planes); + + if(selectedPlanes.contains(plane3_flipped(planes[0]))) + { + Selector_add(selector, m_selectable_right); + } + if(selectedPlanes.contains(plane3_flipped(planes[1]))) + { + Selector_add(selector, m_selectable_left); + } + if(selectedPlanes.contains(plane3_flipped(planes[2]))) + { + Selector_add(selector, m_selectable_front); + } + if(selectedPlanes.contains(plane3_flipped(planes[3]))) + { + Selector_add(selector, m_selectable_back); + } + if(selectedPlanes.contains(plane3_flipped(planes[4]))) + { + Selector_add(selector, m_selectable_top); + } + if(selectedPlanes.contains(plane3_flipped(planes[5]))) + { + Selector_add(selector, m_selectable_bottom); + } + } + void translate(const Vector3& translation) + { + if(m_dragPlanesExtents[0] != 0) + { + if(m_selectable_right.isSelected()) + { + m_dragPlanesMax[0] += translation[0]; + //globalOutputStream() << "moving right\n"; + } + if(m_selectable_left.isSelected()) + { + m_dragPlanesMin[0] += translation[0]; + //globalOutputStream() << "moving left\n"; + } + } + if(m_dragPlanesExtents[1] != 0) + { + if(m_selectable_front.isSelected()) + { + m_dragPlanesMax[1] += translation[1]; + //globalOutputStream() << "moving front\n"; + } + if(m_selectable_back.isSelected()) + { + m_dragPlanesMin[1] += translation[1]; + //globalOutputStream() << "moving back\n"; + } + } + if(m_dragPlanesExtents[2] != 0) + { + if(m_selectable_top.isSelected()) + { + m_dragPlanesMax[2] += translation[2]; + //globalOutputStream() << "moving top\n"; + } + if(m_selectable_bottom.isSelected()) + { + m_dragPlanesMin[2] += translation[2]; + //globalOutputStream() << "moving bottom\n"; + } + } + } + Matrix4 evaluateTransform() const + { + Vector3 originTransformed(vector3_mid(m_dragPlanesMin, m_dragPlanesMax)); + Vector3 scale(vector3_scaled(vector3_subtracted(m_dragPlanesMax, m_dragPlanesMin), 0.5)); + + if(m_dragPlanesExtents[0] != 0) + { + scale[0] /= m_dragPlanesExtents[0]; + } + else + { + scale[0] = 1; + } + if(m_dragPlanesExtents[1] != 0) + { + scale[1] /= m_dragPlanesExtents[1]; + } + else + { + scale[1] = 1; + } + if(m_dragPlanesExtents[2] != 0) + { + scale[2] /= m_dragPlanesExtents[2]; + } + else + { + scale[2] = 1; + } + + Matrix4 matrix(matrix4_translation_for_vec3(originTransformed - m_dragPlanesOrigin)); + matrix4_pivoted_scale_by_vec3(matrix, scale, m_dragPlanesOrigin); + + return matrix; + } +}; + +#endif diff --git a/tools/urt/libs/eclasslib.cpp b/tools/urt/libs/eclasslib.cpp new file mode 100644 index 00000000..2a841873 --- /dev/null +++ b/tools/urt/libs/eclasslib.cpp @@ -0,0 +1,2 @@ + +#include "eclasslib.h" diff --git a/tools/urt/libs/eclasslib.h b/tools/urt/libs/eclasslib.h new file mode 100644 index 00000000..83d4fb13 --- /dev/null +++ b/tools/urt/libs/eclasslib.h @@ -0,0 +1,321 @@ + +#if !defined (INCLUDED_ECLASSLIB_H) +#define INCLUDED_ECLASSLIB_H + +#include +#include +#include +#include +#include + +#include "ieclass.h" +#include "irender.h" + +#include "math/vector.h" +#include "string/string.h" + +typedef Vector3 Colour3; + +class ListAttributeType +{ + typedef std::pair ListItem; + typedef std::vector ListItems; + ListItems m_items; +public: + + typedef ListItems::const_iterator const_iterator; + const_iterator begin() const + { + return m_items.begin(); + } + const_iterator end() const + { + return m_items.end(); + } + + const ListItem& operator[](std::size_t i) const + { + return m_items[i]; + } + const_iterator findValue(const char* value) const + { + for(ListItems::const_iterator i = m_items.begin(); i != m_items.end(); ++i) + { + if(string_equal(value, (*i).second.c_str())) + { + return i; + } + } + return m_items.end(); + } + + void push_back(const char* name, const char* value) + { + m_items.push_back(ListItems::value_type(name, value)); + } +}; + +class EntityClassAttribute +{ +public: + CopiedString m_type; + CopiedString m_name; + CopiedString m_value; + CopiedString m_description; + EntityClassAttribute() + { + } + EntityClassAttribute(const char* type, const char* name, const char* value = "", const char* description = "") : m_type(type), m_name(name), m_value(value), m_description(description) + { + } +}; + +typedef std::pair EntityClassAttributePair; +typedef std::list EntityClassAttributes; +typedef std::list StringList; + +inline const char* EntityClassAttributePair_getName(const EntityClassAttributePair& attributePair) +{ + if(!string_empty(attributePair.second.m_name.c_str())) + { + return attributePair.second.m_name.c_str(); + } + return attributePair.first.c_str(); +} + +inline const char* EntityClassAttributePair_getDescription(const EntityClassAttributePair& attributePair) +{ + if(!string_empty(attributePair.second.m_description.c_str())) + { + return attributePair.second.m_description.c_str(); + } + return EntityClassAttributePair_getName(attributePair); +} + +class EntityClass +{ +public: + CopiedString m_name; + StringList m_parent; + bool fixedsize; + bool unknown; // wasn't found in source + Vector3 mins; + Vector3 maxs; + + Colour3 color; + Shader* m_state_fill; + Shader* m_state_wire; + Shader* m_state_blend; + + CopiedString m_comments; + char flagnames[MAX_FLAGS][32]; + + CopiedString m_modelpath; + CopiedString m_skin; + + void (*free)(EntityClass*); + + EntityClassAttributes m_attributes; + + bool inheritanceResolved; + bool sizeSpecified; + bool colorSpecified; + + const char* name() const + { + return m_name.c_str(); + } + const char* comments() const + { + return m_comments.c_str(); + } + const char* modelpath() const + { + return m_modelpath.c_str(); + } + const char* skin() const + { + return m_skin.c_str(); + } +}; + +inline const char* EntityClass_valueForKey(const EntityClass& entityClass, const char* key) +{ + for(EntityClassAttributes::const_iterator i = entityClass.m_attributes.begin(); i != entityClass.m_attributes.end(); ++i) + { + if(string_equal(key, (*i).first.c_str())) + { + return (*i).second.m_value.c_str(); + } + } + return ""; +} + +inline EntityClassAttributePair& EntityClass_insertAttribute(EntityClass& entityClass, const char* key, const EntityClassAttribute& attribute = EntityClassAttribute()) +{ + entityClass.m_attributes.push_back(EntityClassAttributePair(key, attribute)); + return entityClass.m_attributes.back(); +} + + +inline void buffer_write_colour_fill(char buffer[128], const Colour3& colour) +{ + sprintf(buffer, "(%g %g %g)", colour[0], colour[1], colour[2]); +} + +inline void buffer_write_colour_wire(char buffer[128], const Colour3& colour) +{ + sprintf(buffer, "<%g %g %g>", colour[0], colour[1], colour[2]); +} + +inline void buffer_write_colour_blend(char buffer[128], const Colour3& colour) +{ + sprintf(buffer, "[%g %g %g]", colour[0], colour[1], colour[2]); +} + +inline Shader* colour_capture_state_fill(const Colour3& colour) +{ + char buffer[128]; + buffer_write_colour_fill(buffer, colour); + return GlobalShaderCache().capture(buffer); +} + +inline void colour_release_state_fill(const Colour3& colour) +{ + char buffer[128]; + buffer_write_colour_fill(buffer, colour); + GlobalShaderCache().release(buffer); +} + +inline Shader* colour_capture_state_wire(const Colour3& colour) +{ + char buffer[128]; + buffer_write_colour_wire(buffer, colour); + return GlobalShaderCache().capture(buffer); +} + +inline void colour_release_state_wire(const Colour3& colour) +{ + char buffer[128]; + buffer_write_colour_wire(buffer, colour); + GlobalShaderCache().release(buffer); +} + +inline Shader* colour_capture_state_blend(const Colour3& colour) +{ + char buffer[128]; + buffer_write_colour_blend(buffer, colour); + return GlobalShaderCache().capture(buffer); +} + +inline void colour_release_state_blend(const Colour3& colour) +{ + char buffer[128]; + buffer_write_colour_blend(buffer, colour); + GlobalShaderCache().release(buffer); +} + +inline void eclass_capture_state(EntityClass* eclass) +{ + eclass->m_state_fill = colour_capture_state_fill(eclass->color); + eclass->m_state_wire = colour_capture_state_wire(eclass->color); + eclass->m_state_blend = colour_capture_state_blend(eclass->color); +} + +inline void eclass_release_state(EntityClass* eclass) +{ + colour_release_state_fill(eclass->color); + colour_release_state_wire(eclass->color); + colour_release_state_blend(eclass->color); +} + +// eclass constructor +inline EntityClass* Eclass_Alloc() +{ + EntityClass* e = new EntityClass; + + e->fixedsize = false; + e->unknown = false; + memset(e->flagnames, 0, MAX_FLAGS*32); + + e->maxs = Vector3(-1,-1,-1); + e->mins = Vector3(1, 1, 1); + + e->free = 0; + + e->inheritanceResolved = true; + e->sizeSpecified = false; + e->colorSpecified = false; + + return e; +} + +// eclass destructor +inline void Eclass_Free(EntityClass* e) +{ + eclass_release_state(e); + + delete e; +} + +inline bool classname_equal(const char* classname, const char* other) +{ + return string_equal(classname, other); +} + +inline EntityClass* EClass_Create(const char* name, const Vector3& colour, const char* comments) +{ + EntityClass *e = Eclass_Alloc(); + e->free = &Eclass_Free; + + e->m_name = name; + + e->color = colour; + eclass_capture_state(e); + + if (comments) + e->m_comments = comments; + + return e; +} + +inline EntityClass* EClass_Create_FixedSize(const char* name, const Vector3& colour, const Vector3& mins, const Vector3& maxs, const char* comments) +{ + EntityClass *e = Eclass_Alloc(); + e->free = &Eclass_Free; + + e->m_name = name; + + e->color = colour; + eclass_capture_state(e); + + e->fixedsize = true; + + e->mins = mins; + e->maxs = maxs; + + if (comments) + e->m_comments = comments; + + return e; +} + +const Vector3 smallbox[2] = { + Vector3(-8,-8,-8), + Vector3( 8, 8, 8), +}; + +inline EntityClass *EntityClass_Create_Default(const char *name, bool has_brushes) +{ + // create a new class for it + if (has_brushes) + { + return EClass_Create(name, Vector3(0.0f, 0.5f, 0.0f), "Not found in source."); + } + else + { + return EClass_Create_FixedSize(name, Vector3(0.0f, 0.5f, 0.0f), smallbox[0], smallbox[1], "Not found in source."); + } +} + +#endif diff --git a/tools/urt/libs/entitylib.cpp b/tools/urt/libs/entitylib.cpp new file mode 100644 index 00000000..c58a7dcd --- /dev/null +++ b/tools/urt/libs/entitylib.cpp @@ -0,0 +1,2 @@ + +#include "entitylib.h" diff --git a/tools/urt/libs/entitylib.h b/tools/urt/libs/entitylib.h new file mode 100644 index 00000000..aa6103b4 --- /dev/null +++ b/tools/urt/libs/entitylib.h @@ -0,0 +1,726 @@ + +#if !defined (INCLUDED_ENTITYLIB_H) +#define INCLUDED_ENTITYLIB_H + +#include "ireference.h" +#include "debugging/debugging.h" + +#include "ientity.h" +#include "irender.h" +#include "igl.h" +#include "selectable.h" + +#include "generic/callback.h" +#include "math/vector.h" +#include "math/aabb.h" +#include "undolib.h" +#include "string/string.h" +#include "generic/referencecounted.h" +#include "scenelib.h" +#include "container/container.h" +#include "eclasslib.h" + +#include +#include + +inline void arrow_draw(const Vector3& origin, const Vector3& direction) +{ + Vector3 up(0, 0, 1); + Vector3 left(-direction[1], direction[0], 0); + + Vector3 endpoint(vector3_added(origin, vector3_scaled(direction, 32.0))); + + Vector3 tip1(vector3_added(vector3_added(endpoint, vector3_scaled(direction, -8.0)), vector3_scaled(up, -4.0))); + Vector3 tip2(vector3_added(tip1, vector3_scaled(up, 8.0))); + Vector3 tip3(vector3_added(vector3_added(endpoint, vector3_scaled(direction, -8.0)), vector3_scaled(left, -4.0))); + Vector3 tip4(vector3_added(tip3, vector3_scaled(left, 8.0))); + + glBegin (GL_LINES); + + glVertex3fv(vector3_to_array(origin)); + glVertex3fv(vector3_to_array(endpoint)); + + glVertex3fv(vector3_to_array(endpoint)); + glVertex3fv(vector3_to_array(tip1)); + + glVertex3fv(vector3_to_array(endpoint)); + glVertex3fv(vector3_to_array(tip2)); + + glVertex3fv(vector3_to_array(endpoint)); + glVertex3fv(vector3_to_array(tip3)); + + glVertex3fv(vector3_to_array(endpoint)); + glVertex3fv(vector3_to_array(tip4)); + + glVertex3fv(vector3_to_array(tip1)); + glVertex3fv(vector3_to_array(tip3)); + + glVertex3fv(vector3_to_array(tip3)); + glVertex3fv(vector3_to_array(tip2)); + + glVertex3fv(vector3_to_array(tip2)); + glVertex3fv(vector3_to_array(tip4)); + + glVertex3fv(vector3_to_array(tip4)); + glVertex3fv(vector3_to_array(tip1)); + + glEnd(); +} + +class SelectionIntersection; + +inline void aabb_testselect(const AABB& aabb, SelectionTest& test, SelectionIntersection& best) +{ + const IndexPointer::index_type indices[24] = { + 2, 1, 5, 6, + 1, 0, 4, 5, + 0, 1, 2, 3, + 3, 7, 4, 0, + 3, 2, 6, 7, + 7, 6, 5, 4, + }; + + Vector3 points[8]; + aabb_corners(aabb, points); + test.TestQuads(VertexPointer(reinterpret_cast(points), sizeof(Vector3)), IndexPointer(indices, 24), best); +} + +inline void aabb_draw_wire(const Vector3 points[8]) +{ + typedef std::size_t index_t; + index_t indices[24] = { + 0, 1, 1, 2, 2, 3, 3, 0, + 4, 5, 5, 6, 6, 7, 7, 4, + 0, 4, 1, 5, 2, 6, 3, 7, + }; +#if 1 + glVertexPointer(3, GL_FLOAT, 0, points); + glDrawElements(GL_LINES, sizeof(indices)/sizeof(index_t), GL_UNSIGNED_INT, indices); +#else + glBegin(GL_LINES); + for(std::size_t i = 0; i < sizeof(indices)/sizeof(index_t); ++i) + { + glVertex3fv(points[indices[i]]); + } + glEnd(); +#endif +} + +inline void aabb_draw_flatshade(const Vector3 points[8]) +{ + glBegin(GL_QUADS); + + glNormal3fv(vector3_to_array(aabb_normals[0])); + glVertex3fv(vector3_to_array(points[2])); + glVertex3fv(vector3_to_array(points[1])); + glVertex3fv(vector3_to_array(points[5])); + glVertex3fv(vector3_to_array(points[6])); + + glNormal3fv(vector3_to_array(aabb_normals[1])); + glVertex3fv(vector3_to_array(points[1])); + glVertex3fv(vector3_to_array(points[0])); + glVertex3fv(vector3_to_array(points[4])); + glVertex3fv(vector3_to_array(points[5])); + + glNormal3fv(vector3_to_array(aabb_normals[2])); + glVertex3fv(vector3_to_array(points[0])); + glVertex3fv(vector3_to_array(points[1])); + glVertex3fv(vector3_to_array(points[2])); + glVertex3fv(vector3_to_array(points[3])); + + glNormal3fv(vector3_to_array(aabb_normals[3])); + glVertex3fv(vector3_to_array(points[0])); + glVertex3fv(vector3_to_array(points[3])); + glVertex3fv(vector3_to_array(points[7])); + glVertex3fv(vector3_to_array(points[4])); + + glNormal3fv(vector3_to_array(aabb_normals[4])); + glVertex3fv(vector3_to_array(points[3])); + glVertex3fv(vector3_to_array(points[2])); + glVertex3fv(vector3_to_array(points[6])); + glVertex3fv(vector3_to_array(points[7])); + + glNormal3fv(vector3_to_array(aabb_normals[5])); + glVertex3fv(vector3_to_array(points[7])); + glVertex3fv(vector3_to_array(points[6])); + glVertex3fv(vector3_to_array(points[5])); + glVertex3fv(vector3_to_array(points[4])); + + glEnd(); +} + +inline void aabb_draw_wire(const AABB& aabb) +{ + Vector3 points[8]; + aabb_corners(aabb, points); + aabb_draw_wire(points); +} + +inline void aabb_draw_flatshade(const AABB& aabb) +{ + Vector3 points[8]; + aabb_corners(aabb, points); + aabb_draw_flatshade(points); +} + +inline void aabb_draw_textured(const AABB& aabb) +{ + Vector3 points[8]; + aabb_corners(aabb, points); + + glBegin(GL_QUADS); + + glNormal3fv(vector3_to_array(aabb_normals[0])); + glTexCoord2fv(aabb_texcoord_topleft); + glVertex3fv(vector3_to_array(points[2])); + glTexCoord2fv(aabb_texcoord_topright); + glVertex3fv(vector3_to_array(points[1])); + glTexCoord2fv(aabb_texcoord_botright); + glVertex3fv(vector3_to_array(points[5])); + glTexCoord2fv(aabb_texcoord_botleft); + glVertex3fv(vector3_to_array(points[6])); + + glNormal3fv(vector3_to_array(aabb_normals[1])); + glTexCoord2fv(aabb_texcoord_topleft); + glVertex3fv(vector3_to_array(points[1])); + glTexCoord2fv(aabb_texcoord_topright); + glVertex3fv(vector3_to_array(points[0])); + glTexCoord2fv(aabb_texcoord_botright); + glVertex3fv(vector3_to_array(points[4])); + glTexCoord2fv(aabb_texcoord_botleft); + glVertex3fv(vector3_to_array(points[5])); + + glNormal3fv(vector3_to_array(aabb_normals[2])); + glTexCoord2fv(aabb_texcoord_topleft); + glVertex3fv(vector3_to_array(points[0])); + glTexCoord2fv(aabb_texcoord_topright); + glVertex3fv(vector3_to_array(points[1])); + glTexCoord2fv(aabb_texcoord_botright); + glVertex3fv(vector3_to_array(points[2])); + glTexCoord2fv(aabb_texcoord_botleft); + glVertex3fv(vector3_to_array(points[3])); + + glNormal3fv(vector3_to_array(aabb_normals[3])); + glTexCoord2fv(aabb_texcoord_topleft); + glVertex3fv(vector3_to_array(points[0])); + glTexCoord2fv(aabb_texcoord_topright); + glVertex3fv(vector3_to_array(points[3])); + glTexCoord2fv(aabb_texcoord_botright); + glVertex3fv(vector3_to_array(points[7])); + glTexCoord2fv(aabb_texcoord_botleft); + glVertex3fv(vector3_to_array(points[4])); + + glNormal3fv(vector3_to_array(aabb_normals[4])); + glTexCoord2fv(aabb_texcoord_topleft); + glVertex3fv(vector3_to_array(points[3])); + glTexCoord2fv(aabb_texcoord_topright); + glVertex3fv(vector3_to_array(points[2])); + glTexCoord2fv(aabb_texcoord_botright); + glVertex3fv(vector3_to_array(points[6])); + glTexCoord2fv(aabb_texcoord_botleft); + glVertex3fv(vector3_to_array(points[7])); + + glNormal3fv(vector3_to_array(aabb_normals[5])); + glTexCoord2fv(aabb_texcoord_topleft); + glVertex3fv(vector3_to_array(points[7])); + glTexCoord2fv(aabb_texcoord_topright); + glVertex3fv(vector3_to_array(points[6])); + glTexCoord2fv(aabb_texcoord_botright); + glVertex3fv(vector3_to_array(points[5])); + glTexCoord2fv(aabb_texcoord_botleft); + glVertex3fv(vector3_to_array(points[4])); + + glEnd(); +} + +inline void aabb_draw_solid(const AABB& aabb, RenderStateFlags state) +{ + if(state & RENDER_TEXTURE) + { + aabb_draw_textured(aabb); + } + else + { + aabb_draw_flatshade(aabb); + } +} + +inline void aabb_draw(const AABB& aabb, RenderStateFlags state) +{ + if(state & RENDER_FILL) + { + aabb_draw_solid(aabb, state); + } + else + { + aabb_draw_wire(aabb); + } +} + +class RenderableSolidAABB : public OpenGLRenderable +{ + const AABB& m_aabb; +public: + RenderableSolidAABB(const AABB& aabb) : m_aabb(aabb) + { + } + void render(RenderStateFlags state) const + { + aabb_draw_solid(m_aabb, state); + } +}; + +class RenderableWireframeAABB : public OpenGLRenderable +{ + const AABB& m_aabb; +public: + RenderableWireframeAABB(const AABB& aabb) : m_aabb(aabb) + { + } + void render(RenderStateFlags state) const + { + aabb_draw_wire(m_aabb); + } +}; + + +typedef Callback1 KeyObserver; + +/// \brief A key/value pair of strings. +/// +/// - Notifies observers when value changes - value changes to "" on destruction. +/// - Provides undo support through the global undo system. +class KeyValue +{ + typedef UnsortedSet KeyObservers; + + std::size_t m_refcount; + KeyObservers m_observers; + CopiedString m_string; + const char* m_empty; + ObservedUndoableObject m_undo; + static EntityCreator::KeyValueChangedFunc m_entityKeyValueChanged; +public: + + KeyValue(const char* string, const char* empty) + : m_refcount(0), m_string(string), m_empty(empty), m_undo(m_string, UndoImportCaller(*this)) + { + notify(); + } + ~KeyValue() + { + ASSERT_MESSAGE(m_observers.empty(), "KeyValue::~KeyValue: observers still attached"); + } + + static void setKeyValueChangedFunc(EntityCreator::KeyValueChangedFunc func) + { + m_entityKeyValueChanged = func; + } + + void IncRef() + { + ++m_refcount; + } + void DecRef() + { + if(--m_refcount == 0) + { + delete this; + } + } + + void instanceAttach(MapFile* map) + { + m_undo.instanceAttach(map); + } + void instanceDetach(MapFile* map) + { + m_undo.instanceDetach(map); + } + + void attach(const KeyObserver& observer) + { + (*m_observers.insert(observer))(c_str()); + } + void detach(const KeyObserver& observer) + { + observer(m_empty); + m_observers.erase(observer); + } + const char* c_str() const + { + if(string_empty(m_string.c_str())) + { + return m_empty; + } + return m_string.c_str(); + } + void assign(const char* other) + { + if(!string_equal(m_string.c_str(), other)) + { + m_undo.save(); + m_string = other; + notify(); + } + } + + void notify() + { + m_entityKeyValueChanged(); + KeyObservers::reverse_iterator i = m_observers.rbegin(); + while(i != m_observers.rend()) + { + (*i++)(c_str()); + } + } + + void importState(const CopiedString& string) + { + m_string = string; + + notify(); + } + typedef MemberCaller1 UndoImportCaller; +}; + +/// \brief An unsorted list of key/value pairs. +/// +/// - Notifies observers when a pair is inserted or removed. +/// - Provides undo support through the global undo system. +/// - New keys are appended to the end of the list. +class EntityKeyValues : public Entity +{ +public: + typedef KeyValue Value; + + class Observer + { + public: + virtual void insert(const char* key, Value& value) = 0; + virtual void erase(const char* key, Value& value) = 0; + }; + +private: + static EntityCreator::KeyValueChangedFunc m_entityKeyValueChanged; + static Counter* m_counter; + + EntityClass* m_eclass; + + typedef SmartPointer KeyValuePtr; + typedef UnsortedMap KeyValues; + KeyValues m_keyValues; + + typedef UnsortedSet Observers; + Observers m_observers; + + ObservedUndoableObject m_undo; + bool m_instanced; + + bool m_observerMutex; + + void notifyInsert(const char* key, Value& value) + { + m_observerMutex = true; + for(Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i) + { + (*i)->insert(key, value); + } + m_observerMutex = false; + } + void notifyErase(const char* key, Value& value) + { + m_observerMutex = true; + for(Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i) + { + (*i)->erase(key, value); + } + m_observerMutex = false; + } + void forEachKeyValue_notifyInsert() + { + for(KeyValues::const_iterator i = m_keyValues.begin(); i != m_keyValues.end(); ++i) + { + notifyInsert((*i).first.c_str(), *(*i).second); + } + } + void forEachKeyValue_notifyErase() + { + for(KeyValues::const_iterator i = m_keyValues.begin(); i != m_keyValues.end(); ++i) + { + notifyErase((*i).first.c_str(), *(*i).second); + } + } + + void insert(const char* key, const KeyValuePtr& keyValue) + { + KeyValues::iterator i = m_keyValues.insert(KeyValues::value_type(key, keyValue)); + notifyInsert(key, *(*i).second); + + if(m_instanced) + { + (*i).second->instanceAttach(m_undo.map()); + } + } + + void insert(const char* key, const char* value) + { + KeyValues::iterator i = m_keyValues.find(key); + if(i != m_keyValues.end()) + { + (*i).second->assign(value); + } + else + { + m_undo.save(); + insert(key, KeyValuePtr(new KeyValue(value, EntityClass_valueForKey(*m_eclass, key)))); + } + } + + void erase(KeyValues::iterator i) + { + if(m_instanced) + { + (*i).second->instanceDetach(m_undo.map()); + } + + CopiedString key((*i).first); + KeyValuePtr value((*i).second); + m_keyValues.erase(i); + notifyErase(key.c_str(), *value); + } + + void erase(const char* key) + { + KeyValues::iterator i = m_keyValues.find(key); + if(i != m_keyValues.end()) + { + m_undo.save(); + erase(i); + } + } + +public: + EntityKeyValues(EntityClass* eclass) : + m_eclass(eclass), + m_undo(m_keyValues, UndoImportCaller(*this)), + m_instanced(false), + m_observerMutex(false) + { + } + EntityKeyValues(const EntityKeyValues& other) : + Entity(other), + m_eclass(&other.getEntityClass()), + m_undo(m_keyValues, UndoImportCaller(*this)), + m_instanced(false), + m_observerMutex(false) + { + for(KeyValues::const_iterator i = other.m_keyValues.begin(); i != other.m_keyValues.end(); ++i) + { + insert((*i).first.c_str(), (*i).second->c_str()); + } + } + ~EntityKeyValues() + { + ASSERT_MESSAGE(m_observers.empty(), "EntityKeyValues::~EntityKeyValues: observers still attached"); + } + + static void setKeyValueChangedFunc(EntityCreator::KeyValueChangedFunc func) + { + m_entityKeyValueChanged = func; + KeyValue::setKeyValueChangedFunc(func); + } + static void setCounter(Counter* counter) + { + m_counter = counter; + } + + void importState(const KeyValues& keyValues) + { + for(KeyValues::iterator i = m_keyValues.begin(); i != m_keyValues.end();) + { + erase(i++); + } + + for(KeyValues::const_iterator i = keyValues.begin(); i != keyValues.end(); ++i) + { + insert((*i).first.c_str(), (*i).second); + } + + m_entityKeyValueChanged(); + } + typedef MemberCaller1 UndoImportCaller; + + void attach(Observer& observer) + { + ASSERT_MESSAGE(!m_observerMutex, "observer cannot be attached during iteration"); + m_observers.insert(&observer); + for(KeyValues::const_iterator i = m_keyValues.begin(); i != m_keyValues.end(); ++i) + { + observer.insert((*i).first.c_str(), *(*i).second); + } + } + void detach(Observer& observer) + { + ASSERT_MESSAGE(!m_observerMutex, "observer cannot be detached during iteration"); + m_observers.erase(&observer); + for(KeyValues::const_iterator i = m_keyValues.begin(); i != m_keyValues.end(); ++i) + { + observer.erase((*i).first.c_str(), *(*i).second); + } + } + + void forEachKeyValue_instanceAttach(MapFile* map) + { + for(KeyValues::const_iterator i = m_keyValues.begin(); i != m_keyValues.end(); ++i) + { + (*i).second->instanceAttach(map); + } + } + void forEachKeyValue_instanceDetach(MapFile* map) + { + for(KeyValues::const_iterator i = m_keyValues.begin(); i != m_keyValues.end(); ++i) + { + (*i).second->instanceDetach(map); + } + } + + void instanceAttach(MapFile* map) + { + if(m_counter != 0) + { + m_counter->increment(); + } + + m_instanced = true; + forEachKeyValue_instanceAttach(map); + m_undo.instanceAttach(map); + } + void instanceDetach(MapFile* map) + { + if(m_counter != 0) + { + m_counter->decrement(); + } + + m_undo.instanceDetach(map); + forEachKeyValue_instanceDetach(map); + m_instanced = false; + } + + // entity + EntityClass& getEntityClass() const + { + return *m_eclass; + } + void forEachKeyValue(Visitor& visitor) const + { + for(KeyValues::const_iterator i = m_keyValues.begin(); i != m_keyValues.end(); ++i) + { + visitor.visit((*i).first.c_str(), (*i).second->c_str()); + } + } + void setKeyValue(const char* key, const char* value) + { + if(value[0] == '\0' + /*|| string_equal(EntityClass_valueForKey(*m_eclass, key), value)*/) // don't delete values equal to default + { + erase(key); + } + else + { + insert(key, value); + } + m_entityKeyValueChanged(); + } + const char* getKeyValue(const char* key) const + { + KeyValues::const_iterator i = m_keyValues.find(key); + if(i != m_keyValues.end()) + { + return (*i).second->c_str(); + } + + return EntityClass_valueForKey(*m_eclass, key); + } +}; + +/// \brief A Resource reference with a controlled lifetime. +/// \brief The resource is released when the ResourceReference is destroyed. +class ResourceReference +{ + CopiedString m_name; + Resource* m_resource; +public: + ResourceReference(const char* name) + : m_name(name) + { + capture(); + } + ResourceReference(const ResourceReference& other) + : m_name(other.m_name) + { + capture(); + } + ResourceReference& operator=(const ResourceReference& other) + { + ResourceReference tmp(other); + tmp.swap(*this); + return *this; + } + ~ResourceReference() + { + release(); + } + + void capture() + { + m_resource = GlobalReferenceCache().capture(m_name.c_str()); + } + void release() + { + GlobalReferenceCache().release(m_name.c_str()); + } + + const char* getName() const + { + return m_name.c_str(); + } + void setName(const char* name) + { + ResourceReference tmp(name); + tmp.swap(*this); + } + + void swap(ResourceReference& other) + { + std::swap(m_resource, other.m_resource); + std::swap(m_name, other.m_name); + } + + void attach(ModuleObserver& observer) + { + m_resource->attach(observer); + } + void detach(ModuleObserver& observer) + { + m_resource->detach(observer); + } + + Resource* get() + { + return m_resource; + } +}; + +namespace std +{ + /// \brief Swaps the values of \p self and \p other. + /// Overloads std::swap. + inline void swap(ResourceReference& self, ResourceReference& other) + { + self.swap(other); + } +} + +#endif diff --git a/tools/urt/libs/entityxml.cpp b/tools/urt/libs/entityxml.cpp new file mode 100644 index 00000000..ccb9bcae --- /dev/null +++ b/tools/urt/libs/entityxml.cpp @@ -0,0 +1,2 @@ + +#include "entityxml.h" diff --git a/tools/urt/libs/entityxml.h b/tools/urt/libs/entityxml.h new file mode 100644 index 00000000..609a1b21 --- /dev/null +++ b/tools/urt/libs/entityxml.h @@ -0,0 +1,88 @@ + +#if !defined(INCLUDED_ENTITYXML_H) +#define INCLUDED_ENTITYXML_H + +#include "ientity.h" +#include "xml/ixml.h" +#include "xml/xmlelement.h" + +class entity_import : public XMLImporter +{ + Entity& m_entity; +public: + entity_import(Entity& entity) + : m_entity(entity) + { + } + void pushElement(const XMLElement& element) + { + if(strcmp(element.name(), "epair") == 0) + m_entity.setKeyValue(element.attribute("key"), element.attribute("value")); + } + void popElement(const char* name) + { + } + std::size_t write(const char* data, std::size_t length) + { + return length; + } +}; + +class entity_export : public XMLExporter +{ + class ExportXMLVisitor : public Entity::Visitor + { + XMLImporter& m_importer; + public: + ExportXMLVisitor(XMLImporter& importer) : m_importer(importer) + { + } + void visit(const char* key, const char* value) + { + StaticElement element("epair"); + element.insertAttribute("key", key); + element.insertAttribute("value", value); + m_importer.pushElement(element); + m_importer.popElement(element.name()); + } + }; + + const Entity& m_entity; + +public: + entity_export(const Entity& entity) : m_entity(entity) + { + } + void exportXML(XMLImporter& observer) + { + ExportXMLVisitor visitor(observer); + + m_entity.forEachKeyValue(visitor); + } +}; + +inline void entity_copy(Entity& entity, const Entity& other) +{ + entity_export exporter(other); + entity_import importer(entity); + exporter.exportXML(importer); +} + +template +class EntityConstruction +{ +public: + typedef EntityClass* type; + static type get(const EntityType& entity) + { + return &entity.getEntity().getEntityClass(); + } + static void copy(EntityType& entity, const EntityType& other) + { + entity_copy(entity.getEntity(), other.getEntity()); + } +}; + + + +#endif diff --git a/tools/urt/libs/fs_filesystem.cpp b/tools/urt/libs/fs_filesystem.cpp new file mode 100644 index 00000000..fac81413 --- /dev/null +++ b/tools/urt/libs/fs_filesystem.cpp @@ -0,0 +1,2 @@ + +#include "fs_filesystem.h" diff --git a/tools/urt/libs/fs_filesystem.h b/tools/urt/libs/fs_filesystem.h new file mode 100644 index 00000000..ffcef612 --- /dev/null +++ b/tools/urt/libs/fs_filesystem.h @@ -0,0 +1,160 @@ + +#if !defined(INCLUDED_FS_FILESYSTEM_H) +#define INCLUDED_FS_FILESYSTEM_H + +#include "string/string.h" +#include "os/path.h" + +#include + +inline unsigned int path_get_depth(const char* path) +{ + unsigned int depth = 0; + while(path != 0 && path[0] != '\0') + { + path = strchr(path, '/'); + if(path != 0) + { + ++path; + } + ++depth; + } + return depth; +} + +/// \brief A generic unix-style file-system which maps paths to files and directories. +/// Provides average O(log n) find and insert methods. +/// \param file_type The data type which represents a file. +template +class GenericFileSystem +{ + class Path + { + CopiedString m_path; + unsigned int m_depth; + public: + Path(const char* path) + : m_path(path), m_depth(path_get_depth(c_str())) + { + } + Path(const char* start, const char* finish) + : m_path(start, finish), m_depth(path_get_depth(c_str())) + { + } + bool operator<(const Path& other) const + { + return string_less_nocase(c_str(), other.c_str()); + } + unsigned int depth() const + { + return m_depth; + } + const char* c_str() const + { + return m_path.c_str(); + } + }; + + class Entry + { + file_type* m_file; + public: + Entry(file_type* file) + : m_file(file) + { + } + file_type* file() const + { + return m_file; + } + bool is_directory() const + { + return file() == 0; + } + }; + + typedef std::map Entries; + Entries m_entries; + +public: + typedef typename Entries::iterator iterator; + typedef typename Entries::value_type value_type; + + iterator begin() + { + return m_entries.begin(); + } + iterator end() + { + return m_entries.end(); + } + + /// \brief Adds the file \p entry at \p path. + /// Creates all directories below \p path if they do not exist. + /// O(log n) on average. + void insert(const Path& path, const Entry& entry) + { + { + const char* end = path_remove_directory(path.c_str()); + while(end[0] != '\0') + { + Path dir(path.c_str(), end); + m_entries.insert(value_type(dir, Entry(0))); + end = path_remove_directory(end); + } + } + + m_entries.insert(value_type(path, entry)); + } + + /// \brief Returns the file at \p path or end() if not found. + iterator find(const Path& path) + { + return m_entries.find(path); + } + + iterator begin(const char* root) + { + if(root[0] == '\0') + { + return m_entries.begin(); + } + iterator i = m_entries.find(root); + if(i == m_entries.end()) + { + return i; + } + return ++i; + } + + /// \brief Performs a depth-first traversal of the file-system subtree rooted at \p root. + /// Traverses the entire tree if \p root is "". + /// Calls \p visitor.file() with the path to each file relative to the filesystem root. + /// Calls \p visitor.directory() with the path to each directory relative to the filesystem root. + template + void traverse(visitor_type visitor, const char* root) + { + unsigned int start_depth = path_get_depth(root); + unsigned int skip_depth = 0; + for(iterator i = begin(root); i != end() && i->first.depth() > start_depth; ++i) + { + if(i->first.depth() == skip_depth) + { + skip_depth = 0; + } + if(skip_depth == 0) + { + if(!i->second.is_directory()) + { + visitor.file(i->first.c_str()); + } + else if(visitor.directory(i->first.c_str(), i->first.depth() - start_depth)) + { + skip_depth = i->first.depth(); + } + } + } + } +}; + +#endif diff --git a/tools/urt/libs/fs_path.cpp b/tools/urt/libs/fs_path.cpp new file mode 100644 index 00000000..4017c66f --- /dev/null +++ b/tools/urt/libs/fs_path.cpp @@ -0,0 +1,2 @@ + +#include "fs_path.h" diff --git a/tools/urt/libs/fs_path.h b/tools/urt/libs/fs_path.h new file mode 100644 index 00000000..a8fafa57 --- /dev/null +++ b/tools/urt/libs/fs_path.h @@ -0,0 +1,72 @@ + +#if !defined(INCLUDED_FS_PATH_H) +#define INCLUDED_FS_PATH_H + +#include "stream/stringstream.h" + +/// \brief A unix-style path string which can be modified at runtime. +/// +/// - Maintains a path ending in a path-separator. +/// - Provides a limited STL-style interface to push and pop file or directory names at the end of the path. +class UnixPath +{ + StringBuffer m_string; + + void check_separator() + { + if(!empty() && m_string.back() != '/') + { + m_string.push_back('/'); + } + } + +public: + /// \brief Constructs with the directory \p root. + UnixPath(const char* root) + : m_string(root) + { + check_separator(); + } + + bool empty() const + { + return m_string.empty(); + } + + const char* c_str() const + { + return m_string.c_str(); + } + + /// \brief Appends the directory \p name. + void push(const char* name) + { + m_string.push_string(name); + check_separator(); + } + /// \brief Appends the directory [\p first, \p last). + void push(const char* first, const char* last) + { + m_string.push_range(first, last); + check_separator(); + } + /// \brief Appends the filename \p name. + void push_filename(const char* name) + { + m_string.push_string(name); + } + /// \brief Removes the last directory or filename appended. + void pop() + { + if(m_string.back() == '/') + { + m_string.pop_back(); + } + while(!empty() && m_string.back() != '/') + { + m_string.pop_back(); + } + } +}; + +#endif diff --git a/tools/urt/libs/generic/arrayrange.cpp b/tools/urt/libs/generic/arrayrange.cpp new file mode 100644 index 00000000..58f717dc --- /dev/null +++ b/tools/urt/libs/generic/arrayrange.cpp @@ -0,0 +1,3 @@ + +#include "arrayrange.h" + diff --git a/tools/urt/libs/generic/arrayrange.h b/tools/urt/libs/generic/arrayrange.h new file mode 100644 index 00000000..e1eae81e --- /dev/null +++ b/tools/urt/libs/generic/arrayrange.h @@ -0,0 +1,52 @@ + +#if !defined(INCLUDED_GENERIC_ARRAYRANGE_H) +#define INCLUDED_GENERIC_ARRAYRANGE_H + +/// \file +/// \brief Macros for automatically converting a compile-time-sized array to a range. + +template +struct ArrayRange +{ + typedef Element* Iterator; + ArrayRange(Iterator _begin, Iterator _end) + : begin(_begin), end(_end) + { + } + Iterator begin; + Iterator end; +}; + +template +inline ArrayRange makeArrayRange(Element* begin, Element* end) +{ + return ArrayRange(begin, end); +} + +template +struct ArrayConstRange +{ + typedef const Element* Iterator; + ArrayConstRange(Iterator _begin, Iterator _end) + : begin(_begin), end(_end) + { + } + Iterator begin; + Iterator end; +}; + +template +inline ArrayConstRange makeArrayRange(const Element* begin, const Element* end) +{ + return ArrayConstRange(begin, end); +} + +#define ARRAY_SIZE(array) (sizeof(array) / sizeof(*array)) +#define ARRAY_END(array) (array + ARRAY_SIZE(array)) +#define ARRAY_RANGE(array) (makeArrayRange(array, ARRAY_END(array))) + + +typedef ArrayConstRange StringArrayRange; +#define STRING_ARRAY_RANGE(array) (StringArrayRange(array, ARRAY_END(array))) + +#endif diff --git a/tools/urt/libs/generic/bitfield.cpp b/tools/urt/libs/generic/bitfield.cpp new file mode 100644 index 00000000..eb1d3f42 --- /dev/null +++ b/tools/urt/libs/generic/bitfield.cpp @@ -0,0 +1,3 @@ + +#include "bitfield.h" + diff --git a/tools/urt/libs/generic/bitfield.h b/tools/urt/libs/generic/bitfield.h new file mode 100644 index 00000000..29aee840 --- /dev/null +++ b/tools/urt/libs/generic/bitfield.h @@ -0,0 +1,113 @@ + +#if !defined(INCLUDED_GENERIC_BITFIELD_H) +#define INCLUDED_GENERIC_BITFIELD_H + +/// \file +/// \brief Type safe bitfield. + +/// \brief A bit-field value. +/// +/// - Can be forward-declared when the definition of Enumeration is unknown. +/// - Can only be constructed from valid enumerated values. +/// - Can only be compared and combined with others of the same type. +/// +/// \param Enumeration A type that contains an enum \c Value of the bits that can be set in this field. +template +class BitFieldValue : public Enumeration +{ + unsigned m_value; +protected: + explicit BitFieldValue(unsigned value) : m_value(value) + { + } +public: + BitFieldValue() : m_value(0) + { + } + explicit BitFieldValue(typename Enumeration::Value value) : m_value(1 << value) + { + } + unsigned get() const + { + return m_value; + } +}; + +template +class BitFieldValueUnsafe : public BitFieldValue +{ +public: + explicit BitFieldValueUnsafe(unsigned value) : BitFieldValue(value) + { + } +}; + +template +inline bool operator==(BitFieldValue self, BitFieldValue other) +{ + return self.get() == other.get(); +} +template +inline bool operator!=(BitFieldValue self, BitFieldValue other) +{ + return !operator==(self, other); +} + +template +inline BitFieldValue operator|(BitFieldValue self, BitFieldValue other) +{ + return BitFieldValueUnsafe(self.get() | other.get()); +} +template +inline BitFieldValue& operator|=(BitFieldValue& self, BitFieldValue other) +{ + return self = self | other; +} +template +inline BitFieldValue operator&(BitFieldValue self, BitFieldValue other) +{ + return BitFieldValueUnsafe(self.get() & other.get()); +} +template +inline BitFieldValue& operator&=(BitFieldValue& self, BitFieldValue other) +{ + return self = self & other; +} +template +inline BitFieldValue operator~(BitFieldValue self) +{ + return BitFieldValueUnsafe(~self.get()); +} + + + +inline unsigned int bitfield_enable(unsigned int bitfield, unsigned int mask) +{ + return bitfield | mask; +} +inline unsigned int bitfield_disable(unsigned int bitfield, unsigned int mask) +{ + return bitfield & ~mask; +} +inline bool bitfield_enabled(unsigned int bitfield, unsigned int mask) +{ + return (bitfield & mask) != 0; +} + +template +inline BitFieldValue bitfield_enable(BitFieldValue bitfield, BitFieldValue mask) +{ + return bitfield | mask; +} +template +inline BitFieldValue bitfield_disable(BitFieldValue bitfield, BitFieldValue mask) +{ + return bitfield & ~mask; +} +template +inline bool bitfield_enabled(BitFieldValue bitfield, BitFieldValue mask) +{ + return (bitfield & mask).get() != 0; +} + +#endif diff --git a/tools/urt/libs/generic/callback.cpp b/tools/urt/libs/generic/callback.cpp new file mode 100644 index 00000000..11e3be89 --- /dev/null +++ b/tools/urt/libs/generic/callback.cpp @@ -0,0 +1,89 @@ + +#include "callback.h" + +#if defined(_DEBUG) || defined(DOXYGEN) + +namespace ExampleMemberCaller +{ + // MemberCaller example + class Integer + { + public: + int value; + + void printValue() const + { + // print this->value here; + } + + void setValue() + { + value = 3; + } + // a typedef to make things more readable + typedef MemberCaller SetValueCaller; + }; + + void example() + { + Integer foo = { 0 }; + + { + Callback bar = ConstMemberCaller(foo); + + // invoke the callback + bar(); // foo.printValue() + } + + + { + // use the typedef to improve readability + Callback bar = Integer::SetValueCaller(foo); + + // invoke the callback + bar(); // foo.setValue() + } + } + // end example +} + +namespace ExampleReferenceCaller +{ + // ReferenceCaller example + void Int_printValue(const int& value) + { + // print value here; + } + + void Int_setValue(int& value) + { + value = 3; + } + + // a typedef to make things more readable + typedef ReferenceCaller IntSetValueCaller; + + void example() + { + int foo = 0; + + { + Callback bar = ConstReferenceCaller(foo); + + // invoke the callback + bar(); // Int_printValue(foo) + } + + + { + // use the typedef to improve readability + Callback bar = IntSetValueCaller(foo); + + // invoke the callback + bar(); // Int_setValue(foo) + } + } + // end example +} + +#endif diff --git a/tools/urt/libs/generic/callback.h b/tools/urt/libs/generic/callback.h new file mode 100644 index 00000000..9913439f --- /dev/null +++ b/tools/urt/libs/generic/callback.h @@ -0,0 +1,517 @@ + +#if !defined(INCLUDED_GENERIC_CLOSURE_H) +#define INCLUDED_GENERIC_CLOSURE_H + +/// \file +/// \brief Type-safe techniques for binding the first argument of an anonymous callback. + +#include + +/// \brief Combines a void pointer with a pointer to a function which operates on a void pointer. +/// +/// Use with the callback constructors MemberCaller, ConstMemberCaller, ReferenceCaller, ConstReferenceCaller, PointerCaller, ConstPointerCaller and FreeCaller. +class Callback +{ + typedef void (*Thunk)(void*); + void* m_environment; + Thunk m_thunk; + + static void nullThunk(void*) + { + } + +public: + Callback() : m_environment(0), m_thunk(nullThunk) + { + } + Callback(void* environment, Thunk function) : m_environment(environment), m_thunk(function) + { + } + void* getEnvironment() const + { + return m_environment; + } + Thunk getThunk() const + { + return m_thunk; + } + void operator()() const + { + m_thunk(m_environment); + } +}; + +inline bool operator==(const Callback& self, const Callback& other) +{ + return self.getEnvironment() == other.getEnvironment() && self.getThunk() == other.getThunk(); +} +inline bool operator<(const Callback& self, const Callback& other) +{ + return self.getEnvironment() < other.getEnvironment() || + (!(other.getEnvironment() < self.getEnvironment()) && self.getThunk() < other.getThunk()); +} + +/// \brief Combines a void pointer with a pointer to a function which operates on a void pointer and one other argument. +/// +/// Use with the callback constructors MemberCaller1, ConstMemberCaller1, ReferenceCaller1, ConstReferenceCaller1, PointerCaller1, ConstPointerCaller1 and FreeCaller1. +template +class Callback1 +{ + typedef void (*Thunk)(void*, FirstArgument); + void* m_environment; + Thunk m_thunk; + + static void nullThunk(void*, FirstArgument) + { + } + +public: + typedef FirstArgument first_argument_type; + + Callback1() : m_environment(0), m_thunk(nullThunk) + { + } + Callback1(void* environment, Thunk function) : m_environment(environment), m_thunk(function) + { + } + void* getEnvironment() const + { + return m_environment; + } + Thunk getThunk() const + { + return m_thunk; + } + void operator()(FirstArgument firstArgument) const + { + m_thunk(m_environment, firstArgument); + } +}; + +template +inline bool operator==(const Callback1& self, const Callback1& other) +{ + return self.getEnvironment() == other.getEnvironment() && self.getThunk() == other.getThunk(); +} +template +inline bool operator<(const Callback1& self, const Callback1& other) +{ + return self.getEnvironment() < other.getEnvironment() || + (!(other.getEnvironment() < self.getEnvironment()) && self.getThunk() < other.getThunk()); +} + +template +class FunctorInvoke +{ +public: + inline void operator()(Functor functor) + { + functor(); + } +}; + +typedef FunctorInvoke CallbackInvoke; + + +template +class Functor1Invoke +{ + FirstArgument m_firstArgument; +public: + Functor1Invoke(FirstArgument firstArgument) : m_firstArgument(firstArgument) + { + } + inline void operator()(Functor functor) + { + functor(m_firstArgument); + } +}; + + +typedef Callback1 BoolImportCallback; +typedef Callback1 BoolExportCallback; + +typedef Callback1 IntImportCallback; +typedef Callback1 IntExportCallback; + +typedef Callback1 FloatImportCallback; +typedef Callback1 FloatExportCallback; + +typedef Callback1 StringImportCallback; +typedef Callback1 StringExportCallback; + +typedef Callback1 SizeImportCallback; +typedef Callback1 SizeExportCallback; + + +/// \brief Forms a Callback from a non-const Environment reference and a non-const Environment member-function. +/// +/// \dontinclude generic/callback.cpp +/// \skipline MemberCaller example +/// \until end example +template +class MemberCaller +{ + Environment& m_environment; +public: + MemberCaller(Environment& environment) : m_environment(environment) + { + } + void* getEnvironment() const + { + return &m_environment; + } + static void thunk(void* environment) + { + ((*reinterpret_cast(environment)).*member)(); + } + operator Callback() const + { + return Callback(getEnvironment(), thunk); + } +}; + +/// \brief Forms a Callback from a const Environment reference and a const Environment member-function. +/// +/// \dontinclude generic/callback.cpp +/// \skipline MemberCaller example +/// \until end example +template +class ConstMemberCaller +{ + const Environment& m_environment; +public: + ConstMemberCaller(const Environment& environment) : m_environment(environment) + { + } + void* getEnvironment() const + { + return const_cast(&m_environment); + } + static void thunk(void* environment) + { + ((*reinterpret_cast(environment)).*member)(); + } + operator Callback() const + { + return Callback(getEnvironment(), thunk); + } +}; + +/// \brief Forms a Callback from a non-const Environment reference and a const Environment member-function which takes one argument. +template +class MemberCaller1 +{ + Environment& m_environment; +public: + MemberCaller1(Environment& environment) : m_environment(environment) + { + } + void* getEnvironment() const + { + return &m_environment; + } + static void thunk(void* environment, FirstArgument firstArgument) + { + ((*reinterpret_cast(environment)).*member)(firstArgument); + } + operator Callback1() const + { + return Callback1(getEnvironment(), thunk); + } +}; + +/// \brief Forms a Callback from a const Environment reference and a const Environment member-function which takes one argument. +template +class ConstMemberCaller1 +{ + const Environment& m_environment; +public: + ConstMemberCaller1(const Environment& environment) : m_environment(environment) + { + } + void* getEnvironment() const + { + return const_cast(&m_environment); + } + static void thunk(void* environment, FirstArgument firstArgument) + { + ((*reinterpret_cast(environment)).*member)(firstArgument); + } + operator Callback1() const + { + return Callback1(getEnvironment(), thunk); + } +}; + +/// \brief Forms a Callback from a non-const Environment reference and a free function which operates on a non-const Environment reference. +/// +/// \dontinclude generic/callback.cpp +/// \skipline ReferenceCaller example +/// \until end example +template +class ReferenceCaller +{ + Environment& m_environment; +public: + ReferenceCaller(Environment& environment) : m_environment(environment) + { + } + void* getEnvironment() const + { + return &m_environment; + } + static void thunk(void* environment) + { + (func)(*reinterpret_cast(environment)); + } + operator Callback() const + { + return Callback(getEnvironment(), thunk); + } +}; + +/// \brief Forms a Callback from a const Environment reference and a free function which operates on a const Environment reference. +/// +/// \dontinclude generic/callback.cpp +/// \skipline ReferenceCaller example +/// \until end example +template +class ConstReferenceCaller +{ + const Environment& m_environment; +public: + ConstReferenceCaller(const Environment& environment) : m_environment(environment) + { + } + void* getEnvironment() const + { + return const_cast(&m_environment); + } + static void thunk(void* environment) + { + (func)(*reinterpret_cast(environment)); + } + operator Callback() const + { + return Callback(getEnvironment(), thunk); + } +}; + +/// \brief Forms a Callback from a non-const Environment reference and a free function which operates on a non-const Environment reference and one other argument. +template +class ReferenceCaller1 +{ + Environment& m_environment; +public: + ReferenceCaller1(Environment& environment) : m_environment(environment) + { + } + void* getEnvironment() const + { + return &m_environment; + } + static void thunk(void* environment, FirstArgument firstArgument) + { + (func)(*reinterpret_cast(environment), firstArgument); + } + operator Callback1() const + { + return Callback1(getEnvironment(), thunk); + } +}; + +/// \brief Forms a Callback from a const Environment reference and a free function which operates on a const Environment reference and one other argument. +template +class ConstReferenceCaller1 +{ + const Environment& m_environment; +public: + ConstReferenceCaller1(const Environment& environment) : m_environment(environment) + { + } + void* getEnvironment() const + { + return const_cast(&m_environment); + } + static void thunk(void* environment, FirstArgument firstArgument) + { + (func)(*reinterpret_cast(environment), firstArgument); + } + operator Callback1() const + { + return Callback1(getEnvironment(), thunk); + } +}; + +/// \brief Forms a Callback from a non-const Environment pointer and a free function which operates on a non-const Environment pointer. +template +class PointerCaller +{ + Environment* m_environment; +public: + PointerCaller(Environment* environment) : m_environment(environment) + { + } + void* getEnvironment() const + { + return m_environment; + } + static void thunk(void* environment) + { + (func)(reinterpret_cast(environment)); + } + operator Callback() const + { + return Callback(getEnvironment(), thunk); + } +}; + +/// \brief Forms a Callback from a const Environment pointer and a free function which operates on a const Environment pointer. +template +class ConstPointerCaller +{ + const Environment* m_environment; +public: + ConstPointerCaller(const Environment* environment) : m_environment(environment) + { + } + void* getEnvironment() const + { + return const_cast(m_environment); + } + static void thunk(void* environment) + { + (func)(reinterpret_cast(environment)); + } + operator Callback() const + { + return Callback(getEnvironment(), thunk); + } +}; + +/// \brief Forms a Callback from a non-const Environment pointer and a free function which operates on a non-const Environment pointer and one other argument. +template +class PointerCaller1 +{ + Environment* m_environment; +public: + PointerCaller1(Environment* environment) : m_environment(environment) + { + } + void* getEnvironment() const + { + return m_environment; + } + static void thunk(void* environment, FirstArgument firstArgument) + { + (func)(reinterpret_cast(environment), firstArgument); + } + operator Callback1() const + { + return Callback1(getEnvironment(), thunk); + } +}; + +/// \brief Forms a Callback from a const Environment pointer and a free function which operates on a const Environment pointer and one other argument. +template +class ConstPointerCaller1 +{ + const Environment* m_environment; +public: + ConstPointerCaller1(const Environment* environment) : m_environment(environment) + { + } + void* getEnvironment() const + { + return const_cast(m_environment); + } + static void thunk(void* environment, FirstArgument firstArgument) + { + (func)(reinterpret_cast(environment), firstArgument); + } + operator Callback1() const + { + return Callback1(getEnvironment(), thunk); + } +}; + + +/// \brief Forms a Callback from a free function which takes no arguments. +template +class FreeCaller +{ +public: + void* getEnvironment() const + { + return 0; + } + static void thunk(void*) + { + (func)(); + } + operator Callback() const + { + return Callback(getEnvironment(), thunk); + } +}; + +/// \brief Forms a Callback from a free function which takes a single argument. +template +class FreeCaller1 +{ +public: + void* getEnvironment() const + { + return 0; + } + static void thunk(void*, FirstArgument firstArgument) + { + (func)(firstArgument); + } + operator Callback1() const + { + return Callback1(getEnvironment(), thunk); + } +}; + + +/// \brief Constructs a Callback from a non-const \p functor with zero arguments. +/// +/// \param Functor Must define \c operator()(). +template +inline Callback makeCallback(Functor& functor) +{ + return Callback(MemberCaller(functor)); +} + +/// \brief Constructs a Callback from a const \p functor with zero arguments. +/// +/// \param Functor Must define const \c operator()(). +template +inline Callback makeCallback(const Functor& functor) +{ + return Callback(ConstMemberCaller(functor)); +} + +/// \brief Constructs a Callback1 from a non-const \p functor with one argument. +/// +/// \param Functor Must define \c first_argument_type and \c operator()(first_argument_type). +template +inline Callback1 makeCallback1(Functor& functor) +{ + typedef typename Functor::first_argument_type FirstArgument; + return Callback1(MemberCaller1(functor)); +} + +/// \brief Constructs a Callback1 from a const \p functor with one argument. +/// +/// \param Functor Must define \c first_argument_type and const \c operator()(first_argument_type). +template +inline Callback1 makeCallback1(const Functor& functor) +{ + typedef typename Functor::first_argument_type FirstArgument; + return Callback1(ConstMemberCaller1(functor)); +} + +#endif diff --git a/tools/urt/libs/generic/enumeration.cpp b/tools/urt/libs/generic/enumeration.cpp new file mode 100644 index 00000000..babf692e --- /dev/null +++ b/tools/urt/libs/generic/enumeration.cpp @@ -0,0 +1,3 @@ + +#include "enumeration.h" + diff --git a/tools/urt/libs/generic/enumeration.h b/tools/urt/libs/generic/enumeration.h new file mode 100644 index 00000000..b78b40ab --- /dev/null +++ b/tools/urt/libs/generic/enumeration.h @@ -0,0 +1,40 @@ + +#if !defined(INCLUDED_GENERIC_ENUMERATION_H) +#define INCLUDED_GENERIC_ENUMERATION_H + +/// \file +/// \brief Type safe enumeration. + +/// \brief An enumerated value. +/// +/// - Can be forward-declared when the definition of Enumeration is unknown. +/// - Can only be constructed from valid enumerated values. +/// - Can only be compared with others of the same type. +/// +/// \param Enumeration A type that contains an enum \c Value of the allowed values of the enumeration. +template +class EnumeratedValue : public Enumeration +{ + typename Enumeration::Value m_value; +public: + explicit EnumeratedValue(typename Enumeration::Value value) : m_value(value) + { + } + typename Enumeration::Value get() const + { + return m_value; + } +}; + +template +inline bool operator==(EnumeratedValue self, EnumeratedValue other) +{ + return self.get() == other.get(); +} +template +inline bool operator!=(EnumeratedValue self, EnumeratedValue other) +{ + return !operator==(self, other); +} + +#endif diff --git a/tools/urt/libs/generic/object.cpp b/tools/urt/libs/generic/object.cpp new file mode 100644 index 00000000..57fd76ed --- /dev/null +++ b/tools/urt/libs/generic/object.cpp @@ -0,0 +1,21 @@ + +#include "object.h" + +namespace +{ + class Blah + { + int i; + public: + Blah() + { + i = 3; + } + }; + + void Test() + { + char storage[sizeof(Blah)]; + constructor(*reinterpret_cast(storage)); + } +} \ No newline at end of file diff --git a/tools/urt/libs/generic/object.h b/tools/urt/libs/generic/object.h new file mode 100644 index 00000000..12900911 --- /dev/null +++ b/tools/urt/libs/generic/object.h @@ -0,0 +1,78 @@ + +#if !defined(INCLUDED_GENERIC_OBJECT_H) +#define INCLUDED_GENERIC_OBJECT_H + +/// \file +/// \brief Convenience functions (syntactic sugar) to wrap explicit constructor (aka in-place 'new') and destructor calls. +/// +/// Use makeReference() to wrap non-const-reference constructor parameters. + +#if _MSC_VER > 1000 && defined(WIN32) +#pragma warning(disable:4345) // behavior change: an object of POD type constructed with an initializer of the form () will be default-initialized +#endif + +#include + +template +inline void constructor(Type& object) +{ + new(&object) Type(); +} + +template +inline void constructor(Type& object, const T1& t1) +{ + new(&object) Type(t1); +} + +template +inline void constructor(Type& object, const T1& t1, const T2& t2) +{ + new(&object) Type(t1, t2); +} + +template +inline void constructor(Type& object, const T1& t1, const T2& t2, const T3& t3) +{ + new(&object) Type(t1, t2, t3); +} + +template +inline void constructor(Type& object, const T1& t1, const T2& t2, const T3& t3, const T4& t4) +{ + new(&object) Type(t1, t2, t3, t4); +} + +template +inline void constructor(Type& object, const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5) +{ + new(&object) Type(t1, t2, t3, t4, t5); +} + +template +inline void constructor(Type& object, const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5, const T6& t6) +{ + new(&object) Type(t1, t2, t3, t4, t5, t6); +} + +template +inline void constructor(Type& object, const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5, const T6& t6, const T7& t7) +{ + new(&object) Type(t1, t2, t3, t4, t5, t6, t7); +} + +template +inline void constructor(Type& object, const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5, const T6& t6, const T7& t7, const T8& t8) +{ + new(&object) Type(t1, t2, t3, t4, t5, t6, t7, t8); +} + +template +inline void destructor(Type& object) +{ + object.~Type(); +} + + + +#endif diff --git a/tools/urt/libs/generic/reference.cpp b/tools/urt/libs/generic/reference.cpp new file mode 100644 index 00000000..1df0e189 --- /dev/null +++ b/tools/urt/libs/generic/reference.cpp @@ -0,0 +1,2 @@ + +#include "reference.h" diff --git a/tools/urt/libs/generic/reference.h b/tools/urt/libs/generic/reference.h new file mode 100644 index 00000000..6a79c05f --- /dev/null +++ b/tools/urt/libs/generic/reference.h @@ -0,0 +1,111 @@ + +#if !defined(INCLUDED_GENERIC_REFERENCE_H) +#define INCLUDED_GENERIC_REFERENCE_H + +/// \file +/// \brief Wrappers to allow storing objects in templated containers using 'reference' semantics. + +/// \brief A reference to a mutable object. +/// Has 'reference' semantics, except for \c 'operator==' and \c 'operator.'. +/// \param Type The type of the referenced object. +template +class Reference +{ + Type* m_contained; +public: + explicit Reference(Type& contained) : m_contained(&contained) + { + } + Type& operator*() const + { + return *m_contained; + } + Type* operator->() const + { + return m_contained; + } + operator Type&() const + { + return *m_contained; + } + Type& get() const + { + return *m_contained; + } + Type* get_pointer() const + { + return m_contained; + } +}; + +template +bool operator<(const Reference& self, const Reference& other) +{ + return self.get() < other.get(); +} +template +bool operator==(const Reference& self, const Reference& other) +{ + return self.get() == other.get(); +} + +/// \brief construct a reference to a mutable object. +template +inline Reference makeReference(Type& value) +{ + return Reference(value); +} + +/// \brief A reference to a non-mutable object. +/// Has 'reference' semantics, except for \c 'operator==' and \c 'operator.'. +/// \param Type The type of the referenced object. +template +class ConstReference +{ + const Type* m_contained; +public: + explicit ConstReference(const Type& contained) : m_contained(&contained) + { + } + const Type& operator*() const + { + return *m_contained; + } + const Type* operator->() const + { + return m_contained; + } + operator const Type&() const + { + return *m_contained; + } + const Type& get() const + { + return *m_contained; + } + const Type* get_pointer() const + { + return m_contained; + } +}; + +template +bool operator<(const ConstReference& self, const ConstReference& other) +{ + return self.get() < other.get(); +} +template +bool operator==(const ConstReference& self, const ConstReference& other) +{ + return self.get() == other.get(); +} + +/// \brief construct a reference to a non-mutable object. +template +inline ConstReference makeReference(const Type& value) +{ + return ConstReference(value); +} + + +#endif diff --git a/tools/urt/libs/generic/referencecounted.cpp b/tools/urt/libs/generic/referencecounted.cpp new file mode 100644 index 00000000..7d8a3ef1 --- /dev/null +++ b/tools/urt/libs/generic/referencecounted.cpp @@ -0,0 +1,2 @@ + +#include "referencecounted.h" diff --git a/tools/urt/libs/generic/referencecounted.h b/tools/urt/libs/generic/referencecounted.h new file mode 100644 index 00000000..48ba446b --- /dev/null +++ b/tools/urt/libs/generic/referencecounted.h @@ -0,0 +1,187 @@ + +#if !defined(INCLUDED_GENERIC_REFERENCECOUNTED_H) +#define INCLUDED_GENERIC_REFERENCECOUNTED_H + +/// \file +/// \brief 'smart' pointers and references. + +#include + +template +class IncRefDecRefCounter +{ +public: + void increment(Type& value) + { + value.IncRef(); + } + void decrement(Type& value) + { + value.DecRef(); + } +}; + +/// \brief A smart-pointer that uses a counter stored in the object pointed-to. +template > +class SmartPointer : public Counter +{ + Type* m_value; +public: + + SmartPointer(const SmartPointer& other) + : m_value(other.m_value) + { + Counter::increment(*m_value); + } + explicit SmartPointer(Type* value) + : m_value(value) + { + Counter::increment(*m_value); + } + ~SmartPointer() + { + Counter::decrement(*m_value); + } + SmartPointer& operator=(const SmartPointer& other) + { + SmartPointer temp(other); + temp.swap(*this); + return *this; + } + SmartPointer& operator=(Type* value) + { + SmartPointer temp(value); + temp.swap(*this); + return *this; + } + void swap(SmartPointer& other) + { + std::swap(m_value, other.m_value); + } + + operator Type*() const + { + return m_value; + } + Type& operator*() const + { + return *m_value; + } + Type* operator->() const + { + return m_value; + } + Type* get() const + { + return m_value; + } +}; + +template +inline bool operator<(const SmartPointer& self, const SmartPointer& other) +{ + return self.get() < other.get(); +} +template +inline bool operator==(const SmartPointer& self, const SmartPointer& other) +{ + return self.get() == other.get(); +} +template +inline bool operator!=(const SmartPointer& self, const SmartPointer& other) +{ + return !::operator==(self, other); +} + +namespace std +{ + /// \brief Swaps the values of \p self and \p other. + /// Overloads std::swap(). + template + inline void swap(SmartPointer& self, SmartPointer& other) + { + self.swap(other); + } +} + + +/// \brief A smart-reference that uses a counter stored in the object pointed-to. +template > +class SmartReference : public Counter +{ + Type* m_value; +public: + + SmartReference(const SmartReference& other) + : m_value(other.m_value) + { + Counter::increment(*m_value); + } + explicit SmartReference(Type& value) + : m_value(&value) + { + Counter::increment(*m_value); + } + ~SmartReference() + { + Counter::decrement(*m_value); + } + SmartReference& operator=(const SmartReference& other) + { + SmartReference temp(other); + temp.swap(*this); + return *this; + } + SmartReference& operator=(Type& value) + { + SmartReference temp(value); + temp.swap(*this); + return *this; + } + void swap(SmartReference& other) + { + std::swap(m_value, other.m_value); + } + + operator Type&() const + { + return *m_value; + } + Type& get() const + { + return *m_value; + } + Type* get_pointer() const + { + return m_value; + } +}; + +template +inline bool operator<(const SmartReference& self, const SmartReference& other) +{ + return self.get() < other.get(); +} +template +inline bool operator==(const SmartReference& self, const SmartReference& other) +{ + return self.get() == other.get(); +} +template +inline bool operator!=(const SmartReference& self, const SmartReference& other) +{ + return !::operator==(self, other); +} + +namespace std +{ + /// \brief Swaps the values of \p self and \p other. + /// Overloads std::swap(). + template + inline void swap(SmartReference& self, SmartReference& other) + { + self.swap(other); + } +} + +#endif diff --git a/tools/urt/libs/generic/static.cpp b/tools/urt/libs/generic/static.cpp new file mode 100644 index 00000000..2a6ff8d5 --- /dev/null +++ b/tools/urt/libs/generic/static.cpp @@ -0,0 +1,113 @@ + +#include "static.h" + +#if defined(_DEBUG) || defined(DOXYGEN) + +namespace ExampleStatic +{ + // Static example + // ---- myclass.h + class MyClass + { + public: + int value; + MyClass() : value(3) + { + } + }; + + typedef Static StaticMyClass; + + // ---- main.cpp + class DynamicInitialisation + { + public: + DynamicInitialisation() + { + // StaticMyClass::instance() may be invalid here because construction order is undefined + } + }; + + DynamicInitialisation g_dynamicInitialisation; + + void duringMain() + { + int bar = StaticMyClass::instance().value; + } + // end example +} + +namespace ExampleLazyStatic +{ + // LazyStatic example + // ---- myclass.h + class MyClass + { + public: + int value; + MyClass() : value(3) + { + } + // destructor will never be called + }; + + typedef LazyStatic StaticMyClass; + + // ---- main.cpp + class DynamicInitialisation + { + public: + DynamicInitialisation() + { + int bar = StaticMyClass::instance().value; + } + }; + + DynamicInitialisation g_dynamicInitialisation; + + void duringMain() + { + int bar = StaticMyClass::instance().value; + } + // end example +} + +namespace ExampleSmartStatic +{ + // SmartStatic example + // ---- myclass.h + class MyClass + { + public: + int value; + MyClass() : value(3) + { + } + }; + + typedef CountedStatic StaticMyClass; + + // ---- main.cpp + class DynamicInitialisation + { + public: + DynamicInitialisation() + { + // StaticMyClass::instance() is invalid before the ref is constructed + SmartStatic ref; + int bar = ref.instance().value; + + SmartStatic ref2; // any number of instances are allowed. + } + }; + + DynamicInitialisation g_dynamicInitialisation; + + void duringMain() + { + int bar = SmartStatic().instance().value; // an instance can be a temporary + } + // end example +} + +#endif diff --git a/tools/urt/libs/generic/static.h b/tools/urt/libs/generic/static.h new file mode 100644 index 00000000..d51fbb45 --- /dev/null +++ b/tools/urt/libs/generic/static.h @@ -0,0 +1,123 @@ + +#if !defined(INCLUDED_GENERIC_STATIC_H) +#define INCLUDED_GENERIC_STATIC_H + +/// \file +/// \brief Template techniques for instantiating singletons. + +#include + +/// \brief A singleton which is statically initialised. +/// +/// \param Type The singleton object type. +/// +/// \dontinclude generic/static.cpp +/// \skipline Static example +/// \until end example +template +class Static +{ + static Type m_instance; +public: + static Type& instance() + { + return m_instance; + } +}; + +template +Type Static::m_instance; + + +/// \brief A singleton which is lazily initialised. +/// The instance is constructed the first time it is referenced, and is never destroyed. +/// +/// \param Type The singleton object type. +/// +/// \dontinclude generic/static.cpp +/// \skipline LazyStatic example +/// \until end example +template +class LazyStatic +{ + static Type* m_instance; // this will be initialised to 0 by the CRT, according to the c++ standard +public: + static Type& instance() + { + if(m_instance == 0) + { + m_instance = new Type; // allocate using 'new' to get the correct alignment + } + return *m_instance; + } +}; + +template +Type* LazyStatic::m_instance; + + +/// \brief A singleton which keeps a count of the number of times it is referenced. +/// +/// The instance is constructed when its reference count changes from 0 to 1 and destroyed when its reference count changes from 1 to 0. +/// Use with SmartStatic. +/// +/// \param Type The singleton object type. +template +class CountedStatic +{ + static std::size_t m_refcount; // this will be initialised to 0 by the CRT, according to the c++ standard + static Type* m_instance; +public: + static Type& instance() + { + return *m_instance; + } + static void capture() + { + if(++m_refcount == 1) + { + m_instance = new Type; // allocate using 'new' to get the correct alignment + } + } + static void release() + { + if(--m_refcount == 0) + { + delete m_instance; + } + } +}; + +template +std::size_t CountedStatic::m_refcount; // this will be initialised to 0 by the CRT, according to the c++ standard +template +Type* CountedStatic::m_instance; + +/// \brief A reference to a CountedStatic. +/// Guarantees that CountedStatic will be constructed for the lifetime of this object. +/// +/// \param Type The type parameter of the CountedStatic to reference. +/// +/// \dontinclude generic/static.cpp +/// \skipline SmartStatic example +/// \until end example +template +class SmartStatic +{ +public: + SmartStatic() + { + CountedStatic::capture(); + } + ~SmartStatic() + { + CountedStatic::release(); + } + Type& instance() + { + return CountedStatic::instance(); + } +}; + + +#endif diff --git a/tools/urt/libs/gtkutil/accelerator.cpp b/tools/urt/libs/gtkutil/accelerator.cpp new file mode 100644 index 00000000..539dee2a --- /dev/null +++ b/tools/urt/libs/gtkutil/accelerator.cpp @@ -0,0 +1,527 @@ + +#include "accelerator.h" + +#include "debugging/debugging.h" + +#include +#include +#include +#include + +#include "generic/callback.h" +#include "generic/bitfield.h" + +#include "pointer.h" +#include "closure.h" + + + +typedef std::map AcceleratorMap; + +void accelerator_map_insert(AcceleratorMap& acceleratorMap, Accelerator accelerator, const Callback& callback) +{ + if(accelerator.key != 0) + { + ASSERT_MESSAGE(acceleratorMap.find(accelerator) == acceleratorMap.end(), "failed to add accelerator"); + acceleratorMap.insert(AcceleratorMap::value_type(accelerator, callback)); + } +} + +void accelerator_map_erase(AcceleratorMap& acceleratorMap, Accelerator accelerator) +{ + if(accelerator.key != 0) + { + ASSERT_MESSAGE(acceleratorMap.find(accelerator) != acceleratorMap.end(), "failed to remove accelerator"); + acceleratorMap.erase(accelerator); + } +} + +Accelerator accelerator_for_event_key(guint keyval, guint state) +{ + keyval = gdk_keyval_to_upper(keyval); + if(keyval == GDK_ISO_Left_Tab) + keyval = GDK_Tab; + return Accelerator(keyval, (GdkModifierType)(state & gtk_accelerator_get_default_mod_mask())); +} + +bool AcceleratorMap_activate(const AcceleratorMap& acceleratorMap, const Accelerator& accelerator) +{ + AcceleratorMap::const_iterator i = acceleratorMap.find(accelerator); + if(i != acceleratorMap.end()) + { + (*i).second(); + return true; + } + + return false; +} + +static gboolean accelerator_key_event(GtkWindow* window, GdkEventKey* event, AcceleratorMap* acceleratorMap) +{ + return AcceleratorMap_activate(*acceleratorMap, accelerator_for_event_key(event->keyval, event->state)); +} + + +AcceleratorMap g_special_accelerators; + + +namespace MouseButton +{ + enum + { + Left = 1 << 0, + Right = 1 << 1, + Middle = 1 << 2, + }; +} + +typedef unsigned int ButtonMask; + +void print_buttons(ButtonMask mask) +{ + globalOutputStream() << "button state: "; + if((mask & MouseButton::Left) != 0) + { + globalOutputStream() << "Left "; + } + if((mask & MouseButton::Right) != 0) + { + globalOutputStream() << "Right "; + } + if((mask & MouseButton::Middle) != 0) + { + globalOutputStream() << "Middle "; + } + globalOutputStream() << "\n"; +} + +ButtonMask ButtonMask_for_event_button(guint button) +{ + switch(button) + { + case 1: + return MouseButton::Left; + case 2: + return MouseButton::Middle; + case 3: + return MouseButton::Right; + } + return 0; +} + +bool window_has_accel(GtkWindow* toplevel) +{ + return g_slist_length(gtk_accel_groups_from_object(G_OBJECT(toplevel))) != 0; +} + +namespace +{ + bool g_accel_enabled = true; +} + +bool global_accel_enabled() +{ + return g_accel_enabled; +} + + +AcceleratorMap g_queuedAccelerators; + +GClosure* accel_group_add_accelerator(GtkAccelGroup* group, Accelerator accelerator, const Callback& callback); + +void GlobalQueuedAccelerators_commit() +{ + for(AcceleratorMap::const_iterator i = g_queuedAccelerators.begin(); i != g_queuedAccelerators.end(); ++i) + { + accel_group_add_accelerator(global_accel, (*i).first, (*i).second); + } + g_queuedAccelerators.clear(); +} + +void GlobalQueuedAccelerators_add(Accelerator accelerator, const Callback& callback) +{ + g_queuedAccelerators.insert(AcceleratorMap::value_type(accelerator, callback)); +} + +void accel_group_test(GtkWindow* toplevel, GtkAccelGroup* accel) +{ + guint n_entries; + gtk_accel_group_query(accel, '4', (GdkModifierType)0, &n_entries); + globalOutputStream() << "grid4: " << n_entries << "\n"; + globalOutputStream() << "toplevel accelgroups: " << g_slist_length(gtk_accel_groups_from_object(G_OBJECT(toplevel))) << "\n"; +} + +typedef std::set WindowSet; +WindowSet g_accel_windows; + +bool Buttons_press(ButtonMask& buttons, guint button, guint state) +{ + if(buttons == 0 && bitfield_enable(buttons, ButtonMask_for_event_button(button)) != 0) + { + ASSERT_MESSAGE(g_accel_enabled, "Buttons_press: accelerators not enabled"); + g_accel_enabled = false; + for(WindowSet::iterator i = g_accel_windows.begin(); i != g_accel_windows.end(); ++i) + { + GtkWindow* toplevel = *i; + ASSERT_MESSAGE(window_has_accel(toplevel), "ERROR"); + ASSERT_MESSAGE(GTK_WIDGET_TOPLEVEL(toplevel), "disabling accel for non-toplevel window"); + gtk_window_remove_accel_group(toplevel, global_accel); +#if 0 + globalOutputStream() << reinterpret_cast(toplevel) << ": disabled global accelerators\n"; +#endif +#if 0 + accel_group_test(toplevel, global_accel); +#endif + } + } + buttons = bitfield_enable(buttons, ButtonMask_for_event_button(button)); +#if 0 + globalOutputStream() << "Buttons_press: "; + print_buttons(buttons); +#endif + return false; +} + +bool Buttons_release(ButtonMask& buttons, guint button, guint state) +{ + if(buttons != 0 && bitfield_disable(buttons, ButtonMask_for_event_button(button)) == 0) + { + ASSERT_MESSAGE(!g_accel_enabled, "Buttons_release: accelerators are enabled"); + g_accel_enabled = true; + for(WindowSet::iterator i = g_accel_windows.begin(); i != g_accel_windows.end(); ++i) + { + GtkWindow* toplevel = *i; + ASSERT_MESSAGE(!window_has_accel(toplevel), "ERROR"); + ASSERT_MESSAGE(GTK_WIDGET_TOPLEVEL(toplevel), "enabling accel for non-toplevel window"); + gtk_window_add_accel_group(toplevel, global_accel); +#if 0 + globalOutputStream() << reinterpret_cast(toplevel) << ": enabled global accelerators\n"; +#endif +#if 0 + accel_group_test(toplevel, global_accel); +#endif + } + GlobalQueuedAccelerators_commit(); + } + buttons = bitfield_disable(buttons, ButtonMask_for_event_button(button)); +#if 0 + globalOutputStream() << "Buttons_release: "; + print_buttons(buttons); +#endif + return false; +} + +bool Buttons_releaseAll(ButtonMask& buttons) +{ + Buttons_release(buttons, MouseButton::Left | MouseButton::Middle | MouseButton::Right, 0); + return false; +} + +struct PressedButtons +{ + ButtonMask buttons; + + PressedButtons() : buttons(0) + { + } +}; + +gboolean PressedButtons_button_press(GtkWidget* widget, GdkEventButton* event, PressedButtons* pressed) +{ + if(event->type == GDK_BUTTON_PRESS) + { + return Buttons_press(pressed->buttons, event->button, event->state); + } + return FALSE; +} + +gboolean PressedButtons_button_release(GtkWidget* widget, GdkEventButton* event, PressedButtons* pressed) +{ + if(event->type == GDK_BUTTON_RELEASE) + { + return Buttons_release(pressed->buttons, event->button, event->state); + } + return FALSE; +} + +gboolean PressedButtons_focus_out(GtkWidget* widget, GdkEventFocus* event, PressedButtons* pressed) +{ + Buttons_releaseAll(pressed->buttons); + return FALSE; +} + +void PressedButtons_connect(PressedButtons& pressedButtons, GtkWidget* widget) +{ + g_signal_connect(G_OBJECT(widget), "button_press_event", G_CALLBACK(PressedButtons_button_press), &pressedButtons); + g_signal_connect(G_OBJECT(widget), "button_release_event", G_CALLBACK(PressedButtons_button_release), &pressedButtons); + g_signal_connect(G_OBJECT(widget), "focus_out_event", G_CALLBACK(PressedButtons_focus_out), &pressedButtons); +} + +PressedButtons g_pressedButtons; + + +#include + +struct PressedKeys +{ + typedef std::set Keys; + Keys keys; + std::size_t refcount; + + PressedKeys() : refcount(0) + { + } +}; + +AcceleratorMap g_keydown_accelerators; +AcceleratorMap g_keyup_accelerators; + +bool Keys_press(PressedKeys::Keys& keys, guint keyval) +{ + if(keys.insert(keyval).second) + { + return AcceleratorMap_activate(g_keydown_accelerators, accelerator_for_event_key(keyval, 0)); + } + return g_keydown_accelerators.find(accelerator_for_event_key(keyval, 0)) != g_keydown_accelerators.end(); +} + +bool Keys_release(PressedKeys::Keys& keys, guint keyval) +{ + if(keys.erase(keyval) != 0) + { + return AcceleratorMap_activate(g_keyup_accelerators, accelerator_for_event_key(keyval, 0)); + } + return g_keyup_accelerators.find(accelerator_for_event_key(keyval, 0)) != g_keyup_accelerators.end(); +} + +void Keys_releaseAll(PressedKeys::Keys& keys, guint state) +{ + for(PressedKeys::Keys::iterator i = keys.begin(); i != keys.end(); ++i) + { + AcceleratorMap_activate(g_keyup_accelerators, accelerator_for_event_key(*i, state)); + } + keys.clear(); +} + +gboolean PressedKeys_key_press(GtkWidget* widget, GdkEventKey* event, PressedKeys* pressedKeys) +{ + //globalOutputStream() << "pressed: " << event->keyval << "\n"; + return event->state == 0 && Keys_press(pressedKeys->keys, event->keyval); +} + +gboolean PressedKeys_key_release(GtkWidget* widget, GdkEventKey* event, PressedKeys* pressedKeys) +{ + //globalOutputStream() << "released: " << event->keyval << "\n"; + return Keys_release(pressedKeys->keys, event->keyval); +} + +gboolean PressedKeys_focus_in(GtkWidget* widget, GdkEventFocus* event, PressedKeys* pressedKeys) +{ + ++pressedKeys->refcount; + return FALSE; +} + +gboolean PressedKeys_focus_out(GtkWidget* widget, GdkEventFocus* event, PressedKeys* pressedKeys) +{ + if(--pressedKeys->refcount == 0) + { + Keys_releaseAll(pressedKeys->keys, 0); + } + return FALSE; +} + +PressedKeys g_pressedKeys; + +void GlobalPressedKeys_releaseAll() +{ + Keys_releaseAll(g_pressedKeys.keys, 0); +} + +void GlobalPressedKeys_connect(GtkWindow* window) +{ + unsigned int key_press_handler = g_signal_connect(G_OBJECT(window), "key_press_event", G_CALLBACK(PressedKeys_key_press), &g_pressedKeys); + unsigned int key_release_handler = g_signal_connect(G_OBJECT(window), "key_release_event", G_CALLBACK(PressedKeys_key_release), &g_pressedKeys); + g_object_set_data(G_OBJECT(window), "key_press_handler", gint_to_pointer(key_press_handler)); + g_object_set_data(G_OBJECT(window), "key_release_handler", gint_to_pointer(key_release_handler)); + unsigned int focus_in_handler = g_signal_connect(G_OBJECT(window), "focus_in_event", G_CALLBACK(PressedKeys_focus_in), &g_pressedKeys); + unsigned int focus_out_handler = g_signal_connect(G_OBJECT(window), "focus_out_event", G_CALLBACK(PressedKeys_focus_out), &g_pressedKeys); + g_object_set_data(G_OBJECT(window), "focus_in_handler", gint_to_pointer(focus_in_handler)); + g_object_set_data(G_OBJECT(window), "focus_out_handler", gint_to_pointer(focus_out_handler)); +} + +void GlobalPressedKeys_disconnect(GtkWindow* window) +{ + g_signal_handler_disconnect(G_OBJECT(window), gpointer_to_int(g_object_get_data(G_OBJECT(window), "key_press_handler"))); + g_signal_handler_disconnect(G_OBJECT(window), gpointer_to_int(g_object_get_data(G_OBJECT(window), "key_release_handler"))); + g_signal_handler_disconnect(G_OBJECT(window), gpointer_to_int(g_object_get_data(G_OBJECT(window), "focus_in_handler"))); + g_signal_handler_disconnect(G_OBJECT(window), gpointer_to_int(g_object_get_data(G_OBJECT(window), "focus_out_handler"))); +} + + + +void special_accelerators_add(Accelerator accelerator, const Callback& callback) +{ + accelerator_map_insert(g_special_accelerators, accelerator, callback); +} +void special_accelerators_remove(Accelerator accelerator) +{ + accelerator_map_erase(g_special_accelerators, accelerator); +} + +void keydown_accelerators_add(Accelerator accelerator, const Callback& callback) +{ + accelerator_map_insert(g_keydown_accelerators, accelerator, callback); +} +void keydown_accelerators_remove(Accelerator accelerator) +{ + accelerator_map_erase(g_keydown_accelerators, accelerator); +} + +void keyup_accelerators_add(Accelerator accelerator, const Callback& callback) +{ + accelerator_map_insert(g_keyup_accelerators, accelerator, callback); +} +void keyup_accelerators_remove(Accelerator accelerator) +{ + accelerator_map_erase(g_keyup_accelerators, accelerator); +} + + +gboolean accel_closure_callback(GtkAccelGroup* group, GtkWidget* widget, guint key, GdkModifierType modifiers, gpointer data) +{ + (*reinterpret_cast(data))(); + return TRUE; +} + +GClosure* accel_group_add_accelerator(GtkAccelGroup* group, Accelerator accelerator, const Callback& callback) +{ + if(accelerator.key != 0 && gtk_accelerator_valid(accelerator.key, accelerator.modifiers)) + { + //globalOutputStream() << "adding accelerator: " << accelerator.key << " " << accelerator.modifiers << "\n"; + GClosure* closure = create_cclosure(G_CALLBACK(accel_closure_callback), callback); + gtk_accel_group_connect(group, accelerator.key, accelerator.modifiers, GTK_ACCEL_VISIBLE, closure); + return closure; + } + else + { + special_accelerators_add(accelerator, callback); + return 0; + } +} + +void accel_group_remove_accelerator(GtkAccelGroup* group, Accelerator accelerator) +{ + if(accelerator.key != 0 && gtk_accelerator_valid(accelerator.key, accelerator.modifiers)) + { + gtk_accel_group_disconnect_key(group, accelerator.key, accelerator.modifiers); + } + else + { + special_accelerators_remove(accelerator); + } +} + +GtkAccelGroup* global_accel = 0; + +void global_accel_init() +{ + global_accel = gtk_accel_group_new(); +} + +void global_accel_destroy() +{ + g_object_unref(global_accel); +} + +GClosure* global_accel_group_add_accelerator(Accelerator accelerator, const Callback& callback) +{ + if(!global_accel_enabled()) + { + // workaround: cannot add to GtkAccelGroup while it is disabled + GlobalQueuedAccelerators_add(accelerator, callback); + return 0; + } + return accel_group_add_accelerator(global_accel, accelerator, callback); +} +void global_accel_group_remove_accelerator(Accelerator accelerator) +{ + //ASSERT_MESSAGE(global_accel_enabled(), "removing accelerator while global accel is disabled"); + accel_group_remove_accelerator(global_accel, accelerator); +} + +/// \brief Propagates key events to the focus-widget, overriding global accelerators. +static gboolean override_global_accelerators(GtkWindow* window, GdkEventKey* event, gpointer data) +{ + return gtk_window_propagate_key_event(window, event); +} + +void global_accel_connect_window(GtkWindow* window) +{ +#if 1 + unsigned int override_handler = g_signal_connect(G_OBJECT(window), "key_press_event", G_CALLBACK(override_global_accelerators), 0); + g_object_set_data(G_OBJECT(window), "override_handler", gint_to_pointer(override_handler)); + + unsigned int special_key_press_handler = g_signal_connect(G_OBJECT(window), "key_press_event", G_CALLBACK(accelerator_key_event), &g_special_accelerators); + g_object_set_data(G_OBJECT(window), "special_key_press_handler", gint_to_pointer(special_key_press_handler)); + + GlobalPressedKeys_connect(window); +#else + unsigned int key_press_handler = g_signal_connect(G_OBJECT(window), "key_press_event", G_CALLBACK(accelerator_key_event), &g_keydown_accelerators); + unsigned int key_release_handler = g_signal_connect(G_OBJECT(window), "key_release_event", G_CALLBACK(accelerator_key_event), &g_keyup_accelerators); + g_object_set_data(G_OBJECT(window), "key_press_handler", gint_to_pointer(key_press_handler)); + g_object_set_data(G_OBJECT(window), "key_release_handler", gint_to_pointer(key_release_handler)); +#endif + g_accel_windows.insert(window); + gtk_window_add_accel_group(window, global_accel); +} +void global_accel_disconnect_window(GtkWindow* window) +{ +#if 1 + GlobalPressedKeys_disconnect(window); + + g_signal_handler_disconnect(G_OBJECT(window), gpointer_to_int(g_object_get_data(G_OBJECT(window), "override_handler"))); + g_signal_handler_disconnect(G_OBJECT(window), gpointer_to_int(g_object_get_data(G_OBJECT(window), "special_key_press_handler"))); +#else + g_signal_handler_disconnect(G_OBJECT(window), gpointer_to_int(g_object_get_data(G_OBJECT(window), "key_press_handler"))); + g_signal_handler_disconnect(G_OBJECT(window), gpointer_to_int(g_object_get_data(G_OBJECT(window), "key_release_handler"))); +#endif + gtk_window_remove_accel_group(window, global_accel); + std::size_t count = g_accel_windows.erase(window); + ASSERT_MESSAGE(count == 1, "failed to remove accel group\n"); +} + + +GClosure* global_accel_group_find(Accelerator accelerator) +{ + guint numEntries = 0; + GtkAccelGroupEntry* entry = gtk_accel_group_query(global_accel, accelerator.key, accelerator.modifiers, &numEntries); + if(numEntries != 0) + { + if(numEntries != 1) + { + char* name = gtk_accelerator_name(accelerator.key, accelerator.modifiers); + globalErrorStream() << "accelerator already in-use: " << name << "\n"; + g_free(name); + } + return entry->closure; + } + return 0; +} + +void command_connect_accelerator(const Accelerator& accelerator, const Callback& callback) +{ + if(accelerator.key != 0) + { + global_accel_group_add_accelerator(accelerator, callback); + } +} + +void command_disconnect_accelerator(const Accelerator& accelerator, const Callback& callback) +{ + if(accelerator.key != 0) + { + global_accel_group_remove_accelerator(accelerator); + } +} + + diff --git a/tools/urt/libs/gtkutil/accelerator.h b/tools/urt/libs/gtkutil/accelerator.h new file mode 100644 index 00000000..345904f0 --- /dev/null +++ b/tools/urt/libs/gtkutil/accelerator.h @@ -0,0 +1,97 @@ + +#if !defined(INCLUDED_GTKUTIL_ACCELERATOR_H) +#define INCLUDED_GTKUTIL_ACCELERATOR_H + +#include +#include + +#include "generic/callback.h" + + +struct Accelerator +{ + Accelerator(guint _key) + : key(_key), modifiers((GdkModifierType)0) + { + } + Accelerator(guint _key, GdkModifierType _modifiers) + : key(_key), modifiers(_modifiers) + { + } + bool operator<(const Accelerator& other) const + { + return key < other.key || (!(other.key < key) && modifiers < other.modifiers); + } + guint key; + GdkModifierType modifiers; +}; + +inline Accelerator accelerator_null() +{ + return Accelerator(0, (GdkModifierType)0); +} + + +void keydown_accelerators_add(Accelerator accelerator, const Callback& callback); +void keydown_accelerators_remove(Accelerator accelerator); +void keyup_accelerators_add(Accelerator accelerator, const Callback& callback); +void keyup_accelerators_remove(Accelerator accelerator); + +typedef struct _GtkWidget GtkWidget; +typedef struct _GtkWindow GtkWindow; +void global_accel_connect_window(GtkWindow* window); +void global_accel_disconnect_window(GtkWindow* window); + +void GlobalPressedKeys_releaseAll(); + +typedef struct _GtkAccelGroup GtkAccelGroup; +extern GtkAccelGroup* global_accel; +void global_accel_init(); +void global_accel_destroy(); + +GClosure* global_accel_group_find(Accelerator accelerator); + +void command_connect_accelerator(const Accelerator& accelerator, const Callback& callback); +void command_disconnect_accelerator(const Accelerator& accelerator, const Callback& callback); + + +class Command +{ +public: + Callback m_callback; + const Accelerator& m_accelerator; + Command(const Callback& callback, const Accelerator& accelerator) : m_callback(callback), m_accelerator(accelerator) + { + } +}; + +class Toggle +{ +public: + Command m_command; + BoolExportCallback m_exportCallback; + Toggle(const Callback& callback, const Accelerator& accelerator, const BoolExportCallback& exportCallback) : m_command(callback, accelerator), m_exportCallback(exportCallback) + { + } +}; + +class KeyEvent +{ +public: + const Accelerator& m_accelerator; + Callback m_keyDown; + Callback m_keyUp; + KeyEvent(const Accelerator& accelerator, const Callback& keyDown, const Callback& keyUp) : m_accelerator(accelerator), m_keyDown(keyDown), m_keyUp(keyUp) + { + } +}; + + + +struct PressedButtons; +typedef struct _GtkWidget GtkWidget; +void PressedButtons_connect(PressedButtons& pressedButtons, GtkWidget* widget); + +extern PressedButtons g_pressedButtons; + +#endif diff --git a/tools/urt/libs/gtkutil/button.cpp b/tools/urt/libs/gtkutil/button.cpp new file mode 100644 index 00000000..ba813701 --- /dev/null +++ b/tools/urt/libs/gtkutil/button.cpp @@ -0,0 +1,117 @@ + +#include "button.h" + +#include + +#include "stream/textstream.h" +#include "stream/stringstream.h" +#include "generic/callback.h" + +#include "image.h" +#include "pointer.h" + +void clicked_closure_callback(GtkWidget* widget, gpointer data) +{ + (*reinterpret_cast(data))(); +} + +void button_connect_callback(GtkButton* button, const Callback& callback) +{ +#if 1 + g_signal_connect_swapped(G_OBJECT(button), "clicked", G_CALLBACK(callback.getThunk()), callback.getEnvironment()); +#else + g_signal_connect_closure(G_OBJECT(button), "clicked", create_cclosure(G_CALLBACK(clicked_closure_callback), callback), FALSE); +#endif +} + +guint toggle_button_connect_callback(GtkToggleButton* button, const Callback& callback) +{ +#if 1 + guint handler = g_signal_connect_swapped(G_OBJECT(button), "toggled", G_CALLBACK(callback.getThunk()), callback.getEnvironment()); +#else + guint handler = g_signal_connect_closure(G_OBJECT(button), "toggled", create_cclosure(G_CALLBACK(clicked_closure_callback), callback), TRUE); +#endif + g_object_set_data(G_OBJECT(button), "handler", gint_to_pointer(handler)); + return handler; +} + +void button_set_icon(GtkButton* button, const char* icon) +{ + GtkImage* image = new_local_image(icon); + gtk_widget_show(GTK_WIDGET(image)); + gtk_container_add(GTK_CONTAINER(button), GTK_WIDGET(image)); +} + +void toggle_button_set_active_no_signal(GtkToggleButton* button, gboolean active) +{ + //globalOutputStream() << "set active: " << active << "\n"; + guint handler_id = gpointer_to_int(g_object_get_data(G_OBJECT(button), "handler")); + //guint signal_id = g_signal_lookup("toggled", G_OBJECT_TYPE (button)); + //globalOutputStream() << "signal_id: " << signal_id << "\n"; + //guint found = g_signal_handler_find(G_OBJECT(button), G_SIGNAL_MATCH_ID, signal_id, 0, 0, 0, 0); + //globalOutputStream() << " handler found: " << found << "\n"; + g_signal_handler_block(G_OBJECT(button), handler_id); + gtk_toggle_button_set_active(button, active); + g_signal_handler_unblock(G_OBJECT(button), handler_id); +} + + +void radio_button_print_state(GtkRadioButton* button) +{ + globalOutputStream() << "toggle button: "; + for(GSList* radio = gtk_radio_button_group(button); radio != 0; radio = g_slist_next(radio)) + { + globalOutputStream() << gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (radio->data)); + } + globalOutputStream() << "\n"; +} + +GtkToggleButton* radio_button_get_nth(GtkRadioButton* radio, int index) +{ + GSList *group = gtk_radio_button_group(radio); + return GTK_TOGGLE_BUTTON(g_slist_nth_data(group, g_slist_length(group) - index - 1)); +} + +void radio_button_set_active(GtkRadioButton* radio, int index) +{ + //radio_button_print_state(radio); + gtk_toggle_button_set_active(radio_button_get_nth(radio, index), TRUE); + //radio_button_print_state(radio); +} + +void radio_button_set_active_no_signal(GtkRadioButton* radio, int index) +{ + { + for(GSList* l = gtk_radio_button_get_group(radio); l != 0; l = g_slist_next(l)) + { + g_signal_handler_block(G_OBJECT(l->data), gpointer_to_int(g_object_get_data(G_OBJECT(l->data), "handler"))); + } + } + radio_button_set_active(radio, index); + { + for(GSList* l = gtk_radio_button_get_group(radio); l != 0; l = g_slist_next(l)) + { + g_signal_handler_unblock(G_OBJECT(l->data), gpointer_to_int(g_object_get_data(G_OBJECT(l->data), "handler"))); + } + } +} + +int radio_button_get_active(GtkRadioButton* radio) +{ + //radio_button_print_state(radio); + GSList *group = gtk_radio_button_group(radio); + int index = g_slist_length(group) - 1; + for (; group != 0; group = g_slist_next(group)) + { + if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(group->data))) + { + break; + } + else + { + index--; + } + } + return index; +} + diff --git a/tools/urt/libs/gtkutil/button.h b/tools/urt/libs/gtkutil/button.h new file mode 100644 index 00000000..5916de6f --- /dev/null +++ b/tools/urt/libs/gtkutil/button.h @@ -0,0 +1,23 @@ + +#if !defined(INCLUDED_GTKUTIL_BUTTON_H) +#define INCLUDED_GTKUTIL_BUTTON_H + +typedef struct _GtkButton GtkButton; +typedef struct _GtkToggleButton GtkToggleButton; +typedef struct _GtkRadioButton GtkRadioButton; +typedef int gint; +typedef gint gboolean; +typedef unsigned int guint; +class Callback; + +void button_connect_callback(GtkButton* button, const Callback& callback); +guint toggle_button_connect_callback(GtkToggleButton* button, const Callback& callback); + +void button_set_icon(GtkButton* button, const char* icon); +void toggle_button_set_active_no_signal(GtkToggleButton* item, gboolean active); + +void radio_button_set_active(GtkRadioButton* radio, int index); +void radio_button_set_active_no_signal(GtkRadioButton* radio, int index); +int radio_button_get_active(GtkRadioButton* radio); + +#endif diff --git a/tools/urt/libs/gtkutil/clipboard.cpp b/tools/urt/libs/gtkutil/clipboard.cpp new file mode 100644 index 00000000..1f764b21 --- /dev/null +++ b/tools/urt/libs/gtkutil/clipboard.cpp @@ -0,0 +1,142 @@ + +#include "clipboard.h" + +#include "stream/memstream.h" +#include "stream/textstream.h" + + +/// \file +/// \brief Platform-independent GTK clipboard support. +/// \todo Using GDK_SELECTION_CLIPBOARD fails on win32, so we use the win32 API directly for now. +#if defined (__linux__) || defined (__APPLE__) + +#include + +enum +{ + RADIANT_CLIPPINGS = 23, +}; + +static const GtkTargetEntry clipboard_targets[] = { + { "RADIANT_CLIPPINGS", 0, RADIANT_CLIPPINGS, }, +}; + +static void clipboard_get (GtkClipboard *clipboard, GtkSelectionData *selection_data, guint info, gpointer data) +{ + std::size_t len = *reinterpret_cast(data); + const char* buffer = (len != 0) ? reinterpret_cast(data) + sizeof(std::size_t) : 0; + + GdkAtom type = GDK_NONE; + if(info == clipboard_targets[0].info) + { + type = gdk_atom_intern(clipboard_targets[0].target, FALSE); + } + + gtk_selection_data_set (selection_data, type, 8, reinterpret_cast(buffer), static_cast(len)); +} + +static void clipboard_clear (GtkClipboard *clipboard, gpointer data) +{ + delete [] reinterpret_cast(data); +} + +static void clipboard_received (GtkClipboard *clipboard, GtkSelectionData *data, gpointer user_data) +{ + if (data->length < 0) + { + globalErrorStream() << "Error retrieving selection\n"; + } + else if(strcmp(gdk_atom_name(data->type), clipboard_targets[0].target) == 0) + { + BufferInputStream istream(reinterpret_cast(data->data), data->length); + (*reinterpret_cast(user_data))(istream); + } +} + +void clipboard_copy(ClipboardCopyFunc copy) +{ + GtkClipboard* clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); + + BufferOutputStream ostream; + copy(ostream); + std::size_t length = ostream.size(); + char* data = new char[length + sizeof(std::size_t)]; + *reinterpret_cast(data) = length; + memcpy(data + sizeof(std::size_t), ostream.data(), length); + + gtk_clipboard_set_with_data (clipboard, clipboard_targets, 1, clipboard_get, clipboard_clear, data); +} + +ClipboardPasteFunc g_clipboardPasteFunc = 0; +void clipboard_paste(ClipboardPasteFunc paste) +{ + GtkClipboard* clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); + + g_clipboardPasteFunc = paste; + gtk_clipboard_request_contents (clipboard, gdk_atom_intern(clipboard_targets[0].target, FALSE), clipboard_received, &g_clipboardPasteFunc); +} + +#elif defined(WIN32) + +const char* c_clipboard_format = "RadiantClippings"; + +#include + +void clipboard_copy(ClipboardCopyFunc copy) +{ + BufferOutputStream ostream; + copy(ostream); + + bool bClipped = false; + UINT nClipboard = ::RegisterClipboardFormat(c_clipboard_format); + if (nClipboard > 0) + { + if (::OpenClipboard(0)) + { + EmptyClipboard(); + std::size_t length = ostream.size(); + HANDLE h = ::GlobalAlloc(GMEM_ZEROINIT | GMEM_MOVEABLE | GMEM_DDESHARE, length + sizeof(std::size_t)); + if (h != 0) + { + char *buffer = reinterpret_cast(::GlobalLock(h)); + *reinterpret_cast(buffer) = length; + buffer += sizeof(std::size_t); + memcpy(buffer, ostream.data(), length); + ::GlobalUnlock(h); + ::SetClipboardData(nClipboard, h); + ::CloseClipboard(); + bClipped = true; + } + } + } + + if (!bClipped) + { + globalOutputStream() << "Unable to register Windows clipboard formats, copy/paste between editors will not be possible\n"; + } +} + +void clipboard_paste(ClipboardPasteFunc paste) +{ + UINT nClipboard = ::RegisterClipboardFormat(c_clipboard_format); + if (nClipboard > 0 && ::OpenClipboard(0)) + { + if(IsClipboardFormatAvailable(nClipboard)) + { + HANDLE h = ::GetClipboardData(nClipboard); + if(h) + { + const char *buffer = reinterpret_cast(::GlobalLock(h)); + std::size_t length = *reinterpret_cast(buffer); + buffer += sizeof(std::size_t); + BufferInputStream istream(buffer, length); + paste(istream); + ::GlobalUnlock(h); + } + } + ::CloseClipboard(); + } +} + + +#endif diff --git a/tools/urt/libs/gtkutil/clipboard.h b/tools/urt/libs/gtkutil/clipboard.h new file mode 100644 index 00000000..1bdda04b --- /dev/null +++ b/tools/urt/libs/gtkutil/clipboard.h @@ -0,0 +1,13 @@ + +#if !defined(INCLUDED_GTKUTIL_CLIPBOARD_H) +#define INCLUDED_GTKUTIL_CLIPBOARD_H + +class TextOutputStream; +typedef void(*ClipboardCopyFunc)(TextOutputStream&); +void clipboard_copy(ClipboardCopyFunc copy); + +class TextInputStream; +typedef void(*ClipboardPasteFunc)(TextInputStream&); +void clipboard_paste(ClipboardPasteFunc paste); + +#endif diff --git a/tools/urt/libs/gtkutil/closure.cpp b/tools/urt/libs/gtkutil/closure.cpp new file mode 100644 index 00000000..cf960bb2 --- /dev/null +++ b/tools/urt/libs/gtkutil/closure.cpp @@ -0,0 +1,3 @@ + +#include "closure.h" + diff --git a/tools/urt/libs/gtkutil/closure.h b/tools/urt/libs/gtkutil/closure.h new file mode 100644 index 00000000..b1325e16 --- /dev/null +++ b/tools/urt/libs/gtkutil/closure.h @@ -0,0 +1,57 @@ + +#if !defined(INCLUDED_GTKUTIL_CLOSURE_H) +#define INCLUDED_GTKUTIL_CLOSURE_H + +#include +#include "generic/callback.h" + +inline void closure_destroy(gpointer data, GClosure* closure) +{ + delete reinterpret_cast(data); +} + +inline GClosure* create_cclosure(GCallback func, const Callback& callback) +{ + return g_cclosure_new(func, new Callback(callback), closure_destroy); +} + +inline GValue GValue_default() +{ + GValue value; + value.g_type = 0; + return value; +} + +inline gint object_get_int_property(GObject* object, const char* property) +{ + GValue gvalue = GValue_default(); + g_value_init(&gvalue, G_TYPE_INT); + g_object_get_property(object, property, &gvalue); + return g_value_get_int(&gvalue); +} + +inline void object_set_int_property(GObject* object, const char* property, gint value) +{ + GValue gvalue = GValue_default(); + g_value_init(&gvalue, G_TYPE_INT); + g_value_set_int(&gvalue, value); + g_object_set_property(object, property, &gvalue); +} + +inline gboolean object_get_boolean_property(GObject* object, const char* property) +{ + GValue gvalue = GValue_default(); + g_value_init(&gvalue, G_TYPE_BOOLEAN); + g_object_get_property(object, property, &gvalue); + return g_value_get_boolean(&gvalue); +} + +inline void object_set_boolean_property(GObject* object, const char* property, gboolean value) +{ + GValue gvalue = GValue_default(); + g_value_init(&gvalue, G_TYPE_BOOLEAN); + g_value_set_boolean(&gvalue, value); + g_object_set_property(object, property, &gvalue); +} + +#endif diff --git a/tools/urt/libs/gtkutil/container.cpp b/tools/urt/libs/gtkutil/container.cpp new file mode 100644 index 00000000..6cf60ed4 --- /dev/null +++ b/tools/urt/libs/gtkutil/container.cpp @@ -0,0 +1,3 @@ + +#include "container.h" + diff --git a/tools/urt/libs/gtkutil/container.h b/tools/urt/libs/gtkutil/container.h new file mode 100644 index 00000000..ddd5bfa4 --- /dev/null +++ b/tools/urt/libs/gtkutil/container.h @@ -0,0 +1,23 @@ + +#if !defined(INCLUDED_GTKUTIL_CONTAINER_H) +#define INCLUDED_GTKUTIL_CONTAINER_H + +#include + +inline GtkWidget* container_add_widget(GtkContainer* container, GtkWidget* widget) +{ + gtk_container_add(container, widget); + return widget; +} + +inline void container_remove(GtkWidget* item, gpointer data) +{ + gtk_container_remove(GTK_CONTAINER(data), item); +} + +inline void container_remove_all(GtkContainer* container) +{ + gtk_container_foreach(container, container_remove, container); +} + +#endif diff --git a/tools/urt/libs/gtkutil/cursor.cpp b/tools/urt/libs/gtkutil/cursor.cpp new file mode 100644 index 00000000..cff7c7e6 --- /dev/null +++ b/tools/urt/libs/gtkutil/cursor.cpp @@ -0,0 +1,71 @@ + +#include "cursor.h" + + +#include +#include +#include + + +GdkCursor* create_blank_cursor() +{ + GdkPixmap *pixmap; + GdkBitmap *mask; + char buffer [(32 * 32)/8]; + memset (buffer, 0, (32 * 32)/8); + GdkColor white = {0, 0xffff, 0xffff, 0xffff}; + GdkColor black = {0, 0x0000, 0x0000, 0x0000}; + pixmap = gdk_bitmap_create_from_data(0, buffer, 32, 32); + mask = gdk_bitmap_create_from_data(0, buffer, 32, 32); + GdkCursor *cursor = gdk_cursor_new_from_pixmap(pixmap, mask, &white, &black, 1, 1); + gdk_drawable_unref(pixmap); + gdk_drawable_unref(mask); + + return cursor; +} + +void blank_cursor(GtkWidget* widget) +{ + GdkCursor* cursor = create_blank_cursor(); + gdk_window_set_cursor (widget->window, cursor); + gdk_cursor_unref(cursor); +} + +void default_cursor(GtkWidget* widget) +{ + gdk_window_set_cursor(widget->window, 0); +} + + +#if defined(WIN32) + +#include + +void Sys_GetCursorPos (int *x, int *y) +{ + POINT pos; + GetCursorPos(&pos); + *x = pos.x; + *y = pos.y; +} + +void Sys_SetCursorPos (int x, int y) +{ + SetCursorPos (x, y); +} + +#else + +#include + +void Sys_GetCursorPos (int *x, int *y) +{ + gdk_display_get_pointer(gdk_display_get_default(), 0, x, y, 0); +} + +void Sys_SetCursorPos (int x, int y) +{ + XWarpPointer (GDK_DISPLAY(), None, GDK_ROOT_WINDOW(), 0, 0, 0, 0, x, y); +} + +#endif diff --git a/tools/urt/libs/gtkutil/cursor.h b/tools/urt/libs/gtkutil/cursor.h new file mode 100644 index 00000000..2782ef9c --- /dev/null +++ b/tools/urt/libs/gtkutil/cursor.h @@ -0,0 +1,173 @@ + +#if !defined(INCLUDED_GTKUTIL_CURSOR_H) +#define INCLUDED_GTKUTIL_CURSOR_H + +#include +#include +#include + +#include "debugging/debugging.h" + +typedef struct _GdkCursor GdkCursor; +typedef struct _GtkWidget GtkWidget; + +GdkCursor* create_blank_cursor(); +void blank_cursor(GtkWidget* widget); +void default_cursor(GtkWidget* widget); +void Sys_GetCursorPos (int *x, int *y); +void Sys_SetCursorPos (int x, int y); + + + +class DeferredMotion +{ + guint m_handler; + typedef void(*MotionFunction)(gdouble x, gdouble y, guint state, void* data); + MotionFunction m_function; + void* m_data; + gdouble m_x; + gdouble m_y; + guint m_state; + + static gboolean deferred(DeferredMotion* self) + { + self->m_handler = 0; + self->m_function(self->m_x, self->m_y, self->m_state, self->m_data); + return FALSE; + } +public: + DeferredMotion(MotionFunction function, void* data) : m_handler(0), m_function(function), m_data(data) + { + } + void motion(gdouble x, gdouble y, guint state) + { + m_x = x; + m_y = y; + m_state = state; + if(m_handler == 0) + { + m_handler = g_idle_add((GSourceFunc)deferred, this); + } + } + static gboolean gtk_motion(GtkWidget *widget, GdkEventMotion *event, DeferredMotion* self) + { + self->motion(event->x, event->y, event->state); + return FALSE; + } +}; + +class DeferredMotionDelta +{ + int m_delta_x; + int m_delta_y; + guint m_motion_handler; + typedef void (*MotionDeltaFunction)(int x, int y, void* data); + MotionDeltaFunction m_function; + void* m_data; + + static gboolean deferred_motion(gpointer data) + { + reinterpret_cast(data)->m_function( + reinterpret_cast(data)->m_delta_x, + reinterpret_cast(data)->m_delta_y, + reinterpret_cast(data)->m_data + ); + reinterpret_cast(data)->m_motion_handler = 0; + reinterpret_cast(data)->m_delta_x = 0; + reinterpret_cast(data)->m_delta_y = 0; + return FALSE; + } +public: + DeferredMotionDelta(MotionDeltaFunction function, void* data) : m_delta_x(0), m_delta_y(0), m_motion_handler(0), m_function(function), m_data(data) + { + } + void flush() + { + if(m_motion_handler != 0) + { + g_source_remove(m_motion_handler); + deferred_motion(this); + } + } + void motion_delta(int x, int y, unsigned int state) + { + m_delta_x += x; + m_delta_y += y; + if(m_motion_handler == 0) + { + m_motion_handler = g_idle_add(deferred_motion, this); + } + } +}; + +class FreezePointer +{ + unsigned int handle_motion; + int recorded_x, recorded_y; + typedef void (*MotionDeltaFunction)(int x, int y, unsigned int state, void* data); + MotionDeltaFunction m_function; + void* m_data; +public: + FreezePointer() : handle_motion(0), m_function(0), m_data(0) + { + } + static gboolean motion_delta(GtkWidget *widget, GdkEventMotion *event, FreezePointer* self) + { + int current_x, current_y; + Sys_GetCursorPos(¤t_x, ¤t_y); + int dx = current_x - self->recorded_x; + int dy = current_y - self->recorded_y; + if(dx != 0 || dy != 0) + { + //globalOutputStream() << "motion x: " << dx << ", y: " << dy << "\n"; + Sys_SetCursorPos(self->recorded_x, self->recorded_y); + self->m_function(dx, dy, event->state, self->m_data); + } + return FALSE; + } + + void freeze_pointer(GtkWidget* window, MotionDeltaFunction function, void* data) + { + ASSERT_MESSAGE(m_function == 0, "can't freeze pointer"); + + blank_cursor(window); + + const GdkEventMask mask = static_cast(GDK_POINTER_MOTION_MASK + | GDK_POINTER_MOTION_HINT_MASK + | GDK_BUTTON_MOTION_MASK + | GDK_BUTTON1_MOTION_MASK + | GDK_BUTTON2_MOTION_MASK + | GDK_BUTTON3_MOTION_MASK + | GDK_BUTTON_PRESS_MASK + | GDK_BUTTON_RELEASE_MASK + | GDK_VISIBILITY_NOTIFY_MASK); + + //GdkGrabStatus status = + gdk_pointer_grab(window->window, TRUE, mask, window->window, 0, GDK_CURRENT_TIME); + + Sys_GetCursorPos(&recorded_x, &recorded_y); + + Sys_SetCursorPos(recorded_x, recorded_y); + + m_function = function; + m_data = data; + + handle_motion = g_signal_connect(G_OBJECT(window), "motion_notify_event", G_CALLBACK(motion_delta), this); + } + + void unfreeze_pointer(GtkWidget* window) + { + g_signal_handler_disconnect(G_OBJECT(window), handle_motion); + + m_function = 0; + m_data = 0; + + Sys_SetCursorPos(recorded_x, recorded_y); + + gdk_pointer_ungrab(GDK_CURRENT_TIME); + + default_cursor(window); + } +}; + +#endif diff --git a/tools/urt/libs/gtkutil/dialog.cpp b/tools/urt/libs/gtkutil/dialog.cpp new file mode 100644 index 00000000..5e5f8c88 --- /dev/null +++ b/tools/urt/libs/gtkutil/dialog.cpp @@ -0,0 +1,283 @@ + +#include "dialog.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "button.h" +#include "window.h" + +GtkVBox* create_dialog_vbox(int spacing, int border) +{ + GtkVBox* vbox = GTK_VBOX(gtk_vbox_new(FALSE, spacing)); + gtk_widget_show(GTK_WIDGET(vbox)); + gtk_container_set_border_width(GTK_CONTAINER(vbox), border); + return vbox; +} + +GtkHBox* create_dialog_hbox(int spacing, int border) +{ + GtkHBox* hbox = GTK_HBOX(gtk_hbox_new(FALSE, spacing)); + gtk_widget_show(GTK_WIDGET(hbox)); + gtk_container_set_border_width(GTK_CONTAINER(hbox), border); + return hbox; +} + +GtkFrame* create_dialog_frame(const char* label, GtkShadowType shadow) +{ + GtkFrame* frame = GTK_FRAME(gtk_frame_new(label)); + gtk_widget_show(GTK_WIDGET(frame)); + gtk_frame_set_shadow_type(frame, shadow); + return frame; +} + +GtkTable* create_dialog_table(unsigned int rows, unsigned int columns, unsigned int row_spacing, unsigned int col_spacing, int border) +{ + GtkTable* table = GTK_TABLE(gtk_table_new(rows, columns, FALSE)); + gtk_widget_show(GTK_WIDGET(table)); + gtk_table_set_row_spacings(table, row_spacing); + gtk_table_set_col_spacings(table, col_spacing); + gtk_container_set_border_width(GTK_CONTAINER(table), border); + return table; +} + +GtkButton* create_dialog_button(const char* label, GCallback func, gpointer data) +{ + GtkButton* button = GTK_BUTTON(gtk_button_new_with_label(label)); + gtk_widget_set_size_request(GTK_WIDGET(button), 64, -1); + gtk_widget_show(GTK_WIDGET(button)); + g_signal_connect(G_OBJECT(button), "clicked", func, data); + return button; +} + +GtkWindow* create_dialog_window(GtkWindow* parent, const char* title, GCallback func, gpointer data, int default_w, int default_h) +{ + GtkWindow* window = create_floating_window(title, parent); + gtk_window_set_default_size(window, default_w, default_h); + gtk_window_set_position(window, GTK_WIN_POS_CENTER_ON_PARENT); + g_signal_connect(G_OBJECT(window), "delete_event", func, data); + + return window; +} + +gboolean modal_dialog_button_clicked(GtkWidget *widget, ModalDialogButton* button) +{ + button->m_dialog.loop = false; + button->m_dialog.ret = button->m_value; + return TRUE; +} + +gboolean modal_dialog_delete(GtkWidget *widget, GdkEvent* event, ModalDialog* dialog) +{ + dialog->loop = 0; + dialog->ret = eIDCANCEL; + return TRUE; +} + +EMessageBoxReturn modal_dialog_show(GtkWindow* window, ModalDialog& dialog) +{ + gtk_grab_add(GTK_WIDGET(window)); + gtk_widget_show(GTK_WIDGET(window)); + + dialog.loop = true; + while(dialog.loop) + { + gtk_main_iteration(); + } + + gtk_widget_hide(GTK_WIDGET(window)); + gtk_grab_remove(GTK_WIDGET(window)); + + return dialog.ret; +} + +GtkButton* create_modal_dialog_button(const char* label, ModalDialogButton& button) +{ + return create_dialog_button(label, G_CALLBACK(modal_dialog_button_clicked), &button); +} + +GtkWindow* create_modal_dialog_window(GtkWindow* parent, const char* title, ModalDialog& dialog, int default_w, int default_h) +{ + return create_dialog_window(parent, title, G_CALLBACK(modal_dialog_delete), &dialog, default_w, default_h); +} + +GtkWindow* create_fixedsize_modal_dialog_window(GtkWindow* parent, const char* title, ModalDialog& dialog, int width, int height) +{ + GtkWindow* window = create_modal_dialog_window(parent, title, dialog, width, height); + + gtk_window_set_resizable(window, FALSE); + gtk_window_set_modal(window, TRUE); + gtk_window_set_position(window, GTK_WIN_POS_CENTER); + + window_remove_minmax(window); + + //gtk_widget_set_size_request(GTK_WIDGET(window), width, height); + //gtk_window_set_default_size(window, width, height); + //gtk_window_resize(window, width, height); + //GdkGeometry geometry = { width, height, -1, -1, width, height, -1, -1, -1, -1, GDK_GRAVITY_STATIC, }; + //gtk_window_set_geometry_hints(window, GTK_WIDGET(window), &geometry, (GdkWindowHints)(GDK_HINT_POS|GDK_HINT_MIN_SIZE|GDK_HINT_BASE_SIZE)); + + return window; +} + +gboolean dialog_button_ok(GtkWidget *widget, ModalDialog* data) +{ + data->loop = false; + data->ret = eIDOK; + return TRUE; +} + +gboolean dialog_button_cancel(GtkWidget *widget, ModalDialog* data) +{ + data->loop = false; + data->ret = eIDCANCEL; + return TRUE; +} + +gboolean dialog_button_yes(GtkWidget *widget, ModalDialog* data) +{ + data->loop = false; + data->ret = eIDYES; + return TRUE; +} + +gboolean dialog_button_no(GtkWidget *widget, ModalDialog* data) +{ + data->loop = false; + data->ret = eIDNO; + return TRUE; +} + +gboolean dialog_delete_callback(GtkWidget *widget, GdkEventAny* event, ModalDialog* data) +{ + gtk_widget_hide(widget); + data->loop = false; + return TRUE; +} + +GtkWindow* create_simple_modal_dialog_window(const char* title, ModalDialog& dialog, GtkWidget* contents) +{ + GtkWindow* window = create_fixedsize_modal_dialog_window(0, title, dialog); + + GtkVBox* vbox1 = create_dialog_vbox(8, 4); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(vbox1)); + + gtk_container_add(GTK_CONTAINER(vbox1), contents); + + GtkAlignment* alignment = GTK_ALIGNMENT(gtk_alignment_new(0.5, 0.0, 0.0, 0.0)); + gtk_widget_show(GTK_WIDGET(alignment)); + gtk_box_pack_start(GTK_BOX(vbox1), GTK_WIDGET(alignment), FALSE, FALSE, 0); + + GtkButton* button = create_dialog_button("OK", G_CALLBACK(dialog_button_ok), &dialog); + gtk_container_add(GTK_CONTAINER(alignment), GTK_WIDGET(button)); + + return window; +} + +RadioHBox RadioHBox_new(StringArrayRange names) +{ + GtkHBox* hbox = GTK_HBOX(gtk_hbox_new(TRUE, 4)); + gtk_widget_show(GTK_WIDGET(hbox)); + + GSList* group = 0; + GtkRadioButton* radio = 0; + for(StringArrayRange::Iterator i = names.begin; i != names.end; ++i) + { + radio = GTK_RADIO_BUTTON(gtk_radio_button_new_with_label(group, *i)); + gtk_widget_show(GTK_WIDGET(radio)); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(radio), FALSE, FALSE, 0); + + group = gtk_radio_button_get_group(radio); + } + + return RadioHBox(hbox, radio); +} + + +PathEntry PathEntry_new() +{ + GtkFrame* frame = GTK_FRAME(gtk_frame_new(NULL)); + gtk_widget_show(GTK_WIDGET(frame)); + gtk_frame_set_shadow_type(frame, GTK_SHADOW_IN); + + // path entry + GtkHBox* hbox = GTK_HBOX(gtk_hbox_new(FALSE, 0)); + gtk_widget_show(GTK_WIDGET(hbox)); + + GtkEntry* entry = GTK_ENTRY(gtk_entry_new()); + gtk_entry_set_has_frame(entry, FALSE); + gtk_widget_show(GTK_WIDGET(entry)); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(entry), TRUE, TRUE, 0); + + // browse button + GtkButton* button = GTK_BUTTON(gtk_button_new()); + button_set_icon(button, "ellipsis.bmp"); + gtk_widget_show(GTK_WIDGET(button)); + gtk_box_pack_end(GTK_BOX(hbox), GTK_WIDGET(button), FALSE, FALSE, 0); + + gtk_container_add(GTK_CONTAINER(frame), GTK_WIDGET(hbox)); + + return PathEntry(frame, entry, button); +} + +void PathEntry_setPath(PathEntry& self, const char* path) +{ + gtk_entry_set_text(self.m_entry, path); +} +typedef ReferenceCaller1 PathEntrySetPathCaller; + +void BrowsedPathEntry_clicked(GtkWidget* widget, BrowsedPathEntry* self) +{ + self->m_browse(PathEntrySetPathCaller(self->m_entry)); +} + +BrowsedPathEntry::BrowsedPathEntry(const BrowseCallback& browse) : + m_entry(PathEntry_new()), + m_browse(browse) +{ + g_signal_connect(G_OBJECT(m_entry.m_button), "clicked", G_CALLBACK(BrowsedPathEntry_clicked), this); +} + + +GtkLabel* DialogLabel_new(const char* name) +{ + GtkLabel* label = GTK_LABEL(gtk_label_new(name)); + gtk_widget_show(GTK_WIDGET(label)); + gtk_misc_set_alignment(GTK_MISC(label), 1, 0.5); + gtk_label_set_justify(label, GTK_JUSTIFY_LEFT); + + return label; +} + +GtkTable* DialogRow_new(const char* name, GtkWidget* widget) +{ + GtkTable* table = GTK_TABLE(gtk_table_new(1, 3, TRUE)); + gtk_widget_show(GTK_WIDGET(table)); + + gtk_table_set_col_spacings(table, 4); + gtk_table_set_row_spacings(table, 0); + + gtk_table_attach(table, GTK_WIDGET(DialogLabel_new(name)), 0, 1, 0, 1, + (GtkAttachOptions) (GTK_EXPAND|GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + gtk_table_attach(table, widget, 1, 3, 0, 1, + (GtkAttachOptions) (GTK_EXPAND|GTK_FILL), + (GtkAttachOptions) (0), 0, 0); + + return table; +} + +void DialogVBox_packRow(GtkVBox* vbox, GtkWidget* row) +{ + gtk_box_pack_start(GTK_BOX(vbox), row, FALSE, FALSE, 0); +} + diff --git a/tools/urt/libs/gtkutil/dialog.h b/tools/urt/libs/gtkutil/dialog.h new file mode 100644 index 00000000..d1117119 --- /dev/null +++ b/tools/urt/libs/gtkutil/dialog.h @@ -0,0 +1,125 @@ + +#if !defined(INCLUDED_GTKUTIL_DIALOG_H) +#define INCLUDED_GTKUTIL_DIALOG_H + +#include "generic/callback.h" +#include "generic/arrayrange.h" +#include "qerplugin.h" +#include + +typedef int gint; +typedef gint gboolean; +typedef struct _GdkEventAny GdkEventAny; +typedef struct _GtkWidget GtkWidget; +typedef struct _GtkHBox GtkHBox; +typedef struct _GtkVBox GtkVBox; +typedef struct _GtkRadioButton GtkRadioButton; +typedef struct _GtkFrame GtkFrame; +typedef struct _GtkEntry GtkEntry; +typedef struct _GtkButton GtkButton; +typedef struct _GtkLabel GtkLabel; +typedef struct _GtkTable GtkTable; + + +struct ModalDialog +{ + ModalDialog() + : loop(true), ret(eIDCANCEL) + { + } + bool loop; + EMessageBoxReturn ret; +}; + +struct ModalDialogButton +{ + ModalDialogButton(ModalDialog& dialog, EMessageBoxReturn value) + : m_dialog(dialog), m_value(value) + { + } + ModalDialog& m_dialog; + EMessageBoxReturn m_value; +}; + +typedef void (*GCallback)(void); +typedef void* gpointer; +typedef struct _GtkWindow GtkWindow; +typedef struct _GtkTable GtkTable; +typedef struct _GtkButton GtkButton; +typedef struct _GtkVBox GtkVBox; +typedef struct _GtkHBox GtkHBox; +typedef struct _GtkFrame GtkFrame; + +GtkWindow* create_fixedsize_modal_window(GtkWindow* parent, const char* title, int width, int height); + +GtkWindow* create_dialog_window(GtkWindow* parent, const char* title, GCallback func, gpointer data, int default_w = -1, int default_h = -1); +GtkTable* create_dialog_table(unsigned int rows, unsigned int columns, unsigned int row_spacing, unsigned int col_spacing, int border = 0); +GtkButton* create_dialog_button(const char* label, GCallback func, gpointer data); +GtkVBox* create_dialog_vbox(int spacing, int border = 0); +GtkHBox* create_dialog_hbox(int spacing, int border = 0); +GtkFrame* create_dialog_frame(const char* label, GtkShadowType shadow = GTK_SHADOW_ETCHED_IN); + +GtkButton* create_modal_dialog_button(const char* label, ModalDialogButton& button); +GtkWindow* create_modal_dialog_window(GtkWindow* parent, const char* title, ModalDialog& dialog, int default_w = -1, int default_h = -1); +GtkWindow* create_fixedsize_modal_dialog_window(GtkWindow* parent, const char* title, ModalDialog& dialog, int width = -1, int height = -1); +EMessageBoxReturn modal_dialog_show(GtkWindow* window, ModalDialog& dialog); + + +gboolean dialog_button_ok(GtkWidget *widget, ModalDialog* data); +gboolean dialog_button_cancel(GtkWidget *widget, ModalDialog* data); +gboolean dialog_button_yes(GtkWidget *widget, ModalDialog* data); +gboolean dialog_button_no(GtkWidget *widget, ModalDialog* data); +gboolean dialog_delete_callback(GtkWidget *widget, GdkEventAny* event, ModalDialog* data); + +GtkWindow* create_simple_modal_dialog_window(const char* title, ModalDialog& dialog, GtkWidget* contents); + +class RadioHBox +{ +public: + GtkHBox* m_hbox; + GtkRadioButton* m_radio; + RadioHBox(GtkHBox* hbox, GtkRadioButton* radio) : + m_hbox(hbox), + m_radio(radio) + { + } +}; + +RadioHBox RadioHBox_new(StringArrayRange names); + + +class PathEntry +{ +public: + GtkFrame* m_frame; + GtkEntry* m_entry; + GtkButton* m_button; + PathEntry(GtkFrame* frame, GtkEntry* entry, GtkButton* button) : + m_frame(frame), + m_entry(entry), + m_button(button) + { + } +}; + +PathEntry PathEntry_new(); + +class BrowsedPathEntry +{ +public: + typedef Callback1 SetPathCallback; + typedef Callback1 BrowseCallback; + + PathEntry m_entry; + BrowseCallback m_browse; + + BrowsedPathEntry(const BrowseCallback& browse); +}; + +GtkLabel* DialogLabel_new(const char* name); +GtkTable* DialogRow_new(const char* name, GtkWidget* widget); +typedef struct _GtkVBox GtkVBox; +void DialogVBox_packRow(GtkVBox* vbox, GtkWidget* row); + + +#endif diff --git a/tools/urt/libs/gtkutil/entry.cpp b/tools/urt/libs/gtkutil/entry.cpp new file mode 100644 index 00000000..b8a33719 --- /dev/null +++ b/tools/urt/libs/gtkutil/entry.cpp @@ -0,0 +1,3 @@ + +#include "entry.h" + diff --git a/tools/urt/libs/gtkutil/entry.h b/tools/urt/libs/gtkutil/entry.h new file mode 100644 index 00000000..a07152f3 --- /dev/null +++ b/tools/urt/libs/gtkutil/entry.h @@ -0,0 +1,43 @@ + +#if !defined(INCLUDED_GTKUTIL_ENTRY_H) +#define INCLUDED_GTKUTIL_ENTRY_H + +#include +#include +#include + +inline void entry_set_string(GtkEntry* entry, const char* string) +{ + gtk_entry_set_text(entry, string); +} + +inline void entry_set_int(GtkEntry* entry, int i) +{ + char buf[32]; + sprintf(buf, "%d", i); + entry_set_string(entry, buf); +} + +inline void entry_set_float(GtkEntry* entry, float f) +{ + char buf[32]; + sprintf(buf, "%g", f); + entry_set_string(entry, buf); +} + +inline const char* entry_get_string(GtkEntry* entry) +{ + return gtk_entry_get_text(entry); +} + +inline int entry_get_int(GtkEntry* entry) +{ + return atoi(entry_get_string(entry)); +} + +inline double entry_get_float(GtkEntry* entry) +{ + return atof(entry_get_string(entry)); +} + +#endif diff --git a/tools/urt/libs/gtkutil/filechooser.cpp b/tools/urt/libs/gtkutil/filechooser.cpp new file mode 100644 index 00000000..676b907c --- /dev/null +++ b/tools/urt/libs/gtkutil/filechooser.cpp @@ -0,0 +1,463 @@ + +#include "filechooser.h" + +#include "ifiletypes.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "string/string.h" +#include "stream/stringstream.h" +#include "container/array.h" +#include "os/path.h" +#include "os/file.h" + +#include "messagebox.h" + + +struct filetype_pair_t +{ + filetype_pair_t() + : m_moduleName("") + { + } + filetype_pair_t(const char* moduleName, filetype_t type) + : m_moduleName(moduleName), m_type(type) + { + } + const char* m_moduleName; + filetype_t m_type; +}; + +class FileTypeList : public IFileTypeList +{ + struct filetype_copy_t + { + filetype_copy_t(const filetype_pair_t& other) + : m_moduleName(other.m_moduleName), m_name(other.m_type.name), m_pattern(other.m_type.pattern) + { + } + CopiedString m_moduleName; + CopiedString m_name; + CopiedString m_pattern; + }; + + typedef std::list Types; + Types m_types; +public: + + typedef Types::const_iterator const_iterator; + const_iterator begin() const + { + return m_types.begin(); + } + const_iterator end() const + { + return m_types.end(); + } + + std::size_t size() const + { + return m_types.size(); + } + + void addType(const char* moduleName, filetype_t type) + { + m_types.push_back(filetype_pair_t(moduleName, type)); + } +}; + +#ifdef WIN32 + +class Win32Filters +{ + const FileTypeList& m_types; + Array m_filters; +public: + Win32Filters(const FileTypeList& typeList) : m_types(typeList) + { + std::size_t len = 0; + for(FileTypeList::const_iterator i = m_types.begin(); i != m_types.end(); ++i) + { + len = len + strlen((*i).m_name.c_str()) + strlen((*i).m_pattern.c_str()) * 2 + 5; + } + m_filters.resize(len + 1); // length + null char + char *w = m_filters.data(); + for(FileTypeList::const_iterator i = m_types.begin(); i != m_types.end(); ++i) + { + for(const char *r = (*i).m_name.c_str(); *r!='\0'; r++, w++) + { + *w = *r; + } + *w++ = ' '; + *w++ = '('; + for(const char *r = (*i).m_pattern.c_str(); *r!='\0'; r++, w++) + { + *w = *r; + } + *w++ = ')'; + *w++ = '\0'; + for(const char *r = (*i).m_pattern.c_str(); *r!='\0'; r++, w++) + { + *w = (*r == ',') ? ';' : *r; + } + *w++ = '\0'; + } + m_filters[len] = '\0'; + } + filetype_pair_t getType(const char *filter) const + { + for(FileTypeList::const_iterator i = m_types.begin(); i != m_types.end(); ++i) + { + if(string_equal((*i).m_pattern.c_str(), filter)) + { + return filetype_pair_t((*i).m_moduleName.c_str(), filetype_t((*i).m_name.c_str(), (*i).m_pattern.c_str())); + } + } + return filetype_pair_t(); + } + const char* getFilters() const + { + return m_filters.data(); + } +}; + +#define WIN32_LEAN_AND_MEAN +#include +#include + +static char szFile[MAX_PATH]; /* filename string */ + + +#define FILEDLG_CUSTOM_FILTER_LENGTH 64 +// to be used with the advanced file selector + +const char* file_dialog_show_win32(GtkWidget* parent, bool open, const char* title, const char* path, const char* pattern) +{ + const char* r; + char* w; + filetype_t type; + FileTypeList typelist; + + if(pattern == 0) + { + pattern = "*"; + } + + GlobalFiletypes().getTypeList(pattern, &typelist); + + Win32Filters filters(typelist); + + // win32 dialog stores the selected "save as type" extension in the second null-terminated string + char customfilter[FILEDLG_CUSTOM_FILTER_LENGTH]; + + static OPENFILENAME ofn; /* common dialog box structure */ + static char szDirName[MAX_PATH]; /* directory string */ + static char szFile[MAX_PATH]; /* filename string */ + static char szFileTitle[MAX_PATH]; /* file title string */ + static int i, cbString; /* integer count variables */ + static HANDLE hf; /* file handle */ + + // do that the native way + /* Place the terminating null character in the szFile. */ + szFile[0] = '\0'; + customfilter[0] = customfilter[1] = customfilter[2] = '\0'; + + /* Set the members of the OPENFILENAME structure. */ + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = (HWND)GDK_WINDOW_HWND(parent->window); + ofn.nFilterIndex = 0; + ofn.lpstrFilter = filters.getFilters(); + ofn.lpstrCustomFilter = customfilter; + ofn.nMaxCustFilter = sizeof(customfilter); + ofn.lpstrFile = szFile; + ofn.nMaxFile = sizeof(szFile); + ofn.lpstrFileTitle = 0; // we don't need to get the name of the file + if(path) + { + // szDirName: Radiant uses unix convention for paths internally + // Win32 (of course) and Gtk (who would have thought) expect the '\\' convention + // copy path, replacing dir separators as appropriate + for(r=path, w=szDirName; *r!='\0'; r++) + *w++ = (*r=='/') ? '\\' : *r; + // terminate string + *w = '\0'; + ofn.lpstrInitialDir = szDirName; + } + else ofn.lpstrInitialDir = 0; + ofn.lpstrTitle = title; + ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY; + + /* Display the Open dialog box. */ + // it's open or close depending on 'open' parameter + if (open) + { + if (!GetOpenFileName(&ofn)) + return 0; // canceled + } + else + { + if (!GetSaveFileName(&ofn)) + return 0; // canceled + } + + if(!string_equal(pattern, "*")) + { + type = filters.getType(customfilter+1).m_type; + } + + // don't return an empty filename + if(szFile[0] == '\0') return 0; + + // convert back to unix format + for(w=szFile; *w!='\0'; w++) + { + if(*w=='\\') + { + *w = '/'; + } + } + // when saving, force an extension depending on filetype + /* \todo SPoG - file_dialog should return filetype information separately.. not force file extension.. */ + if(!open && !string_equal(pattern, "*")) + { + // last ext separator + const char* extension = path_get_extension(szFile); + // no extension + if(string_empty(extension)) + { + strcat(szFile, type.pattern+1); + } + else + { + strcpy(szFile + (extension - szFile), type.pattern+2); + } + } + + return szFile; +} + +#endif + + +class GTKMasks +{ + const FileTypeList& m_types; +public: + std::vector m_filters; + std::vector m_masks; + + GTKMasks(const FileTypeList& types) : m_types(types) + { + m_masks.reserve(m_types.size()); + for(FileTypeList::const_iterator i = m_types.begin(); i != m_types.end(); ++i) + { + std::size_t len = strlen((*i).m_name.c_str()) + strlen((*i).m_pattern.c_str()) + 3; + StringOutputStream buffer(len + 1); // length + null char + + buffer << (*i).m_name.c_str() << " <" << (*i).m_pattern.c_str() << ">"; + + m_masks.push_back(buffer.c_str()); + } + + m_filters.reserve(m_types.size()); + for(FileTypeList::const_iterator i = m_types.begin(); i != m_types.end(); ++i) + { + m_filters.push_back((*i).m_pattern); + } + } + + filetype_pair_t GetTypeForGTKMask(const char *mask) const + { + std::vector::const_iterator j = m_masks.begin(); + for(FileTypeList::const_iterator i = m_types.begin(); i != m_types.end(); ++i, ++j) + { + if(string_equal((*j).c_str(), mask)) + { + return filetype_pair_t((*i).m_moduleName.c_str(), filetype_t((*i).m_name.c_str(), (*i).m_pattern.c_str())); + } + } + return filetype_pair_t(); + } + +}; + +static char g_file_dialog_file[1024]; + +const char* file_dialog_show(GtkWidget* parent, bool open, const char* title, const char* path, const char* pattern) +{ + filetype_t type; + + if(pattern == 0) + { + pattern = "*"; + } + + FileTypeList typelist; + GlobalFiletypes().getTypeList(pattern, &typelist); + + GTKMasks masks(typelist); + + if (title == 0) + title = open ? "Open File" : "Save File"; + + GtkWidget* dialog; + if (open) + { + dialog = gtk_file_chooser_dialog_new(title, + GTK_WINDOW(parent), + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, + NULL); + } + else + { + dialog = gtk_file_chooser_dialog_new(title, + GTK_WINDOW(parent), + GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, + NULL); + gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "unnamed"); + } + + gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); + gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER_ON_PARENT); + + // we expect an actual path below, if the path is 0 we might crash + if (path != 0 && !string_empty(path)) + { + ASSERT_MESSAGE(path_is_absolute(path), "file_dialog_show: path not absolute: " << makeQuoted(path)); + + Array new_path(strlen(path)+1); + + // copy path, replacing dir separators as appropriate + Array::iterator w = new_path.begin(); + for(const char* r = path; *r != '\0'; ++r) + { + *w++ = (*r == '/') ? G_DIR_SEPARATOR : *r; + } + // remove separator from end of path if required + if(*(w-1) == G_DIR_SEPARATOR) + { + --w; + } + // terminate string + *w = '\0'; + + gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), new_path.data()); + } + + // we should add all important paths as shortcut folder... + // gtk_file_chooser_add_shortcut_folder(GTK_FILE_CHOOSER(dialog), "/tmp/", NULL); + + + for(std::size_t i = 0; i < masks.m_filters.size(); ++i) + { + GtkFileFilter* filter = gtk_file_filter_new(); + gtk_file_filter_add_pattern(filter, masks.m_filters[i].c_str()); + gtk_file_filter_set_name(filter, masks.m_masks[i].c_str()); + gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter); + } + + if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) + { + strcpy(g_file_dialog_file, gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog))); + + if(!string_equal(pattern, "*")) + { + GtkFileFilter* filter = gtk_file_chooser_get_filter(GTK_FILE_CHOOSER(dialog)); + type = masks.GetTypeForGTKMask(gtk_file_filter_get_name(filter)).m_type; + // last ext separator + const char* extension = path_get_extension(g_file_dialog_file); + // no extension + if(string_empty(extension)) + { + strcat(g_file_dialog_file, type.pattern+1); + } + else + { + strcpy(g_file_dialog_file + (extension - g_file_dialog_file), type.pattern+2); + } + } + + // convert back to unix format + for(char* w = g_file_dialog_file; *w!='\0'; w++) + { + if(*w=='\\') + { + *w = '/'; + } + } + } + else + { + g_file_dialog_file[0] = '\0'; + } + + gtk_widget_destroy(dialog); + + // don't return an empty filename + if(g_file_dialog_file[0] == '\0') return NULL; + + return g_file_dialog_file; +} + +char* dir_dialog(GtkWidget* parent, const char* title, const char* path) +{ + GtkWidget* dialog = gtk_file_chooser_dialog_new(title, + GTK_WINDOW(parent), + GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, + NULL); + + gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); + gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER_ON_PARENT); + + if(!string_empty(path)) + { + gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), path); + } + + char* filename = 0; + if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) + { + filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + } + + gtk_widget_destroy(dialog); + + return filename; +} + + +#ifdef WIN32 +bool g_FileChooser_nativeGUI = true; +#endif + +const char* file_dialog (GtkWidget* parent, bool open, const char* title, const char* path, const char* pattern) +{ + for(;;) + { + const char* file = +#ifdef WIN32 + g_FileChooser_nativeGUI + ? file_dialog_show_win32(parent, open, title, path, pattern) : +#endif + file_dialog_show(parent, open, title, path, pattern); + + if(open + || !file_exists(file) + || gtk_MessageBox(parent, "The file specified already exists.\nDo you want to replace it?", title, eMB_NOYES, eMB_ICONQUESTION) == eIDYES) + { + return file; + } + } +} diff --git a/tools/urt/libs/gtkutil/filechooser.h b/tools/urt/libs/gtkutil/filechooser.h new file mode 100644 index 00000000..8503cfdd --- /dev/null +++ b/tools/urt/libs/gtkutil/filechooser.h @@ -0,0 +1,22 @@ + +#if !defined(INCLUDED_GTKUTIL_FILECHOOSER_H) +#define INCLUDED_GTKUTIL_FILECHOOSER_H + +/// \file +/// GTK+ file-chooser dialogs. + +#ifdef WIN32 +extern bool g_FileChooser_nativeGUI; +#endif + +typedef struct _GtkWidget GtkWidget; +const char* file_dialog(GtkWidget *parent, bool open, const char* title, const char* path = 0, const char* pattern = 0); + + +/// \brief Prompts the user to browse for a directory. +/// The prompt window will be transient to \p parent. +/// The directory will initially default to \p path, which must be an absolute path. +/// The returned string is allocated with \c g_malloc and must be freed with \c g_free. +char* dir_dialog(GtkWidget *parent, const char* title = "Choose Directory", const char* path = ""); + +#endif diff --git a/tools/urt/libs/gtkutil/frame.cpp b/tools/urt/libs/gtkutil/frame.cpp new file mode 100644 index 00000000..7e298177 --- /dev/null +++ b/tools/urt/libs/gtkutil/frame.cpp @@ -0,0 +1,15 @@ + +#include "frame.h" + +#include + +GtkFrame* create_framed_widget(GtkWidget* widget) +{ + GtkFrame* frame = GTK_FRAME(gtk_frame_new(0)); + gtk_widget_show(GTK_WIDGET(frame)); + gtk_frame_set_shadow_type(frame, GTK_SHADOW_IN); + gtk_container_add (GTK_CONTAINER(frame), widget); + gtk_widget_show(GTK_WIDGET(widget)); + return frame; +} + diff --git a/tools/urt/libs/gtkutil/frame.h b/tools/urt/libs/gtkutil/frame.h new file mode 100644 index 00000000..4aa2eb8b --- /dev/null +++ b/tools/urt/libs/gtkutil/frame.h @@ -0,0 +1,9 @@ + +#if !defined(INCLUDED_GTKUTIL_FRAME_H) +#define INCLUDED_GTKUTIL_FRAME_H + +typedef struct _GtkWidget GtkWidget; +typedef struct _GtkFrame GtkFrame; +GtkFrame* create_framed_widget(GtkWidget* widget); + +#endif diff --git a/tools/urt/libs/gtkutil/glfont.cpp b/tools/urt/libs/gtkutil/glfont.cpp new file mode 100644 index 00000000..f7d7346d --- /dev/null +++ b/tools/urt/libs/gtkutil/glfont.cpp @@ -0,0 +1,36 @@ + +#include "glfont.h" + +#include "igl.h" +#include + +GLFont glfont_create(const char* font_string) +{ + GLuint font_list_base = glGenLists (256); + gint font_height = 0; + + PangoFontDescription* font_desc = pango_font_description_from_string (font_string); + + PangoFont* font = gdk_gl_font_use_pango_font (font_desc, 0, 256, font_list_base); + + if(font != 0) + { + PangoFontMetrics* font_metrics = pango_font_get_metrics (font, 0); + + font_height = pango_font_metrics_get_ascent (font_metrics) + + pango_font_metrics_get_descent (font_metrics); + font_height = PANGO_PIXELS (font_height); + + pango_font_metrics_unref (font_metrics); + } + + pango_font_description_free (font_desc); + + return GLFont(font_list_base, font_height); +} + +void glfont_release(GLFont& font) +{ + glDeleteLists(font.getDisplayList(), 256); + font = GLFont(0, 0); +} diff --git a/tools/urt/libs/gtkutil/glfont.h b/tools/urt/libs/gtkutil/glfont.h new file mode 100644 index 00000000..133bf829 --- /dev/null +++ b/tools/urt/libs/gtkutil/glfont.h @@ -0,0 +1,28 @@ + +#if !defined(INCLUDED_GTKUTIL_GLFONT_H) +#define INCLUDED_GTKUTIL_GLFONT_H + +typedef unsigned int GLuint; + +class GLFont +{ + GLuint m_displayList; + int m_pixelHeight; +public: + GLFont(GLuint displayList, int pixelHeight) : m_displayList(displayList), m_pixelHeight(pixelHeight) + { + } + GLuint getDisplayList() const + { + return m_displayList; + } + int getPixelHeight() const + { + return m_pixelHeight; + } +}; + +GLFont glfont_create(const char* font_string); +void glfont_release(GLFont& font); + +#endif diff --git a/tools/urt/libs/gtkutil/glwidget.cpp b/tools/urt/libs/gtkutil/glwidget.cpp new file mode 100644 index 00000000..b03c480e --- /dev/null +++ b/tools/urt/libs/gtkutil/glwidget.cpp @@ -0,0 +1,254 @@ + +// OpenGL widget based on GtkGLExt + +#include "glwidget.h" + +#include "debugging/debugging.h" + +#include "igl.h" + +#include +#include + +#include "pointer.h" + +void (*GLWidget_sharedContextCreated)() = 0; +void (*GLWidget_sharedContextDestroyed)() = 0; + + +typedef int* attribs_t; +struct config_t +{ + const char* name; + attribs_t attribs; +}; +typedef const config_t* configs_iterator; + +int config_rgba32[] = { + GDK_GL_RGBA, + GDK_GL_DOUBLEBUFFER, + GDK_GL_BUFFER_SIZE, 24, + GDK_GL_ATTRIB_LIST_NONE, +}; + +int config_rgba[] = { + GDK_GL_RGBA, + GDK_GL_DOUBLEBUFFER, + GDK_GL_BUFFER_SIZE, 16, + GDK_GL_ATTRIB_LIST_NONE, +}; + +const config_t configs[] = { + { + "colour-buffer = 32bpp, depth-buffer = none", + config_rgba32, + }, + { + "colour-buffer = 16bpp, depth-buffer = none", + config_rgba, + } +}; + +GdkGLConfig* glconfig_new() +{ + GdkGLConfig* glconfig = 0; + + for(configs_iterator i = configs, end = configs + 2; i != end; ++i) + { + glconfig = gdk_gl_config_new((*i).attribs); + if(glconfig != 0) + { + globalOutputStream() << "OpenGL window configuration: " << (*i).name << "\n"; + return glconfig; + } + } + + globalOutputStream() << "OpenGL window configuration: colour-buffer = auto, depth-buffer = none\n"; + return gdk_gl_config_new_by_mode((GdkGLConfigMode)(GDK_GL_MODE_RGBA | GDK_GL_MODE_DOUBLE)); +} + +int config_rgba32_depth32[] = { + GDK_GL_RGBA, + GDK_GL_DOUBLEBUFFER, + GDK_GL_BUFFER_SIZE, 24, + GDK_GL_DEPTH_SIZE, 32, + GDK_GL_ATTRIB_LIST_NONE, +}; + +int config_rgba32_depth24[] = { + GDK_GL_RGBA, + GDK_GL_DOUBLEBUFFER, + GDK_GL_BUFFER_SIZE, 24, + GDK_GL_DEPTH_SIZE, 24, + GDK_GL_ATTRIB_LIST_NONE, +}; + +int config_rgba32_depth16[] = { + GDK_GL_RGBA, + GDK_GL_DOUBLEBUFFER, + GDK_GL_BUFFER_SIZE, 24, + GDK_GL_DEPTH_SIZE, 16, + GDK_GL_ATTRIB_LIST_NONE, +}; + +int config_rgba32_depth[] = { + GDK_GL_RGBA, + GDK_GL_DOUBLEBUFFER, + GDK_GL_BUFFER_SIZE, 24, + GDK_GL_DEPTH_SIZE, 1, + GDK_GL_ATTRIB_LIST_NONE, +}; + +int config_rgba_depth16[] = { + GDK_GL_RGBA, + GDK_GL_DOUBLEBUFFER, + GDK_GL_BUFFER_SIZE, 16, + GDK_GL_DEPTH_SIZE, 16, + GDK_GL_ATTRIB_LIST_NONE, +}; + +int config_rgba_depth[] = { + GDK_GL_RGBA, + GDK_GL_DOUBLEBUFFER, + GDK_GL_BUFFER_SIZE, 16, + GDK_GL_DEPTH_SIZE, 1, + GDK_GL_ATTRIB_LIST_NONE, +}; + +const config_t configs_with_depth[] = +{ + { + "colour-buffer = 32bpp, depth-buffer = 32bpp", + config_rgba32_depth32, + }, + { + "colour-buffer = 32bpp, depth-buffer = 24bpp", + config_rgba32_depth24, + }, + { + "colour-buffer = 32bpp, depth-buffer = 16bpp", + config_rgba32_depth16, + }, + { + "colour-buffer = 32bpp, depth-buffer = auto", + config_rgba32_depth, + }, + { + "colour-buffer = 16bpp, depth-buffer = 16bpp", + config_rgba_depth16, + }, + { + "colour-buffer = auto, depth-buffer = auto", + config_rgba_depth, + }, +}; + +GdkGLConfig* glconfig_new_with_depth() +{ + GdkGLConfig* glconfig = 0; + + for(configs_iterator i = configs_with_depth, end = configs_with_depth + 6; i != end; ++i) + { + glconfig = gdk_gl_config_new((*i).attribs); + if(glconfig != 0) + { + globalOutputStream() << "OpenGL window configuration: " << (*i).name << "\n"; + return glconfig; + } + } + + globalOutputStream() << "OpenGL window configuration: colour-buffer = auto, depth-buffer = auto (fallback)\n"; + return gdk_gl_config_new_by_mode((GdkGLConfigMode)(GDK_GL_MODE_RGBA | GDK_GL_MODE_DOUBLE | GDK_GL_MODE_DEPTH)); +} + +unsigned int g_context_count = 0; + +namespace +{ + GtkWidget* g_shared = 0; +} + +gint glwidget_context_created(GtkWidget* widget, gpointer data) +{ + if(++g_context_count == 1) + { + g_shared = widget; + gtk_widget_ref(g_shared); + + glwidget_make_current(g_shared); + GlobalOpenGL().contextValid = true; + + GLWidget_sharedContextCreated(); + } + return FALSE; +} + +gint glwidget_context_destroyed(GtkWidget* widget, gpointer data) +{ + if(--g_context_count == 0) + { + GlobalOpenGL().contextValid = false; + + GLWidget_sharedContextDestroyed(); + + gtk_widget_unref(g_shared); + g_shared = 0; + } + return FALSE; +} + +gboolean glwidget_enable_gl(GtkWidget* widget, GtkWidget* widget2, gpointer data) +{ + if(widget2 == 0 && !gtk_widget_is_gl_capable(widget)) + { + GdkGLConfig* glconfig = (g_object_get_data(G_OBJECT(widget), "zbuffer")) ? glconfig_new_with_depth() : glconfig_new(); + ASSERT_MESSAGE(glconfig != 0, "failed to create OpenGL config"); + + gtk_widget_set_gl_capability(widget, glconfig, g_shared != 0 ? gtk_widget_get_gl_context(g_shared) : 0, TRUE, GDK_GL_RGBA_TYPE); + + gtk_widget_realize(widget); + if(g_shared == 0) + { + g_shared = widget; + } + + // free glconfig? + } + return FALSE; +} + +GtkWidget* glwidget_new(gboolean zbuffer) +{ + GtkWidget* widget = gtk_drawing_area_new(); + + g_object_set_data(G_OBJECT(widget), "zbuffer", gint_to_pointer(zbuffer)); + + g_signal_connect(G_OBJECT(widget), "hierarchy-changed", G_CALLBACK(glwidget_enable_gl), 0); + + g_signal_connect(G_OBJECT(widget), "realize", G_CALLBACK(glwidget_context_created), 0); + g_signal_connect(G_OBJECT(widget), "unrealize", G_CALLBACK(glwidget_context_destroyed), 0); + + return widget; +} + +void glwidget_destroy_context (GtkWidget *widget) +{ +} + +void glwidget_create_context (GtkWidget *widget) +{ +} + +void glwidget_swap_buffers (GtkWidget *widget) +{ + GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable (widget); + gdk_gl_drawable_swap_buffers (gldrawable); +} + +gboolean glwidget_make_current (GtkWidget *widget) +{ + GdkGLContext *glcontext = gtk_widget_get_gl_context (widget); + GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable (widget); + return gdk_gl_drawable_gl_begin (gldrawable, glcontext); +} + diff --git a/tools/urt/libs/gtkutil/glwidget.h b/tools/urt/libs/gtkutil/glwidget.h new file mode 100644 index 00000000..c50eb0f5 --- /dev/null +++ b/tools/urt/libs/gtkutil/glwidget.h @@ -0,0 +1,19 @@ + +#if !defined(INCLUDED_GTKUTIL_GLWIDGET_H) +#define INCLUDED_GTKUTIL_GLWIDGET_H + +typedef struct _GtkWidget GtkWidget; +typedef int gint; +typedef gint gboolean; + +GtkWidget* glwidget_new(gboolean zbuffer); +void glwidget_swap_buffers(GtkWidget* widget); +gboolean glwidget_make_current(GtkWidget* widget); +void glwidget_destroy_context(GtkWidget* widget); +void glwidget_create_context(GtkWidget* widget); + +extern void (*GLWidget_sharedContextCreated)(); +extern void (*GLWidget_sharedContextDestroyed)(); + + +#endif diff --git a/tools/urt/libs/gtkutil/gtkutil.vcproj b/tools/urt/libs/gtkutil/gtkutil.vcproj new file mode 100644 index 00000000..e5f548bf --- /dev/null +++ b/tools/urt/libs/gtkutil/gtkutil.vcproj @@ -0,0 +1,255 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/urt/libs/gtkutil/idledraw.cpp b/tools/urt/libs/gtkutil/idledraw.cpp new file mode 100644 index 00000000..108c7217 --- /dev/null +++ b/tools/urt/libs/gtkutil/idledraw.cpp @@ -0,0 +1,3 @@ + +#include "idledraw.h" + diff --git a/tools/urt/libs/gtkutil/idledraw.h b/tools/urt/libs/gtkutil/idledraw.h new file mode 100644 index 00000000..89492b6a --- /dev/null +++ b/tools/urt/libs/gtkutil/idledraw.h @@ -0,0 +1,49 @@ + +#if !defined(INCLUDED_GTKUTIL_IDLEDRAW_H) +#define INCLUDED_GTKUTIL_IDLEDRAW_H + +#include + +#include "generic/callback.h" + +class IdleDraw +{ + Callback m_draw; + unsigned int m_handler; + static gboolean draw(gpointer data) + { + reinterpret_cast(data)->m_draw(); + reinterpret_cast(data)->m_handler = 0; + return FALSE; + } +public: + IdleDraw(const Callback& draw) : m_draw(draw), m_handler(0) + { + } + ~IdleDraw() + { + if(m_handler != 0) + { + g_source_remove(m_handler); + } + } + void queueDraw() + { + if(m_handler == 0) + { + m_handler = g_idle_add(&draw, this); + } + } + typedef MemberCaller QueueDrawCaller; + + void flush() + { + if(m_handler != 0) + { + draw(this); + } + } +}; + + +#endif diff --git a/tools/urt/libs/gtkutil/image.cpp b/tools/urt/libs/gtkutil/image.cpp new file mode 100644 index 00000000..fb1da567 --- /dev/null +++ b/tools/urt/libs/gtkutil/image.cpp @@ -0,0 +1,76 @@ + +#include "image.h" + +#include +#include + +#include "string/string.h" +#include "stream/stringstream.h" +#include "stream/textstream.h" + + +namespace +{ + CopiedString g_bitmapsPath; +} + +void BitmapsPath_set(const char* path) +{ + g_bitmapsPath = path; +} + +GdkPixbuf* pixbuf_new_from_file_with_mask(const char* filename) +{ + GdkPixbuf* rgb = gdk_pixbuf_new_from_file(filename, 0); + if(rgb == 0) + { + return 0; + } + else + { + GdkPixbuf* rgba = gdk_pixbuf_add_alpha(rgb, TRUE, 255, 0, 255); + gdk_pixbuf_unref(rgb); + return rgba; + } +} + +GtkImage* image_new_from_file_with_mask(const char* filename) +{ + GdkPixbuf* rgba = pixbuf_new_from_file_with_mask(filename); + if(rgba == 0) + { + return 0; + } + else + { + GtkImage* image = GTK_IMAGE(gtk_image_new_from_pixbuf(rgba)); + gdk_pixbuf_unref(rgba); + return image; + } +} + +GtkImage* image_new_missing() +{ + return GTK_IMAGE(gtk_image_new_from_stock(GTK_STOCK_MISSING_IMAGE, GTK_ICON_SIZE_SMALL_TOOLBAR)); +} + +GtkImage* new_image(const char* filename) +{ + { + GtkImage* image = image_new_from_file_with_mask(filename); + if(image != 0) + { + return image; + } + } + + return image_new_missing(); +} + +GtkImage* new_local_image(const char* filename) +{ + StringOutputStream fullPath(256); + fullPath << g_bitmapsPath.c_str() << filename; + return new_image(fullPath.c_str()); +} + diff --git a/tools/urt/libs/gtkutil/image.h b/tools/urt/libs/gtkutil/image.h new file mode 100644 index 00000000..448d0298 --- /dev/null +++ b/tools/urt/libs/gtkutil/image.h @@ -0,0 +1,16 @@ + +#if !defined(INCLUDED_GTKUTIL_IMAGE_H) +#define INCLUDED_GTKUTIL_IMAGE_H + +void BitmapsPath_set(const char* path); + +typedef struct _GtkImage GtkImage; +typedef struct _GdkPixbuf GdkPixbuf; + +GdkPixbuf* pixbuf_new_from_file_with_mask(const char* filename); +GtkImage* image_new_from_file_with_mask(const char* filename); +GtkImage* image_new_missing(); +GtkImage* new_image(const char* filename); // filename is full path to image file +GtkImage* new_local_image(const char* filename); // filename is relative to local bitmaps path + +#endif diff --git a/tools/urt/libs/gtkutil/menu.cpp b/tools/urt/libs/gtkutil/menu.cpp new file mode 100644 index 00000000..374b23ae --- /dev/null +++ b/tools/urt/libs/gtkutil/menu.cpp @@ -0,0 +1,293 @@ + +#include "menu.h" + +#include +#include +#include +#include +#include +#include + +#include "generic/callback.h" + +#include "accelerator.h" +#include "closure.h" +#include "container.h" +#include "pointer.h" + +void menu_add_item(GtkMenu* menu, GtkMenuItem* item) +{ + gtk_container_add(GTK_CONTAINER(menu), GTK_WIDGET(item)); +} + +GtkMenuItem* menu_separator(GtkMenu* menu) +{ + GtkMenuItem* menu_item = GTK_MENU_ITEM(gtk_menu_item_new()); + container_add_widget(GTK_CONTAINER(menu), GTK_WIDGET(menu_item)); + gtk_widget_set_sensitive(GTK_WIDGET(menu_item), FALSE); + gtk_widget_show(GTK_WIDGET(menu_item)); + return menu_item; +} + +GtkTearoffMenuItem* menu_tearoff(GtkMenu* menu) +{ + GtkTearoffMenuItem* menu_item = GTK_TEAROFF_MENU_ITEM(gtk_tearoff_menu_item_new()); + container_add_widget(GTK_CONTAINER(menu), GTK_WIDGET(menu_item)); +// gtk_widget_set_sensitive(GTK_WIDGET(menu_item), FALSE); -- controls whether menu is detachable + gtk_widget_show(GTK_WIDGET(menu_item)); + return menu_item; +} + +GtkMenuItem* new_sub_menu_item_with_mnemonic(const char* mnemonic) +{ + GtkMenuItem* item = GTK_MENU_ITEM(gtk_menu_item_new_with_mnemonic(mnemonic)); + gtk_widget_show(GTK_WIDGET(item)); + + GtkWidget* sub_menu = gtk_menu_new(); + gtk_menu_item_set_submenu(item, sub_menu); + + return item; +} + +GtkMenu* create_sub_menu_with_mnemonic(GtkMenuShell* parent, const char* mnemonic) +{ + GtkMenuItem* item = new_sub_menu_item_with_mnemonic(mnemonic); + container_add_widget(GTK_CONTAINER(parent), GTK_WIDGET(item)); + return GTK_MENU(gtk_menu_item_get_submenu(item)); +} + +GtkMenu* create_sub_menu_with_mnemonic(GtkMenuBar* bar, const char* mnemonic) +{ + return create_sub_menu_with_mnemonic(GTK_MENU_SHELL(bar), mnemonic); +} + +GtkMenu* create_sub_menu_with_mnemonic(GtkMenu* parent, const char* mnemonic) +{ + return create_sub_menu_with_mnemonic(GTK_MENU_SHELL(parent), mnemonic); +} + +void activate_closure_callback(GtkWidget* widget, gpointer data) +{ + (*reinterpret_cast(data))(); +} + +guint menu_item_connect_callback(GtkMenuItem* item, const Callback& callback) +{ +#if 1 + return g_signal_connect_swapped(G_OBJECT(item), "activate", G_CALLBACK(callback.getThunk()), callback.getEnvironment()); +#else + return g_signal_connect_closure(G_OBJECT(item), "activate", create_cclosure(G_CALLBACK(activate_closure_callback), callback), FALSE); +#endif +} + +guint check_menu_item_connect_callback(GtkCheckMenuItem* item, const Callback& callback) +{ +#if 1 + guint handler = g_signal_connect_swapped(G_OBJECT(item), "toggled", G_CALLBACK(callback.getThunk()), callback.getEnvironment()); +#else + guint handler = g_signal_connect_closure(G_OBJECT(item), "toggled", create_cclosure(G_CALLBACK(activate_closure_callback), callback), TRUE); +#endif + g_object_set_data(G_OBJECT(item), "handler", gint_to_pointer(handler)); + return handler; +} + +GtkMenuItem* new_menu_item_with_mnemonic(const char *mnemonic, const Callback& callback) +{ + GtkMenuItem* item = GTK_MENU_ITEM(gtk_menu_item_new_with_mnemonic(mnemonic)); + gtk_widget_show(GTK_WIDGET(item)); + menu_item_connect_callback(item, callback); + return item; +} + +GtkMenuItem* create_menu_item_with_mnemonic(GtkMenu* menu, const char *mnemonic, const Callback& callback) +{ + GtkMenuItem* item = new_menu_item_with_mnemonic(mnemonic, callback); + container_add_widget(GTK_CONTAINER(menu), GTK_WIDGET(item)); + return item; +} + +GtkCheckMenuItem* new_check_menu_item_with_mnemonic(const char* mnemonic, const Callback& callback) +{ + GtkCheckMenuItem* item = GTK_CHECK_MENU_ITEM(gtk_check_menu_item_new_with_mnemonic(mnemonic)); + gtk_widget_show(GTK_WIDGET(item)); + check_menu_item_connect_callback(item, callback); + return item; +} + +GtkCheckMenuItem* create_check_menu_item_with_mnemonic(GtkMenu* menu, const char* mnemonic, const Callback& callback) +{ + GtkCheckMenuItem* item = new_check_menu_item_with_mnemonic(mnemonic, callback); + container_add_widget(GTK_CONTAINER(menu), GTK_WIDGET(item)); + return item; +} + +GtkRadioMenuItem* new_radio_menu_item_with_mnemonic(GSList** group, const char* mnemonic, const Callback& callback) +{ + GtkRadioMenuItem* item = GTK_RADIO_MENU_ITEM(gtk_radio_menu_item_new_with_mnemonic(*group, mnemonic)); + if(*group == 0) + { + gtk_check_menu_item_set_state(GTK_CHECK_MENU_ITEM(item), TRUE); + } + *group = gtk_radio_menu_item_group(item); + gtk_widget_show(GTK_WIDGET(item)); + check_menu_item_connect_callback(GTK_CHECK_MENU_ITEM(item), callback); + return item; +} + +GtkRadioMenuItem* create_radio_menu_item_with_mnemonic(GtkMenu* menu, GSList** group, const char* mnemonic, const Callback& callback) +{ + GtkRadioMenuItem* item = new_radio_menu_item_with_mnemonic(group, mnemonic, callback); + container_add_widget(GTK_CONTAINER(menu), GTK_WIDGET(item)); + return item; +} + +void check_menu_item_set_active_no_signal(GtkCheckMenuItem* item, gboolean active) +{ + guint handler_id = gpointer_to_int(g_object_get_data(G_OBJECT(item), "handler")); + g_signal_handler_block(G_OBJECT(item), handler_id); + gtk_check_menu_item_set_active(item, active); + g_signal_handler_unblock(G_OBJECT(item), handler_id); +} + + + +void radio_menu_item_set_active_no_signal(GtkRadioMenuItem* item, gboolean active) +{ + { + for(GSList* l = gtk_radio_menu_item_get_group(item); l != 0; l = g_slist_next(l)) + { + g_signal_handler_block(G_OBJECT(l->data), gpointer_to_int(g_object_get_data(G_OBJECT(l->data), "handler"))); + } + } + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), active); + { + for(GSList* l = gtk_radio_menu_item_get_group(item); l != 0; l = g_slist_next(l)) + { + g_signal_handler_unblock(G_OBJECT(l->data), gpointer_to_int(g_object_get_data(G_OBJECT(l->data), "handler"))); + } + } +} + + +void menu_item_set_accelerator(GtkMenuItem* item, GClosure* closure) +{ + GtkAccelLabel* accel_label = GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(item))); + gtk_accel_label_set_accel_closure(accel_label, closure); +} + +void accelerator_name(const Accelerator& accelerator, GString* gstring) +{ + gboolean had_mod = FALSE; + if (accelerator.modifiers & GDK_SHIFT_MASK) + { + g_string_append (gstring, "Shift"); + had_mod = TRUE; + } + if (accelerator.modifiers & GDK_CONTROL_MASK) + { + if (had_mod) + g_string_append (gstring, "+"); + g_string_append (gstring, "Ctrl"); + had_mod = TRUE; + } + if (accelerator.modifiers & GDK_MOD1_MASK) + { + if (had_mod) + g_string_append (gstring, "+"); + g_string_append (gstring, "Alt"); + had_mod = TRUE; + } + + if (had_mod) + g_string_append (gstring, "+"); + if (accelerator.key < 0x80 || (accelerator.key > 0x80 && accelerator.key <= 0xff)) + { + switch (accelerator.key) + { + case ' ': + g_string_append (gstring, "Space"); + break; + case '\\': + g_string_append (gstring, "Backslash"); + break; + default: + g_string_append_c (gstring, gchar(toupper(accelerator.key))); + break; + } + } + else + { + gchar *tmp; + + tmp = gtk_accelerator_name (accelerator.key, (GdkModifierType)0); + if (tmp[0] != 0 && tmp[1] == 0) + tmp[0] = gchar(toupper(tmp[0])); + g_string_append (gstring, tmp); + g_free (tmp); + } +} + +void menu_item_set_accelerator(GtkMenuItem* item, Accelerator accelerator) +{ + GtkAccelLabel* accel_label = GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(item))); + + g_free (accel_label->accel_string); + accel_label->accel_string = 0; + + GString* gstring = g_string_new (accel_label->accel_string); + g_string_append (gstring, " "); + + accelerator_name(accelerator, gstring); + + g_free (accel_label->accel_string); + accel_label->accel_string = gstring->str; + g_string_free (gstring, FALSE); + + if (!accel_label->accel_string) + accel_label->accel_string = g_strdup (""); + + gtk_widget_queue_resize (GTK_WIDGET (accel_label)); +} + +void menu_item_add_accelerator(GtkMenuItem* item, Accelerator accelerator) +{ + if(accelerator.key != 0) + { + GClosure* closure = global_accel_group_find(accelerator); + if(closure != 0) + { + menu_item_set_accelerator(item, closure); + } + else + { + menu_item_set_accelerator(item, accelerator); + } + } +} + +GtkMenuItem* create_menu_item_with_mnemonic(GtkMenu* menu, const char* mnemonic, const Command& command) +{ + command_connect_accelerator(command.m_accelerator, command.m_callback); + GtkMenuItem* item = create_menu_item_with_mnemonic(menu, mnemonic, command.m_callback); + menu_item_add_accelerator(item, command.m_accelerator); + return item; +} + +void check_menu_item_set_active_callback(GtkCheckMenuItem& item, bool enabled) +{ + check_menu_item_set_active_no_signal(&item, enabled); +} +typedef ReferenceCaller1 CheckMenuItemSetActiveCaller; + +GtkCheckMenuItem* create_check_menu_item_with_mnemonic(GtkMenu* menu, const char* mnemonic, const Toggle& toggle) +{ + command_connect_accelerator(toggle.m_command.m_accelerator, toggle.m_command.m_callback); + GtkCheckMenuItem* item = create_check_menu_item_with_mnemonic(menu, mnemonic, toggle.m_command.m_callback); + menu_item_add_accelerator(GTK_MENU_ITEM(item), toggle.m_command.m_accelerator); + toggle.m_exportCallback(CheckMenuItemSetActiveCaller(*item)); + return item; +} + + + + diff --git a/tools/urt/libs/gtkutil/menu.h b/tools/urt/libs/gtkutil/menu.h new file mode 100644 index 00000000..355dbdb3 --- /dev/null +++ b/tools/urt/libs/gtkutil/menu.h @@ -0,0 +1,37 @@ + +#if !defined(INCLUDED_GTKUTIL_MENU_H) +#define INCLUDED_GTKUTIL_MENU_H + +class Callback; +typedef int gint; +typedef gint gboolean; +typedef struct _GSList GSList; +typedef struct _GtkMenu GtkMenu; +typedef struct _GtkMenuBar GtkMenuBar; +typedef struct _GtkMenuItem GtkMenuItem; +typedef struct _GtkCheckMenuItem GtkCheckMenuItem; +typedef struct _GtkRadioMenuItem GtkRadioMenuItem; +typedef struct _GtkTearoffMenuItem GtkTearoffMenuItem; + +void menu_add_item(GtkMenu* menu, GtkMenuItem* item); +GtkMenuItem* menu_separator(GtkMenu* menu); +GtkTearoffMenuItem* menu_tearoff(GtkMenu* menu); +GtkMenuItem* new_sub_menu_item_with_mnemonic(const char* mnemonic); +GtkMenu* create_sub_menu_with_mnemonic(GtkMenuBar* bar, const char* mnemonic); +GtkMenu* create_sub_menu_with_mnemonic(GtkMenu* parent, const char* mnemonic); +GtkMenuItem* create_menu_item_with_mnemonic(GtkMenu* menu, const char* mnemonic, const Callback& callback); +GtkCheckMenuItem* create_check_menu_item_with_mnemonic(GtkMenu* menu, const char* mnemonic, const Callback& callback); +GtkRadioMenuItem* create_radio_menu_item_with_mnemonic(GtkMenu* menu, GSList** group, const char* mnemonic, const Callback& callback); + +class Command; +GtkMenuItem* create_menu_item_with_mnemonic(GtkMenu* menu, const char* mnemonic, const Command& command); +class Toggle; +GtkCheckMenuItem* create_check_menu_item_with_mnemonic(GtkMenu* menu, const char* mnemonic, const Toggle& toggle); + + +typedef struct _GtkCheckMenuItem GtkCheckMenuItem; +void check_menu_item_set_active_no_signal(GtkCheckMenuItem* item, gboolean active); +typedef struct _GtkRadioMenuItem GtkRadioMenuItem; +void radio_menu_item_set_active_no_signal(GtkRadioMenuItem* item, gboolean active); + +#endif diff --git a/tools/urt/libs/gtkutil/messagebox.cpp b/tools/urt/libs/gtkutil/messagebox.cpp new file mode 100644 index 00000000..dd7e0e4e --- /dev/null +++ b/tools/urt/libs/gtkutil/messagebox.cpp @@ -0,0 +1,193 @@ + +#include "messagebox.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dialog.h" +#include "widget.h" + +GtkWidget* create_padding(int width, int height) +{ + GtkWidget* widget = gtk_alignment_new(0.0, 0.0, 0.0, 0.0); + gtk_widget_show(widget); + gtk_widget_set_size_request(widget, width, height); + return widget; +} + +const char* messagebox_stock_icon(EMessageBoxIcon type) +{ + switch(type) + { + default: + case eMB_ICONDEFAULT: + return GTK_STOCK_DIALOG_INFO; + case eMB_ICONERROR: + return GTK_STOCK_DIALOG_ERROR; + case eMB_ICONWARNING: + return GTK_STOCK_DIALOG_WARNING; + case eMB_ICONQUESTION: + return GTK_STOCK_DIALOG_QUESTION; + case eMB_ICONASTERISK: + return GTK_STOCK_DIALOG_INFO; + } +} + +EMessageBoxReturn gtk_MessageBox (GtkWidget *parent, const char* text, const char* title, EMessageBoxType type, EMessageBoxIcon icon) +{ + ModalDialog dialog; + ModalDialogButton ok_button(dialog, eIDOK); + ModalDialogButton cancel_button(dialog, eIDCANCEL); + ModalDialogButton yes_button(dialog, eIDYES); + ModalDialogButton no_button(dialog, eIDNO); + + GtkWindow* parentWindow = parent != 0 ? GTK_WINDOW(parent) : 0; + + GtkWindow* window = create_fixedsize_modal_dialog_window(parentWindow, title, dialog, 400, 100); + + if(parentWindow != 0) + { + //g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(floating_window_delete_present), parent); + gtk_window_deiconify(parentWindow); + } + + GtkAccelGroup* accel = gtk_accel_group_new(); + gtk_window_add_accel_group(window, accel); + + GtkVBox* vbox = create_dialog_vbox(8, 8); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(vbox)); + + + GtkHBox* hboxDummy = create_dialog_hbox(0, 0); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(hboxDummy), FALSE, FALSE, 0); + + gtk_box_pack_start(GTK_BOX(hboxDummy), create_padding(0, 50), FALSE, FALSE, 0); // HACK to force minimum height + + GtkHBox* iconBox = create_dialog_hbox(16, 0); + gtk_box_pack_start(GTK_BOX(hboxDummy), GTK_WIDGET(iconBox), FALSE, FALSE, 0); + + GtkImage* image = GTK_IMAGE(gtk_image_new_from_stock(messagebox_stock_icon(icon), GTK_ICON_SIZE_DIALOG)); + gtk_widget_show(GTK_WIDGET(image)); + gtk_box_pack_start(GTK_BOX(iconBox), GTK_WIDGET(image), FALSE, FALSE, 0); + + GtkLabel* label = GTK_LABEL(gtk_label_new(text)); + gtk_widget_show(GTK_WIDGET(label)); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + gtk_label_set_justify(label, GTK_JUSTIFY_LEFT); + gtk_label_set_line_wrap(label, TRUE); + gtk_box_pack_start(GTK_BOX(iconBox), GTK_WIDGET(label), TRUE, TRUE, 0); + + + GtkVBox* vboxDummy = create_dialog_vbox(0, 0); + gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(vboxDummy), FALSE, FALSE, 0); + + GtkAlignment* alignment = GTK_ALIGNMENT(gtk_alignment_new(0.5, 0.0, 0.0, 0.0)); + gtk_widget_show(GTK_WIDGET(alignment)); + gtk_box_pack_start(GTK_BOX(vboxDummy), GTK_WIDGET(alignment), FALSE, FALSE, 0); + + GtkHBox* hbox = create_dialog_hbox(8, 0); + gtk_container_add(GTK_CONTAINER(alignment), GTK_WIDGET(hbox)); + + gtk_box_pack_start(GTK_BOX(vboxDummy), create_padding(400, 0), FALSE, FALSE, 0); // HACK to force minimum width + + + if (type == eMB_OK) + { + GtkButton* button = create_modal_dialog_button("OK", ok_button); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(button), TRUE, FALSE, 0); + gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0); + gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0); + widget_make_default(GTK_WIDGET(button)); + gtk_widget_show(GTK_WIDGET(button)); + + dialog.ret = eIDOK; + } + else if (type == eMB_OKCANCEL) + { + { + GtkButton* button = create_modal_dialog_button("OK", ok_button); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(button), TRUE, FALSE, 0); + gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel, GDK_Return, (GdkModifierType)0, (GtkAccelFlags)0); + widget_make_default(GTK_WIDGET(button)); + gtk_widget_show(GTK_WIDGET(button)); + } + + { + GtkButton* button = create_modal_dialog_button("OK", cancel_button); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(button), TRUE, FALSE, 0); + gtk_widget_add_accelerator(GTK_WIDGET(button), "clicked", accel, GDK_Escape, (GdkModifierType)0, (GtkAccelFlags)0); + gtk_widget_show(GTK_WIDGET(button)); + } + + dialog.ret = eIDCANCEL; + } + else if (type == eMB_YESNOCANCEL) + { + { + GtkButton* button = create_modal_dialog_button("Yes", yes_button); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(button), TRUE, FALSE, 0); + widget_make_default(GTK_WIDGET(button)); + gtk_widget_show(GTK_WIDGET(button)); + } + + { + GtkButton* button = create_modal_dialog_button("No", no_button); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(button), TRUE, FALSE, 0); + gtk_widget_show(GTK_WIDGET(button)); + } + { + GtkButton* button = create_modal_dialog_button("Cancel", cancel_button); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(button), TRUE, FALSE, 0); + gtk_widget_show(GTK_WIDGET(button)); + } + + dialog.ret = eIDCANCEL; + } + else if (type == eMB_NOYES) + { + { + GtkButton* button = create_modal_dialog_button("No", no_button); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(button), TRUE, FALSE, 0); + widget_make_default(GTK_WIDGET(button)); + gtk_widget_show(GTK_WIDGET(button)); + } + { + GtkButton* button = create_modal_dialog_button("Yes", yes_button); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(button), TRUE, FALSE, 0); + gtk_widget_show(GTK_WIDGET(button)); + } + + dialog.ret = eIDNO; + } + else /* if (type == eMB_YESNO) */ + { + { + GtkButton* button = create_modal_dialog_button("Yes", yes_button); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(button), TRUE, FALSE, 0); + widget_make_default(GTK_WIDGET(button)); + gtk_widget_show(GTK_WIDGET(button)); + } + + { + GtkButton* button = create_modal_dialog_button("No", no_button); + gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(button), TRUE, FALSE, 0); + gtk_widget_show(GTK_WIDGET(button)); + } + dialog.ret = eIDNO; + } + + modal_dialog_show(window, dialog); + + gtk_widget_destroy(GTK_WIDGET(window)); + + return dialog.ret; +} + diff --git a/tools/urt/libs/gtkutil/messagebox.h b/tools/urt/libs/gtkutil/messagebox.h new file mode 100644 index 00000000..5edcbfde --- /dev/null +++ b/tools/urt/libs/gtkutil/messagebox.h @@ -0,0 +1,11 @@ + +#if !defined(INCLUDED_GTKUTIL_MESSAGEBOX_H) +#define INCLUDED_GTKUTIL_MESSAGEBOX_H + +#include "qerplugin.h" + +typedef struct _GtkWidget GtkWidget; +/// \brief Shows a modal message-box. +EMessageBoxReturn gtk_MessageBox(GtkWidget *parent, const char* text, const char* title = "GtkRadiant", EMessageBoxType type = eMB_OK, EMessageBoxIcon icon = eMB_ICONDEFAULT); + +#endif diff --git a/tools/urt/libs/gtkutil/nonmodal.cpp b/tools/urt/libs/gtkutil/nonmodal.cpp new file mode 100644 index 00000000..ec51bfea --- /dev/null +++ b/tools/urt/libs/gtkutil/nonmodal.cpp @@ -0,0 +1,3 @@ + +#include "nonmodal.h" + diff --git a/tools/urt/libs/gtkutil/nonmodal.h b/tools/urt/libs/gtkutil/nonmodal.h new file mode 100644 index 00000000..eba98e70 --- /dev/null +++ b/tools/urt/libs/gtkutil/nonmodal.h @@ -0,0 +1,166 @@ + +#if !defined(INCLUDED_GTKUTIL_NONMODAL_H) +#define INCLUDED_GTKUTIL_NONMODAL_H + +#include +#include +#include +#include + +#include "generic/callback.h" + +#include "pointer.h" +#include "button.h" + +typedef struct _GtkEntry GtkEntry; + + +inline gboolean escape_clear_focus_widget(GtkWidget* widget, GdkEventKey* event, gpointer data) +{ + if(event->keyval == GDK_Escape) + { + gtk_window_set_focus(GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(widget))), NULL); + return TRUE; + } + return FALSE; +} + +inline void widget_connect_escape_clear_focus_widget(GtkWidget* widget) +{ + g_signal_connect(G_OBJECT(widget), "key_press_event", G_CALLBACK(escape_clear_focus_widget), 0); +} + + +class NonModalEntry +{ + bool m_editing; + Callback m_apply; + Callback m_cancel; + + static gboolean focus_in(GtkEntry* entry, GdkEventFocus *event, NonModalEntry* self) + { + self->m_editing = false; + return FALSE; + } + + static gboolean focus_out(GtkEntry* entry, GdkEventFocus *event, NonModalEntry* self) + { + if(self->m_editing && GTK_WIDGET_VISIBLE(entry)) + { + self->m_apply(); + } + self->m_editing = false; + return FALSE; + } + + static gboolean changed(GtkEntry* entry, NonModalEntry* self) + { + self->m_editing = true; + return FALSE; + } + + static gboolean enter(GtkEntry* entry, GdkEventKey* event, NonModalEntry* self) + { + if(event->keyval == GDK_Return) + { + self->m_apply(); + self->m_editing = false; + gtk_window_set_focus(GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(entry))), NULL); + return TRUE; + } + return FALSE; + } + + static gboolean escape(GtkEntry* entry, GdkEventKey* event, NonModalEntry* self) + { + if(event->keyval == GDK_Escape) + { + self->m_cancel(); + self->m_editing = false; + gtk_window_set_focus(GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(entry))), NULL); + return TRUE; + } + return FALSE; + } + +public: + NonModalEntry(const Callback& apply, const Callback& cancel) : m_editing(false), m_apply(apply), m_cancel(cancel) + { + } + void connect(GtkEntry* entry) + { + g_signal_connect(G_OBJECT(entry), "focus_in_event", G_CALLBACK(focus_in), this); + g_signal_connect(G_OBJECT(entry), "focus_out_event", G_CALLBACK(focus_out), this); + g_signal_connect(G_OBJECT(entry), "key_press_event", G_CALLBACK(enter), this); + g_signal_connect(G_OBJECT(entry), "key_press_event", G_CALLBACK(escape), this); + g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(changed), this); + } +}; + + +class NonModalSpinner +{ + Callback m_apply; + Callback m_cancel; + + static gboolean changed(GtkSpinButton* spin, NonModalSpinner* self) + { + self->m_apply(); + return FALSE; + } + + static gboolean enter(GtkSpinButton* spin, GdkEventKey* event, NonModalSpinner* self) + { + if(event->keyval == GDK_Return) + { + gtk_window_set_focus(GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(spin))), NULL); + return TRUE; + } + return FALSE; + } + + static gboolean escape(GtkSpinButton* spin, GdkEventKey* event, NonModalSpinner* self) + { + if(event->keyval == GDK_Escape) + { + self->m_cancel(); + gtk_window_set_focus(GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(spin))), NULL); + return TRUE; + } + return FALSE; + } + +public: + NonModalSpinner(const Callback& apply, const Callback& cancel) : m_apply(apply), m_cancel(cancel) + { + } + void connect(GtkSpinButton* spin) + { + guint handler = g_signal_connect(G_OBJECT(gtk_spin_button_get_adjustment(spin)), "value_changed", G_CALLBACK(changed), this); + g_object_set_data(G_OBJECT(spin), "handler", gint_to_pointer(handler)); + g_signal_connect(G_OBJECT(spin), "key_press_event", G_CALLBACK(enter), this); + g_signal_connect(G_OBJECT(spin), "key_press_event", G_CALLBACK(escape), this); + } +}; + + +class NonModalRadio +{ + Callback m_changed; + +public: + NonModalRadio(const Callback& changed) : m_changed(changed) + { + } + void connect(GtkRadioButton* radio) + { + GSList* group = gtk_radio_button_group(radio); + for(; group != 0; group = g_slist_next(group)) + { + toggle_button_connect_callback(GTK_TOGGLE_BUTTON(group->data), m_changed); + } + } +}; + + +#endif diff --git a/tools/urt/libs/gtkutil/paned.cpp b/tools/urt/libs/gtkutil/paned.cpp new file mode 100644 index 00000000..bbf0f5ef --- /dev/null +++ b/tools/urt/libs/gtkutil/paned.cpp @@ -0,0 +1,80 @@ + +#include "paned.h" + +#include +#include + +#include "frame.h" + + +class PanedState +{ +public: + float position; + int size; +}; + +gboolean hpaned_allocate(GtkWidget* widget, GtkAllocation* allocation, PanedState* paned) +{ + if(paned->size != allocation->width) + { + paned->size = allocation->width; + gtk_paned_set_position (GTK_PANED (widget), static_cast(paned->size * paned->position)); + } + return FALSE; +} + +gboolean vpaned_allocate(GtkWidget* widget, GtkAllocation* allocation, PanedState* paned) +{ + if(paned->size != allocation->height) + { + paned->size = allocation->height; + gtk_paned_set_position (GTK_PANED (widget), static_cast(paned->size * paned->position)); + } + return FALSE; +} + +gboolean paned_position(GtkWidget* widget, gpointer dummy, PanedState* paned) +{ + if(paned->size != -1) + paned->position = gtk_paned_get_position (GTK_PANED (widget)) / static_cast(paned->size); + return FALSE; +} + +PanedState g_hpaned = { 0.5f, -1, }; +PanedState g_vpaned1 = { 0.5f, -1, }; +PanedState g_vpaned2 = { 0.5f, -1, }; + +GtkHPaned* create_split_views(GtkWidget* topleft, GtkWidget* topright, GtkWidget* botleft, GtkWidget* botright) +{ + GtkHPaned* hsplit = GTK_HPANED(gtk_hpaned_new()); + gtk_widget_show(GTK_WIDGET(hsplit)); + + g_signal_connect(G_OBJECT(hsplit), "size_allocate", G_CALLBACK(hpaned_allocate), &g_hpaned); + g_signal_connect(G_OBJECT(hsplit), "notify::position", G_CALLBACK(paned_position), &g_hpaned); + + { + GtkVPaned* vsplit = GTK_VPANED(gtk_vpaned_new()); + gtk_paned_add1(GTK_PANED(hsplit), GTK_WIDGET(vsplit)); + gtk_widget_show(GTK_WIDGET(vsplit)); + + g_signal_connect(G_OBJECT(vsplit), "size_allocate", G_CALLBACK(vpaned_allocate), &g_vpaned1); + g_signal_connect(G_OBJECT(vsplit), "notify::position", G_CALLBACK(paned_position), &g_vpaned1); + + gtk_paned_add1(GTK_PANED(vsplit), GTK_WIDGET(create_framed_widget(topleft))); + gtk_paned_add2(GTK_PANED(vsplit), GTK_WIDGET(create_framed_widget(topright))); + } + { + GtkVPaned* vsplit = GTK_VPANED(gtk_vpaned_new()); + gtk_paned_add2(GTK_PANED(hsplit), GTK_WIDGET(vsplit)); + gtk_widget_show(GTK_WIDGET(vsplit)); + + g_signal_connect(G_OBJECT(vsplit), "size_allocate", G_CALLBACK(vpaned_allocate), &g_vpaned2); + g_signal_connect(G_OBJECT(vsplit), "notify::position", G_CALLBACK(paned_position), &g_vpaned2); + + gtk_paned_add1(GTK_PANED(vsplit), GTK_WIDGET(create_framed_widget(botleft))); + gtk_paned_add2(GTK_PANED(vsplit), GTK_WIDGET(create_framed_widget(botright))); + } + return hsplit; +} + diff --git a/tools/urt/libs/gtkutil/paned.h b/tools/urt/libs/gtkutil/paned.h new file mode 100644 index 00000000..2b40676a --- /dev/null +++ b/tools/urt/libs/gtkutil/paned.h @@ -0,0 +1,9 @@ + +#if !defined(INCLUDED_GTKUTIL_PANED_H) +#define INCLUDED_GTKUTIL_PANED_H + +typedef struct _GtkWidget GtkWidget; +typedef struct _GtkHPaned GtkHPaned; +GtkHPaned* create_split_views(GtkWidget* topleft, GtkWidget* topright, GtkWidget* botleft, GtkWidget* botright); + +#endif diff --git a/tools/urt/libs/gtkutil/pointer.cpp b/tools/urt/libs/gtkutil/pointer.cpp new file mode 100644 index 00000000..cbfb4697 --- /dev/null +++ b/tools/urt/libs/gtkutil/pointer.cpp @@ -0,0 +1,3 @@ + +#include "pointer.h" + diff --git a/tools/urt/libs/gtkutil/pointer.h b/tools/urt/libs/gtkutil/pointer.h new file mode 100644 index 00000000..59cbe69d --- /dev/null +++ b/tools/urt/libs/gtkutil/pointer.h @@ -0,0 +1,20 @@ + +#if !defined(INCLUDED_GTKUTIL_POINTER_H) +#define INCLUDED_GTKUTIL_POINTER_H + +typedef int gint; +typedef void* gpointer; + +#include + +inline gint gpointer_to_int(gpointer p) +{ + return gint(std::size_t(p)); +} + +inline gpointer gint_to_pointer(gint i) +{ + return gpointer(std::size_t(i)); +} + +#endif diff --git a/tools/urt/libs/gtkutil/toolbar.cpp b/tools/urt/libs/gtkutil/toolbar.cpp new file mode 100644 index 00000000..8dd22ff6 --- /dev/null +++ b/tools/urt/libs/gtkutil/toolbar.cpp @@ -0,0 +1,58 @@ + +#include "toolbar.h" + +#include +#include + +#include "generic/callback.h" + +#include "accelerator.h" +#include "button.h" +#include "closure.h" +#include "pointer.h" + + +void toolbar_append(GtkToolbar* toolbar, GtkButton* button, const char* description) +{ + gtk_widget_show(GTK_WIDGET(button)); + gtk_button_set_relief(button, GTK_RELIEF_NONE); + GTK_WIDGET_UNSET_FLAGS(GTK_WIDGET(button), GTK_CAN_FOCUS); + GTK_WIDGET_UNSET_FLAGS(GTK_WIDGET(button), GTK_CAN_DEFAULT); + gtk_toolbar_append_element(toolbar, GTK_TOOLBAR_CHILD_WIDGET, GTK_WIDGET(button), "", description, "", 0, 0, 0); +} + +GtkButton* toolbar_append_button(GtkToolbar* toolbar, const char* description, const char* icon, const Callback& callback) +{ + GtkButton* button = GTK_BUTTON(gtk_button_new()); + button_set_icon(button, icon); + button_connect_callback(button, callback); + toolbar_append(toolbar, button, description); + return button; +} + +GtkToggleButton* toolbar_append_toggle_button(GtkToolbar* toolbar, const char* description, const char* icon, const Callback& callback) +{ + GtkToggleButton* button = GTK_TOGGLE_BUTTON(gtk_toggle_button_new()); + button_set_icon(GTK_BUTTON(button), icon); + toggle_button_connect_callback(button, callback); + toolbar_append(toolbar, GTK_BUTTON(button), description); + return button; +} + +GtkButton* toolbar_append_button(GtkToolbar* toolbar, const char* description, const char* icon, const Command& command) +{ + return toolbar_append_button(toolbar, description, icon, command.m_callback); +} + +void toggle_button_set_active_callback(GtkToggleButton& button, bool active) +{ + toggle_button_set_active_no_signal(&button, active); +} +typedef ReferenceCaller1 ToggleButtonSetActiveCaller; + +GtkToggleButton* toolbar_append_toggle_button(GtkToolbar* toolbar, const char* description, const char* icon, const Toggle& toggle) +{ + GtkToggleButton* button = toolbar_append_toggle_button(toolbar, description, icon, toggle.m_command.m_callback); + toggle.m_exportCallback(ToggleButtonSetActiveCaller(*button)); + return button; +} diff --git a/tools/urt/libs/gtkutil/toolbar.h b/tools/urt/libs/gtkutil/toolbar.h new file mode 100644 index 00000000..05c5e24d --- /dev/null +++ b/tools/urt/libs/gtkutil/toolbar.h @@ -0,0 +1,17 @@ + +#if !defined(INCLUDED_GTKUTIL_TOOLBAR_H) +#define INCLUDED_GTKUTIL_TOOLBAR_H + +class Callback; +typedef struct _GtkButton GtkButton; +typedef struct _GtkToggleButton GtkToggleButton; +typedef struct _GtkToolbar GtkToolbar; +class Command; +class Toggle; + +GtkButton* toolbar_append_button(GtkToolbar* toolbar, const char* description, const char* icon, const Callback& callback); +GtkButton* toolbar_append_button(GtkToolbar* toolbar, const char* description, const char* icon, const Command& command); +GtkToggleButton* toolbar_append_toggle_button(GtkToolbar* toolbar, const char* description, const char* icon, const Callback& callback); +GtkToggleButton* toolbar_append_toggle_button(GtkToolbar* toolbar, const char* description, const char* icon, const Toggle& toggle); + +#endif diff --git a/tools/urt/libs/gtkutil/widget.cpp b/tools/urt/libs/gtkutil/widget.cpp new file mode 100644 index 00000000..23c24a65 --- /dev/null +++ b/tools/urt/libs/gtkutil/widget.cpp @@ -0,0 +1,3 @@ + +#include "widget.h" + diff --git a/tools/urt/libs/gtkutil/widget.h b/tools/urt/libs/gtkutil/widget.h new file mode 100644 index 00000000..0e6f6866 --- /dev/null +++ b/tools/urt/libs/gtkutil/widget.h @@ -0,0 +1,145 @@ + +#if !defined(INCLUDED_GTKUTIL_WIDGET_H) +#define INCLUDED_GTKUTIL_WIDGET_H + +#include +#include +#include "generic/callback.h" +#include "warnings.h" + +inline void widget_set_visible(GtkWidget* widget, bool shown) +{ + if(shown) + { + gtk_widget_show(widget); + } + else + { + gtk_widget_hide(widget); + } +} + +inline bool widget_is_visible(GtkWidget* widget) +{ + return GTK_WIDGET_VISIBLE(widget) != FALSE; +} + +inline void widget_toggle_visible(GtkWidget* widget) +{ + widget_set_visible(widget, !widget_is_visible(widget)); +} + +class ToggleItem +{ + BoolExportCallback m_exportCallback; + typedef std::list ImportCallbacks; + ImportCallbacks m_importCallbacks; +public: + ToggleItem(const BoolExportCallback& exportCallback) : m_exportCallback(exportCallback) + { + } + + void update() + { + for(ImportCallbacks::iterator i = m_importCallbacks.begin(); i != m_importCallbacks.end(); ++i) + { + m_exportCallback(*i); + } + } + + void addCallback(const BoolImportCallback& callback) + { + m_importCallbacks.push_back(callback); + m_exportCallback(callback); + } + typedef MemberCaller1 AddCallbackCaller; +}; + +class ToggleShown +{ + bool m_shownDeferred; + + ToggleShown(const ToggleShown& other); // NOT COPYABLE + ToggleShown& operator=(const ToggleShown& other); // NOT ASSIGNABLE + + static gboolean notify_visible(GtkWidget* widget, gpointer dummy, ToggleShown* self) + { + self->update(); + return FALSE; + } + static gboolean destroy(GtkWidget* widget, ToggleShown* self) + { + self->m_shownDeferred = GTK_WIDGET_VISIBLE(self->m_widget) != FALSE; + self->m_widget = 0; + return FALSE; + } +public: + GtkWidget* m_widget; + ToggleItem m_item; + + ToggleShown(bool shown) + : m_shownDeferred(shown), m_widget(0), m_item(ActiveCaller(*this)) + { + } + void update() + { + m_item.update(); + } + bool active() const + { + if(m_widget == 0) + { + return m_shownDeferred; + } + else + { + return GTK_WIDGET_VISIBLE(m_widget) != FALSE; + } + } + void exportActive(const BoolImportCallback& importCallback) + { + importCallback(active()); + } + typedef MemberCaller1 ActiveCaller; + void set(bool shown) + { + if(m_widget == 0) + { + m_shownDeferred = shown; + } + else + { + widget_set_visible(m_widget, shown); + } + } + void toggle() + { + widget_toggle_visible(m_widget); + } + typedef MemberCaller ToggleCaller; + void connect(GtkWidget* widget) + { + m_widget = widget; + widget_set_visible(m_widget, m_shownDeferred); + g_signal_connect(G_OBJECT(m_widget), "notify::visible", G_CALLBACK(notify_visible), this); + g_signal_connect(G_OBJECT(m_widget), "destroy", G_CALLBACK(destroy), this); + update(); + } +}; + + +inline void widget_queue_draw(GtkWidget& widget) +{ + gtk_widget_queue_draw(&widget); +} +typedef ReferenceCaller WidgetQueueDrawCaller; + + +inline void widget_make_default(GtkWidget* widget) +{ + GTK_WIDGET_SET_FLAGS(widget, GTK_CAN_DEFAULT); + gtk_widget_grab_default(widget); +} + + +#endif diff --git a/tools/urt/libs/gtkutil/window.cpp b/tools/urt/libs/gtkutil/window.cpp new file mode 100644 index 00000000..47c44f8d --- /dev/null +++ b/tools/urt/libs/gtkutil/window.cpp @@ -0,0 +1,150 @@ + +#include "window.h" + +#include + +#include "pointer.h" +#include "accelerator.h" + +inline void CHECK_RESTORE(GtkWidget* w) +{ + if(gpointer_to_int(g_object_get_data(G_OBJECT(w), "was_mapped")) != 0) + { + gtk_widget_show(w); + } +} + +inline void CHECK_MINIMIZE(GtkWidget* w) +{ + g_object_set_data(G_OBJECT(w), "was_mapped", gint_to_pointer(GTK_WIDGET_VISIBLE(w))); + gtk_widget_hide(w); +} + +static gboolean main_window_iconified(GtkWidget* widget, GdkEventWindowState* event, gpointer data) +{ + if((event->changed_mask & (GDK_WINDOW_STATE_ICONIFIED|GDK_WINDOW_STATE_WITHDRAWN)) != 0) + { + if((event->new_window_state & (GDK_WINDOW_STATE_ICONIFIED|GDK_WINDOW_STATE_WITHDRAWN)) != 0) + { + CHECK_MINIMIZE(GTK_WIDGET(data)); + } + else + { + CHECK_RESTORE(GTK_WIDGET(data)); + } + } + return FALSE; +} + +unsigned int connect_floating(GtkWindow* main_window, GtkWindow* floating) +{ + return g_signal_connect(G_OBJECT(main_window), "window_state_event", G_CALLBACK(main_window_iconified), floating); +} + +gboolean destroy_disconnect_floating(GtkWindow* widget, gpointer data) +{ + g_signal_handler_disconnect(G_OBJECT(data), gpointer_to_int(g_object_get_data(G_OBJECT(widget), "floating_handler"))); + return FALSE; +} + +gboolean floating_window_delete_present(GtkWindow* floating, GdkEventFocus *event, GtkWindow* main_window) +{ + if(gtk_window_is_active(floating) || gtk_window_is_active(main_window)) + { + gtk_window_present(main_window); + } + return FALSE; +} + +guint connect_floating_window_delete_present(GtkWindow* floating, GtkWindow* main_window) +{ + return g_signal_connect(G_OBJECT(floating), "delete_event", G_CALLBACK(floating_window_delete_present), main_window); +} + +gboolean floating_window_destroy_present(GtkWindow* floating, GtkWindow* main_window) +{ + if(gtk_window_is_active(floating) || gtk_window_is_active(main_window)) + { + gtk_window_present(main_window); + } + return FALSE; +} + +guint connect_floating_window_destroy_present(GtkWindow* floating, GtkWindow* main_window) +{ + return g_signal_connect(G_OBJECT(floating), "destroy", G_CALLBACK(floating_window_destroy_present), main_window); +} + +GtkWindow* create_floating_window(const char* title, GtkWindow* parent) +{ + GtkWindow* window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL)); + gtk_window_set_title(window, title); + + if(parent != 0) + { + gtk_window_set_transient_for(window, parent); + connect_floating_window_destroy_present(window, parent); + g_object_set_data(G_OBJECT(window), "floating_handler", gint_to_pointer(connect_floating(parent, window))); + g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(destroy_disconnect_floating), parent); + } + + return window; +} + +void destroy_floating_window(GtkWindow* window) +{ + gtk_widget_destroy(GTK_WIDGET(window)); +} + +gint window_realize_remove_sysmenu(GtkWidget* widget, gpointer data) +{ + gdk_window_set_decorations(widget->window, (GdkWMDecoration)(GDK_DECOR_ALL|GDK_DECOR_MENU)); + return FALSE; +} + +gboolean persistent_floating_window_delete(GtkWindow* floating, GdkEvent *event, GtkWindow* main_window) +{ + gtk_widget_hide(GTK_WIDGET(floating)); + return TRUE; +} + +GtkWindow* create_persistent_floating_window(const char* title, GtkWindow* main_window) +{ + GtkWindow* window = GTK_WINDOW(create_floating_window(title, main_window)); + + gtk_widget_set_events(GTK_WIDGET(window), GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK); + + connect_floating_window_delete_present(window, main_window); + g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(persistent_floating_window_delete), 0); + +#if 0 + if(g_multimon_globals.m_bStartOnPrimMon && g_multimon_globals.m_bNoSysMenuPopups) + g_signal_connect(G_OBJECT(window), "realize", G_CALLBACK(window_realize_remove_sysmenu), 0); +#endif + + return window; +} + +gint window_realize_remove_minmax(GtkWidget* widget, gpointer data) +{ + gdk_window_set_decorations(widget->window, (GdkWMDecoration)(GDK_DECOR_ALL|GDK_DECOR_MINIMIZE|GDK_DECOR_MAXIMIZE)); + return FALSE; +} + +void window_remove_minmax(GtkWindow* window) +{ + g_signal_connect(G_OBJECT(window), "realize", G_CALLBACK(window_realize_remove_minmax), 0); +} + + +GtkScrolledWindow* create_scrolled_window(GtkPolicyType hscrollbar_policy, GtkPolicyType vscrollbar_policy, int border) +{ + GtkScrolledWindow* scr = GTK_SCROLLED_WINDOW(gtk_scrolled_window_new(0, 0)); + gtk_widget_show(GTK_WIDGET(scr)); + gtk_scrolled_window_set_policy(scr, hscrollbar_policy, vscrollbar_policy); + gtk_scrolled_window_set_shadow_type(scr, GTK_SHADOW_IN); + gtk_container_set_border_width(GTK_CONTAINER(scr), border); + return scr; +} + + diff --git a/tools/urt/libs/gtkutil/window.h b/tools/urt/libs/gtkutil/window.h new file mode 100644 index 00000000..263aca39 --- /dev/null +++ b/tools/urt/libs/gtkutil/window.h @@ -0,0 +1,154 @@ + +#if !defined(INCLUDED_GTKUTIL_WINDOW_H) +#define INCLUDED_GTKUTIL_WINDOW_H + +#include + +#include "debugging/debugging.h" +#include "generic/callback.h" +#include "widget.h" + +inline gboolean window_focus_in_clear_focus_widget(GtkWidget* widget, GdkEventKey* event, gpointer data) +{ + gtk_window_set_focus(GTK_WINDOW(widget), NULL); + return FALSE; +} + +inline guint window_connect_focus_in_clear_focus_widget(GtkWindow* window) +{ + return g_signal_connect(G_OBJECT(window), "focus_in_event", G_CALLBACK(window_focus_in_clear_focus_widget), NULL); +} + + +unsigned int connect_floating(GtkWindow* main_window, GtkWindow* floating); +GtkWindow* create_floating_window(const char* title, GtkWindow* parent); +void destroy_floating_window(GtkWindow* window); + +GtkWindow* create_persistent_floating_window(const char* title, GtkWindow* main_window); +gboolean persistent_floating_window_delete(GtkWindow* floating, GdkEvent *event, GtkWindow* main_window); + +void window_remove_minmax(GtkWindow* window); + +typedef struct _GtkScrolledWindow GtkScrolledWindow; +GtkScrolledWindow* create_scrolled_window(GtkPolicyType hscrollbar_policy, GtkPolicyType vscrollbar_policy, int border = 0); + + +struct WindowPosition +{ + int x, y, w, h; + + WindowPosition() + { + } + WindowPosition(int _x, int _y, int _w, int _h) + : x(_x), y(_y), w(_w), h(_h) + { + } +}; + +const WindowPosition c_default_window_pos(50, 25, 400, 300); + +inline void window_get_position(GtkWindow* window, WindowPosition& position) +{ + ASSERT_MESSAGE(window != 0, "error saving window position"); + + gtk_window_get_position(window, &position.x, &position.y); + gtk_window_get_size(window, &position.w, &position.h); +} + +inline void window_set_position(GtkWindow* window, const WindowPosition& position) +{ + gtk_window_set_gravity(window, GDK_GRAVITY_STATIC); + + GdkScreen* screen = gdk_screen_get_default(); + if(position.x < 0 + || position.y < 0 + || position.x > gdk_screen_get_width(screen) + || position.y > gdk_screen_get_height(screen)) + { + gtk_window_set_position(window, GTK_WIN_POS_CENTER_ON_PARENT); + } + else + { + gtk_window_move(window, position.x, position.y); + } + + gtk_window_set_default_size(window, position.w, position.h); +} + +inline void WindowPosition_Parse(WindowPosition& position, const char* value) +{ + if(sscanf(value, "%d %d %d %d", &position.x, &position.y, &position.w, &position.h) != 4) + { + position = WindowPosition(c_default_window_pos); // ensure sane default value for window position + } +} +typedef ReferenceCaller1 WindowPositionImportStringCaller; + +inline void WindowPosition_Write(const WindowPosition& position, const StringImportCallback& importCallback) +{ + char buffer[64]; + sprintf(buffer, "%d %d %d %d", position.x, position.y, position.w, position.h); + importCallback(buffer); +} +typedef ConstReferenceCaller1 WindowPositionExportStringCaller; + + + +class WindowPositionTracker +{ + WindowPosition m_position; + + static gboolean configure(GtkWidget* widget, GdkEventConfigure *event, WindowPositionTracker* self) + { + self->m_position = WindowPosition(event->x, event->y, event->width, event->height); + return FALSE; + } + +public: + WindowPositionTracker() + : m_position(c_default_window_pos) + { + } + + void sync(GtkWindow* window) + { + window_set_position(window, m_position); + } + + void connect(GtkWindow* window) + { + sync(window); + g_signal_connect(G_OBJECT(window), "configure_event", G_CALLBACK(configure), this); + } + + const WindowPosition& getPosition() const + { + return m_position; + } + + //hack + void setPosition(const WindowPosition& position) + { + m_position = position; + } +}; + + +inline void WindowPositionTracker_importString(WindowPositionTracker& self, const char* value) +{ + WindowPosition position; + WindowPosition_Parse(position, value); + self.setPosition(position); +} +typedef ReferenceCaller1 WindowPositionTrackerImportStringCaller; + +inline void WindowPositionTracker_exportString(const WindowPositionTracker& self, const StringImportCallback& importer) +{ + WindowPosition_Write(self.getPosition(), importer); +} +typedef ConstReferenceCaller1 WindowPositionTrackerExportStringCaller; + + + +#endif diff --git a/tools/urt/libs/gtkutil/xorrectangle.cpp b/tools/urt/libs/gtkutil/xorrectangle.cpp new file mode 100644 index 00000000..5d759416 --- /dev/null +++ b/tools/urt/libs/gtkutil/xorrectangle.cpp @@ -0,0 +1,3 @@ + +#include "xorrectangle.h" + diff --git a/tools/urt/libs/gtkutil/xorrectangle.h b/tools/urt/libs/gtkutil/xorrectangle.h new file mode 100644 index 00000000..58ad77f5 --- /dev/null +++ b/tools/urt/libs/gtkutil/xorrectangle.h @@ -0,0 +1,105 @@ + +#if !defined (INCLUDED_XORRECTANGLE_H) +#define INCLUDED_XORRECTANGLE_H + +#include +#include "math/vector.h" + +class rectangle_t +{ +public: + rectangle_t() + : x(0), y(0), w(0), h(0) + {} + rectangle_t(float _x, float _y, float _w, float _h) + : x(_x), y(_y), w(_w), h(_h) + {} + float x; + float y; + float w; + float h; +}; + +struct Coord2D +{ + float x, y; + Coord2D(float _x, float _y) + : x(_x), y(_y) + { + } +}; + +inline Coord2D coord2d_device2screen(const Coord2D& coord, unsigned int width, unsigned int height) +{ + return Coord2D(((coord.x + 1.0f) * 0.5f) * width, ((coord.y + 1.0f) * 0.5f) * height); +} + +inline rectangle_t rectangle_from_area(const float min[2], const float max[2], unsigned int width, unsigned int height) +{ + Coord2D botleft(coord2d_device2screen(Coord2D(min[0], min[1]), width, height)); + Coord2D topright(coord2d_device2screen(Coord2D(max[0], max[1]), width, height)); + return rectangle_t(botleft.x, botleft.y, topright.x - botleft.x, topright.y - botleft.y); +} + +class XORRectangle +{ + + rectangle_t m_rectangle; + + GtkWidget* m_widget; + GdkGC* m_gc; + + bool initialised() const + { + return m_gc != 0; + } + void lazy_init() + { + if(!initialised()) + { + m_gc = gdk_gc_new(m_widget->window); + + GdkColor color = { 0, 0xffff, 0xffff, 0xffff, }; + GdkColormap* colormap = gdk_window_get_colormap(m_widget->window); + gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE); + gdk_gc_copy(m_gc, m_widget->style->white_gc); + gdk_gc_set_foreground(m_gc, &color); + gdk_gc_set_background(m_gc, &color); + + gdk_gc_set_function(m_gc, GDK_INVERT); + } + } + void draw() const + { + const int x = float_to_integer(m_rectangle.x); + const int y = float_to_integer(m_rectangle.y); + const int w = float_to_integer(m_rectangle.w); + const int h = float_to_integer(m_rectangle.h); + gdk_draw_rectangle(m_widget->window, m_gc, FALSE, x, -(h) - (y - m_widget->allocation.height), w, h); + } + +public: + XORRectangle(GtkWidget* widget) : m_widget(widget), m_gc(0) + { + } + ~XORRectangle() + { + if(initialised()) + { + gdk_gc_unref(m_gc); + } + } + void set(rectangle_t rectangle) + { + if(GTK_WIDGET_REALIZED(m_widget)) + { + lazy_init(); + draw(); + m_rectangle = rectangle; + draw(); + } + } +}; + + +#endif diff --git a/tools/urt/libs/imagelib.cpp b/tools/urt/libs/imagelib.cpp new file mode 100644 index 00000000..05181ce0 --- /dev/null +++ b/tools/urt/libs/imagelib.cpp @@ -0,0 +1,3 @@ + +#include "imagelib.h" + diff --git a/tools/urt/libs/imagelib.h b/tools/urt/libs/imagelib.h new file mode 100644 index 00000000..5822ce50 --- /dev/null +++ b/tools/urt/libs/imagelib.h @@ -0,0 +1,132 @@ + +#if !defined(INCLUDED_IMAGELIB_H) +#define INCLUDED_IMAGELIB_H + +#include "iimage.h" +#include "iarchive.h" +#include "idatastream.h" +#include + +struct RGBAPixel +{ + unsigned char red, green, blue, alpha; +}; + +class RGBAImage : public Image +{ + RGBAImage(const RGBAImage& other); + RGBAImage& operator=(const RGBAImage& other); +public: + RGBAPixel* pixels; + unsigned int width, height; + + RGBAImage(unsigned int _width, unsigned int _height) + : pixels(new RGBAPixel[_width * _height]), width(_width), height(_height) + { + } + ~RGBAImage() + { + delete pixels; + } + + void release() + { + delete this; + } + byte* getRGBAPixels() const + { + return reinterpret_cast(pixels); + } + unsigned int getWidth() const + { + return width; + } + unsigned int getHeight() const + { + return height; + } +}; + +class RGBAImageFlags : public RGBAImage +{ +public: + int m_surfaceFlags; + int m_contentFlags; + int m_value; + RGBAImageFlags(unsigned short _width, unsigned short _height, int surfaceFlags, short contentFlags, int value) : + RGBAImage(_width, _height), m_surfaceFlags(surfaceFlags), m_contentFlags(contentFlags), m_value(value) + { + } + + int getSurfaceFlags() const + { + return m_surfaceFlags; + } + int getContentFlags() const + { + return m_contentFlags; + } + int getValue() const + { + return m_value; + } +}; + + +inline InputStream::byte_type* ArchiveFile_loadBuffer(ArchiveFile& file, std::size_t& length) +{ + InputStream::byte_type* buffer = (InputStream::byte_type*)malloc(file.size() + 1); + length = file.getInputStream().read(buffer, file.size()); + buffer[file.size()] = 0; + return buffer; +} + +inline void ArchiveFile_freeBuffer(InputStream::byte_type* buffer) +{ + free(buffer); +} + +class ScopedArchiveBuffer +{ +public: + std::size_t length; + InputStream::byte_type* buffer; + + ScopedArchiveBuffer(ArchiveFile& file) + { + buffer = ArchiveFile_loadBuffer(file, length); + } + ~ScopedArchiveBuffer() + { + ArchiveFile_freeBuffer(buffer); + } +}; + +class PointerInputStream : public InputStream +{ + const byte* m_read; +public: + PointerInputStream(const byte* pointer) + : m_read(pointer) + { + } + std::size_t read(byte* buffer, std::size_t length) + { + const byte* end = m_read + length; + while(m_read != end) + { + *buffer++ = *m_read++; + } + return length; + } + void seek(std::size_t offset) + { + m_read += offset; + } + const byte* get() + { + return m_read; + } +}; + +#endif diff --git a/tools/urt/libs/instancelib.cpp b/tools/urt/libs/instancelib.cpp new file mode 100644 index 00000000..98878ceb --- /dev/null +++ b/tools/urt/libs/instancelib.cpp @@ -0,0 +1,2 @@ + +#include "instancelib.h" diff --git a/tools/urt/libs/instancelib.h b/tools/urt/libs/instancelib.h new file mode 100644 index 00000000..a8e46ac3 --- /dev/null +++ b/tools/urt/libs/instancelib.h @@ -0,0 +1,143 @@ + +#if !defined (INCLUDED_INSTANCELIB_H) +#define INCLUDED_INSTANCELIB_H + +#include "debugging/debugging.h" + +#include "iscenegraph.h" + +#include "scenelib.h" +#include "generic/reference.h" +#include "generic/callback.h" +#include + +class InstanceSubgraphWalker : public scene::Traversable::Walker +{ + scene::Instantiable::Observer* m_observer; + mutable scene::Path m_path; + mutable Stack m_parent; +public: + InstanceSubgraphWalker(scene::Instantiable::Observer* observer, const scene::Path& path, scene::Instance* parent) + : m_observer(observer), m_path(path), m_parent(parent) + { + } + bool pre(scene::Node& node) const + { + m_path.push(makeReference(node)); + scene::Instance* instance = Node_getInstantiable(node)->create(m_path, m_parent.top()); + m_observer->insert(instance); + Node_getInstantiable(node)->insert(m_observer, m_path, instance); + m_parent.push(instance); + return true; + } + void post(scene::Node& node) const + { + m_path.pop(); + m_parent.pop(); + } +}; + +class UninstanceSubgraphWalker : public scene::Traversable::Walker +{ + scene::Instantiable::Observer* m_observer; + mutable scene::Path m_path; +public: + UninstanceSubgraphWalker(scene::Instantiable::Observer* observer, const scene::Path& parent) + : m_observer(observer), m_path(parent) + { + } + bool pre(scene::Node& node) const + { + m_path.push(makeReference(node)); + return true; + } + void post(scene::Node& node) const + { + scene::Instance* instance = Node_getInstantiable(node)->erase(m_observer, m_path); + m_observer->erase(instance); + delete instance; + m_path.pop(); + } +}; + +class Instances : public scene::Traversable::Observer +{ + typedef std::pair CachePath; + + typedef CachePath key_type; + + typedef std::map InstanceMap; + InstanceMap m_instances; +public: + + typedef InstanceMap::iterator iterator; + + iterator begin() + { + return m_instances.begin(); + } + iterator end() + { + return m_instances.end(); + } + + // traverse observer + void insert(scene::Node& child) + { + for(iterator i = begin(); i != end(); ++i) + { + Node_traverseSubgraph(child, InstanceSubgraphWalker((*i).first.first, (*i).first.second, (*i).second)); + (*i).second->boundsChanged(); + } + } + void erase(scene::Node& child) + { + for(iterator i = begin(); i != end(); ++i) + { + Node_traverseSubgraph(child, UninstanceSubgraphWalker((*i).first.first, (*i).first.second)); + (*i).second->boundsChanged(); + } + } + + // instance + void forEachInstance(const scene::Instantiable::Visitor& visitor) + { + for(iterator i = begin(); i != end(); ++i) + { + visitor.visit(*(*i).second); + } + } + + void insert(scene::Instantiable::Observer* observer, const scene::Path& path, scene::Instance* instance) + { + ASSERT_MESSAGE(m_instances.find(key_type(observer, PathConstReference(instance->path()))) == m_instances.end(), "Instances::insert - element already exists"); + m_instances.insert(InstanceMap::value_type(key_type(observer, PathConstReference(instance->path())), instance)); + } + scene::Instance* erase(scene::Instantiable::Observer* observer, const scene::Path& path) + { + ASSERT_MESSAGE(m_instances.find(key_type(observer, PathConstReference(path))) != m_instances.end(), "Instances::erase - failed to find element"); + InstanceMap::iterator i = m_instances.find(key_type(observer, PathConstReference(path))); + scene::Instance* instance = i->second; + m_instances.erase(i); + return instance; + } + + void transformChanged() + { + for(InstanceMap::iterator i = m_instances.begin(); i != m_instances.end(); ++i) + { + (*i).second->transformChanged(); + } + } + typedef MemberCaller TransformChangedCaller; + void boundsChanged() + { + for(InstanceMap::iterator i = m_instances.begin(); i != m_instances.end(); ++i) + { + (*i).second->boundsChanged(); + } + } + typedef MemberCaller BoundsChangedCaller; +}; + +#endif diff --git a/tools/urt/libs/jpeg6/.cvsignore b/tools/urt/libs/jpeg6/.cvsignore new file mode 100644 index 00000000..face6ad3 --- /dev/null +++ b/tools/urt/libs/jpeg6/.cvsignore @@ -0,0 +1,8 @@ +Debug +Release +*.ncb +*.opt +*.plg +*.001 +*.BAK +.consign diff --git a/tools/urt/libs/jpeg6/.cvswrappers b/tools/urt/libs/jpeg6/.cvswrappers new file mode 100644 index 00000000..cdfd6d4a --- /dev/null +++ b/tools/urt/libs/jpeg6/.cvswrappers @@ -0,0 +1,3 @@ +*.dsp -m 'COPY' -k 'b' +*.dsw -m 'COPY' -k 'b' +*.scc -m 'COPY' -k 'b' diff --git a/tools/urt/libs/jpeg6/jchuff.h b/tools/urt/libs/jpeg6/jchuff.h new file mode 100644 index 00000000..7ae05e85 --- /dev/null +++ b/tools/urt/libs/jpeg6/jchuff.h @@ -0,0 +1,68 @@ +/* + + * jchuff.h + + * + + * Copyright (C) 1991-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains declarations for Huffman entropy encoding routines + + * that are shared between the sequential encoder (jchuff.c) and the + + * progressive encoder (jcphuff.c). No other modules need to see these. + + */ + + + +/* Derived data constructed for each Huffman table */ + + + +typedef struct { + + unsigned int ehufco[256]; /* code for each symbol */ + + char ehufsi[256]; /* length of code for each symbol */ + + /* If no code has been allocated for a symbol S, ehufsi[S] contains 0 */ + +} c_derived_tbl; + + + +/* Short forms of external names for systems with brain-damaged linkers. */ + + + +#ifdef NEED_SHORT_EXTERNAL_NAMES + +#define jpeg_make_c_derived_tbl jMkCDerived + +#define jpeg_gen_optimal_table jGenOptTbl + +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + + +/* Expand a Huffman table definition into the derived format */ + +EXTERN void jpeg_make_c_derived_tbl JPP((j_compress_ptr cinfo, + + JHUFF_TBL * htbl, c_derived_tbl ** pdtbl)); + + + +/* Generate an optimal table definition given the specified counts */ + +EXTERN void jpeg_gen_optimal_table JPP((j_compress_ptr cinfo, + + JHUFF_TBL * htbl, long freq[])); + diff --git a/tools/urt/libs/jpeg6/jcomapi.cpp b/tools/urt/libs/jpeg6/jcomapi.cpp new file mode 100644 index 00000000..ebf2d791 --- /dev/null +++ b/tools/urt/libs/jpeg6/jcomapi.cpp @@ -0,0 +1,188 @@ +/* + + * jcomapi.c + + * + + * Copyright (C) 1994, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains application interface routines that are used for both + + * compression and decompression. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "radiant_jpeglib.h" + + + + + +/* + + * Abort processing of a JPEG compression or decompression operation, + + * but don't destroy the object itself. + + * + + * For this, we merely clean up all the nonpermanent memory pools. + + * Note that temp files (virtual arrays) are not allowed to belong to + + * the permanent pool, so we will be able to close all temp files here. + + * Closing a data source or destination, if necessary, is the application's + + * responsibility. + + */ + + + +GLOBAL void + +jpeg_abort (j_common_ptr cinfo) + +{ + + int pool; + + + + /* Releasing pools in reverse order might help avoid fragmentation + + * with some (brain-damaged) malloc libraries. + + */ + + for (pool = JPOOL_NUMPOOLS-1; pool > JPOOL_PERMANENT; pool--) { + + (*cinfo->mem->free_pool) (cinfo, pool); + + } + + + + /* Reset overall state for possible reuse of object */ + + cinfo->global_state = (cinfo->is_decompressor ? DSTATE_START : CSTATE_START); + +} + + + + + +/* + + * Destruction of a JPEG object. + + * + + * Everything gets deallocated except the master jpeg_compress_struct itself + + * and the error manager struct. Both of these are supplied by the application + + * and must be freed, if necessary, by the application. (Often they are on + + * the stack and so don't need to be freed anyway.) + + * Closing a data source or destination, if necessary, is the application's + + * responsibility. + + */ + + + +GLOBAL void + +jpeg_destroy (j_common_ptr cinfo) + +{ + + /* We need only tell the memory manager to release everything. */ + + /* NB: mem pointer is NULL if memory mgr failed to initialize. */ + + if (cinfo->mem != NULL) + + (*cinfo->mem->self_destruct) (cinfo); + + cinfo->mem = NULL; /* be safe if jpeg_destroy is called twice */ + + cinfo->global_state = 0; /* mark it destroyed */ + +} + + + + + +/* + + * Convenience routines for allocating quantization and Huffman tables. + + * (Would jutils.c be a more reasonable place to put these?) + + */ + + + +GLOBAL JQUANT_TBL * + +jpeg_alloc_quant_table (j_common_ptr cinfo) + +{ + + JQUANT_TBL *tbl; + + + + tbl = (JQUANT_TBL *) + + (*cinfo->mem->alloc_small) (cinfo, JPOOL_PERMANENT, SIZEOF(JQUANT_TBL)); + + tbl->sent_table = FALSE; /* make sure this is false in any new table */ + + return tbl; + +} + + + + + +GLOBAL JHUFF_TBL * + +jpeg_alloc_huff_table (j_common_ptr cinfo) + +{ + + JHUFF_TBL *tbl; + + + + tbl = (JHUFF_TBL *) + + (*cinfo->mem->alloc_small) (cinfo, JPOOL_PERMANENT, SIZEOF(JHUFF_TBL)); + + tbl->sent_table = FALSE; /* make sure this is false in any new table */ + + return tbl; + +} + diff --git a/tools/urt/libs/jpeg6/jconfig.h b/tools/urt/libs/jpeg6/jconfig.h new file mode 100644 index 00000000..c727519b --- /dev/null +++ b/tools/urt/libs/jpeg6/jconfig.h @@ -0,0 +1,82 @@ +/* jconfig.wat --- jconfig.h for Watcom C/C++ on MS-DOS or OS/2. */ + +/* see jconfig.doc for explanations */ + + + +#define HAVE_PROTOTYPES + +#define HAVE_UNSIGNED_CHAR + +#define HAVE_UNSIGNED_SHORT + +/* #define void char */ + +/* #define const */ + +#define CHAR_IS_UNSIGNED + +#define HAVE_STDDEF_H + +#define HAVE_STDLIB_H + +#undef NEED_BSD_STRINGS + +#undef NEED_SYS_TYPES_H + +#undef NEED_FAR_POINTERS /* Watcom uses flat 32-bit addressing */ + +#undef NEED_SHORT_EXTERNAL_NAMES + +#undef INCOMPLETE_TYPES_BROKEN + + + +#define JDCT_DEFAULT JDCT_FLOAT + +#define JDCT_FASTEST JDCT_FLOAT + + + +#ifdef JPEG_INTERNALS + + + +#undef RIGHT_SHIFT_IS_UNSIGNED + + + +#endif /* JPEG_INTERNALS */ + + + +#ifdef JPEG_CJPEG_DJPEG + + + +#define BMP_SUPPORTED /* BMP image file format */ + +#define GIF_SUPPORTED /* GIF image file format */ + +#define PPM_SUPPORTED /* PBMPLUS PPM/PGM image file format */ + +#undef RLE_SUPPORTED /* Utah RLE image file format */ + +#define TARGA_SUPPORTED /* Targa image file format */ + + + +#undef TWO_FILE_COMMANDLINE /* optional */ + +#define USE_SETMODE /* Needed to make one-file style work in Watcom */ + +#undef NEED_SIGNAL_CATCHER /* Define this if you use jmemname.c */ + +#undef DONT_USE_B_MODE + +#undef PROGRESS_REPORT /* optional */ + + + +#endif /* JPEG_CJPEG_DJPEG */ + diff --git a/tools/urt/libs/jpeg6/jdapimin.cpp b/tools/urt/libs/jpeg6/jdapimin.cpp new file mode 100644 index 00000000..9086b490 --- /dev/null +++ b/tools/urt/libs/jpeg6/jdapimin.cpp @@ -0,0 +1,800 @@ +/* + + * jdapimin.c + + * + + * Copyright (C) 1994-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains application interface code for the decompression half + + * of the JPEG library. These are the "minimum" API routines that may be + + * needed in either the normal full-decompression case or the + + * transcoding-only case. + + * + + * Most of the routines intended to be called directly by an application + + * are in this file or in jdapistd.c. But also see jcomapi.c for routines + + * shared by compression and decompression, and jdtrans.c for the transcoding + + * case. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "radiant_jpeglib.h" + + + + + +/* + + * Initialization of a JPEG decompression object. + + * The error manager must already be set up (in case memory manager fails). + + */ + + + +GLOBAL void + +jpeg_create_decompress (j_decompress_ptr cinfo) + +{ + + int i; + + + + /* For debugging purposes, zero the whole master structure. + + * But error manager pointer is already there, so save and restore it. + + */ + + { + + struct jpeg_error_mgr * err = cinfo->err; + + i = sizeof(struct jpeg_decompress_struct); + + i = SIZEOF(struct jpeg_decompress_struct); + + MEMZERO(cinfo, SIZEOF(struct jpeg_decompress_struct)); + + cinfo->err = err; + + } + + cinfo->is_decompressor = TRUE; + + + + /* Initialize a memory manager instance for this object */ + + jinit_memory_mgr((j_common_ptr) cinfo); + + + + /* Zero out pointers to permanent structures. */ + + cinfo->progress = NULL; + + cinfo->src = NULL; + + + + for (i = 0; i < NUM_QUANT_TBLS; i++) + + cinfo->quant_tbl_ptrs[i] = NULL; + + + + for (i = 0; i < NUM_HUFF_TBLS; i++) { + + cinfo->dc_huff_tbl_ptrs[i] = NULL; + + cinfo->ac_huff_tbl_ptrs[i] = NULL; + + } + + + + /* Initialize marker processor so application can override methods + + * for COM, APPn markers before calling jpeg_read_header. + + */ + + jinit_marker_reader(cinfo); + + + + /* And initialize the overall input controller. */ + + jinit_input_controller(cinfo); + + + + /* OK, I'm ready */ + + cinfo->global_state = DSTATE_START; + +} + + + + + +/* + + * Destruction of a JPEG decompression object + + */ + + + +GLOBAL void + +jpeg_destroy_decompress (j_decompress_ptr cinfo) + +{ + + jpeg_destroy((j_common_ptr) cinfo); /* use common routine */ + +} + + + + + +/* + + * Abort processing of a JPEG decompression operation, + + * but don't destroy the object itself. + + */ + + + +GLOBAL void + +jpeg_abort_decompress (j_decompress_ptr cinfo) + +{ + + jpeg_abort((j_common_ptr) cinfo); /* use common routine */ + +} + + + + + +/* + + * Install a special processing method for COM or APPn markers. + + */ + + + +GLOBAL void + +jpeg_set_marker_processor (j_decompress_ptr cinfo, int marker_code, + + jpeg_marker_parser_method routine) + +{ + + if (marker_code == JPEG_COM) + + cinfo->marker->process_COM = routine; + + else if (marker_code >= JPEG_APP0 && marker_code <= JPEG_APP0+15) + + cinfo->marker->process_APPn[marker_code-JPEG_APP0] = routine; + + else + + ERREXIT1(cinfo, JERR_UNKNOWN_MARKER, marker_code); + +} + + + + + +/* + + * Set default decompression parameters. + + */ + + + +LOCAL void + +default_decompress_parms (j_decompress_ptr cinfo) + +{ + + /* Guess the input colorspace, and set output colorspace accordingly. */ + + /* (Wish JPEG committee had provided a real way to specify this...) */ + + /* Note application may override our guesses. */ + + switch (cinfo->num_components) { + + case 1: + + cinfo->jpeg_color_space = JCS_GRAYSCALE; + + cinfo->out_color_space = JCS_GRAYSCALE; + + break; + + + + case 3: + + if (cinfo->saw_JFIF_marker) { + + cinfo->jpeg_color_space = JCS_YCbCr; /* JFIF implies YCbCr */ + + } else if (cinfo->saw_Adobe_marker) { + + switch (cinfo->Adobe_transform) { + + case 0: + + cinfo->jpeg_color_space = JCS_RGB; + + break; + + case 1: + + cinfo->jpeg_color_space = JCS_YCbCr; + + break; + + default: + + WARNMS1(cinfo, JWRN_ADOBE_XFORM, cinfo->Adobe_transform); + + cinfo->jpeg_color_space = JCS_YCbCr; /* assume it's YCbCr */ + + break; + + } + + } else { + + /* Saw no special markers, try to guess from the component IDs */ + + int cid0 = cinfo->comp_info[0].component_id; + + int cid1 = cinfo->comp_info[1].component_id; + + int cid2 = cinfo->comp_info[2].component_id; + + + + if (cid0 == 1 && cid1 == 2 && cid2 == 3) + + cinfo->jpeg_color_space = JCS_YCbCr; /* assume JFIF w/out marker */ + + else if (cid0 == 82 && cid1 == 71 && cid2 == 66) + + cinfo->jpeg_color_space = JCS_RGB; /* ASCII 'R', 'G', 'B' */ + + else { + + TRACEMS3(cinfo, 1, JTRC_UNKNOWN_IDS, cid0, cid1, cid2); + + cinfo->jpeg_color_space = JCS_YCbCr; /* assume it's YCbCr */ + + } + + } + + /* Always guess RGB is proper output colorspace. */ + + cinfo->out_color_space = JCS_RGB; + + break; + + + + case 4: + + if (cinfo->saw_Adobe_marker) { + + switch (cinfo->Adobe_transform) { + + case 0: + + cinfo->jpeg_color_space = JCS_CMYK; + + break; + + case 2: + + cinfo->jpeg_color_space = JCS_YCCK; + + break; + + default: + + WARNMS1(cinfo, JWRN_ADOBE_XFORM, cinfo->Adobe_transform); + + cinfo->jpeg_color_space = JCS_YCCK; /* assume it's YCCK */ + + break; + + } + + } else { + + /* No special markers, assume straight CMYK. */ + + cinfo->jpeg_color_space = JCS_CMYK; + + } + + cinfo->out_color_space = JCS_CMYK; + + break; + + + + default: + + cinfo->jpeg_color_space = JCS_UNKNOWN; + + cinfo->out_color_space = JCS_UNKNOWN; + + break; + + } + + + + /* Set defaults for other decompression parameters. */ + + cinfo->scale_num = 1; /* 1:1 scaling */ + + cinfo->scale_denom = 1; + + cinfo->output_gamma = 1.0; + + cinfo->buffered_image = FALSE; + + cinfo->raw_data_out = FALSE; + + cinfo->dct_method = JDCT_DEFAULT; + + cinfo->do_fancy_upsampling = TRUE; + + cinfo->do_block_smoothing = TRUE; + + cinfo->quantize_colors = FALSE; + + /* We set these in case application only sets quantize_colors. */ + + cinfo->dither_mode = JDITHER_FS; + +#ifdef QUANT_2PASS_SUPPORTED + + cinfo->two_pass_quantize = TRUE; + +#else + + cinfo->two_pass_quantize = FALSE; + +#endif + + cinfo->desired_number_of_colors = 256; + + cinfo->colormap = NULL; + + /* Initialize for no mode change in buffered-image mode. */ + + cinfo->enable_1pass_quant = FALSE; + + cinfo->enable_external_quant = FALSE; + + cinfo->enable_2pass_quant = FALSE; + +} + + + + + +/* + + * Decompression startup: read start of JPEG datastream to see what's there. + + * Need only initialize JPEG object and supply a data source before calling. + + * + + * This routine will read as far as the first SOS marker (ie, actual start of + + * compressed data), and will save all tables and parameters in the JPEG + + * object. It will also initialize the decompression parameters to default + + * values, and finally return JPEG_HEADER_OK. On return, the application may + + * adjust the decompression parameters and then call jpeg_start_decompress. + + * (Or, if the application only wanted to determine the image parameters, + + * the data need not be decompressed. In that case, call jpeg_abort or + + * jpeg_destroy to release any temporary space.) + + * If an abbreviated (tables only) datastream is presented, the routine will + + * return JPEG_HEADER_TABLES_ONLY upon reaching EOI. The application may then + + * re-use the JPEG object to read the abbreviated image datastream(s). + + * It is unnecessary (but OK) to call jpeg_abort in this case. + + * The JPEG_SUSPENDED return code only occurs if the data source module + + * requests suspension of the decompressor. In this case the application + + * should load more source data and then re-call jpeg_read_header to resume + + * processing. + + * If a non-suspending data source is used and require_image is TRUE, then the + + * return code need not be inspected since only JPEG_HEADER_OK is possible. + + * + + * This routine is now just a front end to jpeg_consume_input, with some + + * extra error checking. + + */ + + + +GLOBAL int + +jpeg_read_header (j_decompress_ptr cinfo, boolean require_image) + +{ + + int retcode; + + + + if (cinfo->global_state != DSTATE_START && + + cinfo->global_state != DSTATE_INHEADER) + + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + + + retcode = jpeg_consume_input(cinfo); + + + + switch (retcode) { + + case JPEG_REACHED_SOS: + + retcode = JPEG_HEADER_OK; + + break; + + case JPEG_REACHED_EOI: + + if (require_image) /* Complain if application wanted an image */ + + ERREXIT(cinfo, JERR_NO_IMAGE); + + /* Reset to start state; it would be safer to require the application to + + * call jpeg_abort, but we can't change it now for compatibility reasons. + + * A side effect is to free any temporary memory (there shouldn't be any). + + */ + + jpeg_abort((j_common_ptr) cinfo); /* sets state = DSTATE_START */ + + retcode = JPEG_HEADER_TABLES_ONLY; + + break; + + case JPEG_SUSPENDED: + + /* no work */ + + break; + + } + + + + return retcode; + +} + + + + + +/* + + * Consume data in advance of what the decompressor requires. + + * This can be called at any time once the decompressor object has + + * been created and a data source has been set up. + + * + + * This routine is essentially a state machine that handles a couple + + * of critical state-transition actions, namely initial setup and + + * transition from header scanning to ready-for-start_decompress. + + * All the actual input is done via the input controller's consume_input + + * method. + + */ + + + +GLOBAL int + +jpeg_consume_input (j_decompress_ptr cinfo) + +{ + + int retcode = JPEG_SUSPENDED; + + + + /* NB: every possible DSTATE value should be listed in this switch */ + + switch (cinfo->global_state) { + + case DSTATE_START: + + /* Start-of-datastream actions: reset appropriate modules */ + + (*cinfo->inputctl->reset_input_controller) (cinfo); + + /* Initialize application's data source module */ + + (*cinfo->src->init_source) (cinfo); + + cinfo->global_state = DSTATE_INHEADER; + + /*FALLTHROUGH*/ + + case DSTATE_INHEADER: + + retcode = (*cinfo->inputctl->consume_input) (cinfo); + + if (retcode == JPEG_REACHED_SOS) { /* Found SOS, prepare to decompress */ + + /* Set up default parameters based on header data */ + + default_decompress_parms(cinfo); + + /* Set global state: ready for start_decompress */ + + cinfo->global_state = DSTATE_READY; + + } + + break; + + case DSTATE_READY: + + /* Can't advance past first SOS until start_decompress is called */ + + retcode = JPEG_REACHED_SOS; + + break; + + case DSTATE_PRELOAD: + + case DSTATE_PRESCAN: + + case DSTATE_SCANNING: + + case DSTATE_RAW_OK: + + case DSTATE_BUFIMAGE: + + case DSTATE_BUFPOST: + + case DSTATE_STOPPING: + + retcode = (*cinfo->inputctl->consume_input) (cinfo); + + break; + + default: + + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + } + + return retcode; + +} + + + + + +/* + + * Have we finished reading the input file? + + */ + + + +GLOBAL boolean + +jpeg_input_complete (j_decompress_ptr cinfo) + +{ + + /* Check for valid jpeg object */ + + if (cinfo->global_state < DSTATE_START || + + cinfo->global_state > DSTATE_STOPPING) + + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + return cinfo->inputctl->eoi_reached; + +} + + + + + +/* + + * Is there more than one scan? + + */ + + + +GLOBAL boolean + +jpeg_has_multiple_scans (j_decompress_ptr cinfo) + +{ + + /* Only valid after jpeg_read_header completes */ + + if (cinfo->global_state < DSTATE_READY || + + cinfo->global_state > DSTATE_STOPPING) + + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + return cinfo->inputctl->has_multiple_scans; + +} + + + + + +/* + + * Finish JPEG decompression. + + * + + * This will normally just verify the file trailer and release temp storage. + + * + + * Returns FALSE if suspended. The return value need be inspected only if + + * a suspending data source is used. + + */ + + + +GLOBAL boolean + +jpeg_finish_decompress (j_decompress_ptr cinfo) + +{ + + if ((cinfo->global_state == DSTATE_SCANNING || + + cinfo->global_state == DSTATE_RAW_OK) && ! cinfo->buffered_image) { + + /* Terminate final pass of non-buffered mode */ + + if (cinfo->output_scanline < cinfo->output_height) + + ERREXIT(cinfo, JERR_TOO_LITTLE_DATA); + + (*cinfo->master->finish_output_pass) (cinfo); + + cinfo->global_state = DSTATE_STOPPING; + + } else if (cinfo->global_state == DSTATE_BUFIMAGE) { + + /* Finishing after a buffered-image operation */ + + cinfo->global_state = DSTATE_STOPPING; + + } else if (cinfo->global_state != DSTATE_STOPPING) { + + /* STOPPING = repeat call after a suspension, anything else is error */ + + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + } + + /* Read until EOI */ + + while (! cinfo->inputctl->eoi_reached) { + + if ((*cinfo->inputctl->consume_input) (cinfo) == JPEG_SUSPENDED) + + return FALSE; /* Suspend, come back later */ + + } + + /* Do final cleanup */ + + (*cinfo->src->term_source) (cinfo); + + /* We can use jpeg_abort to release memory and reset global_state */ + + jpeg_abort((j_common_ptr) cinfo); + + return TRUE; + +} + diff --git a/tools/urt/libs/jpeg6/jdapistd.cpp b/tools/urt/libs/jpeg6/jdapistd.cpp new file mode 100644 index 00000000..0718fb3b --- /dev/null +++ b/tools/urt/libs/jpeg6/jdapistd.cpp @@ -0,0 +1,550 @@ +/* + + * jdapistd.c + + * + + * Copyright (C) 1994-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains application interface code for the decompression half + + * of the JPEG library. These are the "standard" API routines that are + + * used in the normal full-decompression case. They are not used by a + + * transcoding-only application. Note that if an application links in + + * jpeg_start_decompress, it will end up linking in the entire decompressor. + + * We thus must separate this file from jdapimin.c to avoid linking the + + * whole decompression library into a transcoder. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "radiant_jpeglib.h" + + + + + +/* Forward declarations */ + +LOCAL boolean output_pass_setup JPP((j_decompress_ptr cinfo)); + + + + + +/* + + * Decompression initialization. + + * jpeg_read_header must be completed before calling this. + + * + + * If a multipass operating mode was selected, this will do all but the + + * last pass, and thus may take a great deal of time. + + * + + * Returns FALSE if suspended. The return value need be inspected only if + + * a suspending data source is used. + + */ + + + +GLOBAL boolean + +jpeg_start_decompress (j_decompress_ptr cinfo) + +{ + + if (cinfo->global_state == DSTATE_READY) { + + /* First call: initialize master control, select active modules */ + + jinit_master_decompress(cinfo); + + if (cinfo->buffered_image) { + + /* No more work here; expecting jpeg_start_output next */ + + cinfo->global_state = DSTATE_BUFIMAGE; + + return TRUE; + + } + + cinfo->global_state = DSTATE_PRELOAD; + + } + + if (cinfo->global_state == DSTATE_PRELOAD) { + + /* If file has multiple scans, absorb them all into the coef buffer */ + + if (cinfo->inputctl->has_multiple_scans) { + +#ifdef D_MULTISCAN_FILES_SUPPORTED + + for (;;) { + + int retcode; + + /* Call progress monitor hook if present */ + + if (cinfo->progress != NULL) + + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + + /* Absorb some more input */ + + retcode = (*cinfo->inputctl->consume_input) (cinfo); + + if (retcode == JPEG_SUSPENDED) + + return FALSE; + + if (retcode == JPEG_REACHED_EOI) + + break; + + /* Advance progress counter if appropriate */ + + if (cinfo->progress != NULL && + + (retcode == JPEG_ROW_COMPLETED || retcode == JPEG_REACHED_SOS)) { + + if (++cinfo->progress->pass_counter >= cinfo->progress->pass_limit) { + + /* jdmaster underestimated number of scans; ratchet up one scan */ + + cinfo->progress->pass_limit += (long) cinfo->total_iMCU_rows; + + } + + } + + } + +#else + + ERREXIT(cinfo, JERR_NOT_COMPILED); + +#endif /* D_MULTISCAN_FILES_SUPPORTED */ + + } + + cinfo->output_scan_number = cinfo->input_scan_number; + + } else if (cinfo->global_state != DSTATE_PRESCAN) + + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + /* Perform any dummy output passes, and set up for the final pass */ + + return output_pass_setup(cinfo); + +} + + + + + +/* + + * Set up for an output pass, and perform any dummy pass(es) needed. + + * Common subroutine for jpeg_start_decompress and jpeg_start_output. + + * Entry: global_state = DSTATE_PRESCAN only if previously suspended. + + * Exit: If done, returns TRUE and sets global_state for proper output mode. + + * If suspended, returns FALSE and sets global_state = DSTATE_PRESCAN. + + */ + + + +LOCAL boolean + +output_pass_setup (j_decompress_ptr cinfo) + +{ + + if (cinfo->global_state != DSTATE_PRESCAN) { + + /* First call: do pass setup */ + + (*cinfo->master->prepare_for_output_pass) (cinfo); + + cinfo->output_scanline = 0; + + cinfo->global_state = DSTATE_PRESCAN; + + } + + /* Loop over any required dummy passes */ + + while (cinfo->master->is_dummy_pass) { + +#ifdef QUANT_2PASS_SUPPORTED + + /* Crank through the dummy pass */ + + while (cinfo->output_scanline < cinfo->output_height) { + + JDIMENSION last_scanline; + + /* Call progress monitor hook if present */ + + if (cinfo->progress != NULL) { + + cinfo->progress->pass_counter = (long) cinfo->output_scanline; + + cinfo->progress->pass_limit = (long) cinfo->output_height; + + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + + } + + /* Process some data */ + + last_scanline = cinfo->output_scanline; + + (*cinfo->main->process_data) (cinfo, (JSAMPARRAY) NULL, + + &cinfo->output_scanline, (JDIMENSION) 0); + + if (cinfo->output_scanline == last_scanline) + + return FALSE; /* No progress made, must suspend */ + + } + + /* Finish up dummy pass, and set up for another one */ + + (*cinfo->master->finish_output_pass) (cinfo); + + (*cinfo->master->prepare_for_output_pass) (cinfo); + + cinfo->output_scanline = 0; + +#else + + ERREXIT(cinfo, JERR_NOT_COMPILED); + +#endif /* QUANT_2PASS_SUPPORTED */ + + } + + /* Ready for application to drive output pass through + + * jpeg_read_scanlines or jpeg_read_raw_data. + + */ + + cinfo->global_state = cinfo->raw_data_out ? DSTATE_RAW_OK : DSTATE_SCANNING; + + return TRUE; + +} + + + + + +/* + + * Read some scanlines of data from the JPEG decompressor. + + * + + * The return value will be the number of lines actually read. + + * This may be less than the number requested in several cases, + + * including bottom of image, data source suspension, and operating + + * modes that emit multiple scanlines at a time. + + * + + * Note: we warn about excess calls to jpeg_read_scanlines() since + + * this likely signals an application programmer error. However, + + * an oversize buffer (max_lines > scanlines remaining) is not an error. + + */ + + + +GLOBAL JDIMENSION + +jpeg_read_scanlines (j_decompress_ptr cinfo, JSAMPARRAY scanlines, + + JDIMENSION max_lines) + +{ + + JDIMENSION row_ctr; + + + + if (cinfo->global_state != DSTATE_SCANNING) + + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + if (cinfo->output_scanline >= cinfo->output_height) { + + WARNMS(cinfo, JWRN_TOO_MUCH_DATA); + + return 0; + + } + + + + /* Call progress monitor hook if present */ + + if (cinfo->progress != NULL) { + + cinfo->progress->pass_counter = (long) cinfo->output_scanline; + + cinfo->progress->pass_limit = (long) cinfo->output_height; + + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + + } + + + + /* Process some data */ + + row_ctr = 0; + + (*cinfo->main->process_data) (cinfo, scanlines, &row_ctr, max_lines); + + cinfo->output_scanline += row_ctr; + + return row_ctr; + +} + + + + + +/* + + * Alternate entry point to read raw data. + + * Processes exactly one iMCU row per call, unless suspended. + + */ + + + +GLOBAL JDIMENSION + +jpeg_read_raw_data (j_decompress_ptr cinfo, JSAMPIMAGE data, + + JDIMENSION max_lines) + +{ + + JDIMENSION lines_per_iMCU_row; + + + + if (cinfo->global_state != DSTATE_RAW_OK) + + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + if (cinfo->output_scanline >= cinfo->output_height) { + + WARNMS(cinfo, JWRN_TOO_MUCH_DATA); + + return 0; + + } + + + + /* Call progress monitor hook if present */ + + if (cinfo->progress != NULL) { + + cinfo->progress->pass_counter = (long) cinfo->output_scanline; + + cinfo->progress->pass_limit = (long) cinfo->output_height; + + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + + } + + + + /* Verify that at least one iMCU row can be returned. */ + + lines_per_iMCU_row = cinfo->max_v_samp_factor * cinfo->min_DCT_scaled_size; + + if (max_lines < lines_per_iMCU_row) + + ERREXIT(cinfo, JERR_BUFFER_SIZE); + + + + /* Decompress directly into user's buffer. */ + + if (! (*cinfo->coef->decompress_data) (cinfo, data)) + + return 0; /* suspension forced, can do nothing more */ + + + + /* OK, we processed one iMCU row. */ + + cinfo->output_scanline += lines_per_iMCU_row; + + return lines_per_iMCU_row; + +} + + + + + +/* Additional entry points for buffered-image mode. */ + + + +#ifdef D_MULTISCAN_FILES_SUPPORTED + + + +/* + + * Initialize for an output pass in buffered-image mode. + + */ + + + +GLOBAL boolean + +jpeg_start_output (j_decompress_ptr cinfo, int scan_number) + +{ + + if (cinfo->global_state != DSTATE_BUFIMAGE && + + cinfo->global_state != DSTATE_PRESCAN) + + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + /* Limit scan number to valid range */ + + if (scan_number <= 0) + + scan_number = 1; + + if (cinfo->inputctl->eoi_reached && + + scan_number > cinfo->input_scan_number) + + scan_number = cinfo->input_scan_number; + + cinfo->output_scan_number = scan_number; + + /* Perform any dummy output passes, and set up for the real pass */ + + return output_pass_setup(cinfo); + +} + + + + + +/* + + * Finish up after an output pass in buffered-image mode. + + * + + * Returns FALSE if suspended. The return value need be inspected only if + + * a suspending data source is used. + + */ + + + +GLOBAL boolean + +jpeg_finish_output (j_decompress_ptr cinfo) + +{ + + if ((cinfo->global_state == DSTATE_SCANNING || + + cinfo->global_state == DSTATE_RAW_OK) && cinfo->buffered_image) { + + /* Terminate this pass. */ + + /* We do not require the whole pass to have been completed. */ + + (*cinfo->master->finish_output_pass) (cinfo); + + cinfo->global_state = DSTATE_BUFPOST; + + } else if (cinfo->global_state != DSTATE_BUFPOST) { + + /* BUFPOST = repeat call after a suspension, anything else is error */ + + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + } + + /* Read markers looking for SOS or EOI */ + + while (cinfo->input_scan_number <= cinfo->output_scan_number && + + ! cinfo->inputctl->eoi_reached) { + + if ((*cinfo->inputctl->consume_input) (cinfo) == JPEG_SUSPENDED) + + return FALSE; /* Suspend, come back later */ + + } + + cinfo->global_state = DSTATE_BUFIMAGE; + + return TRUE; + +} + + + +#endif /* D_MULTISCAN_FILES_SUPPORTED */ + diff --git a/tools/urt/libs/jpeg6/jdatasrc.cpp b/tools/urt/libs/jpeg6/jdatasrc.cpp new file mode 100644 index 00000000..ae95d214 --- /dev/null +++ b/tools/urt/libs/jpeg6/jdatasrc.cpp @@ -0,0 +1,215 @@ +/* + * jdatasrc.c + * + * Copyright (C) 1994, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains decompression data source routines for the case of + * reading JPEG data from a file (or any stdio stream). While these routines + * are sufficient for most applications, some will want to use a different + * source manager. + * IMPORTANT: we assume that fread() will correctly transcribe an array of + * JOCTETs from 8-bit-wide elements on external storage. If char is wider + * than 8 bits on your machine, you may need to do some tweaking. + */ + + +/* this is not a core library module, so it doesn't define JPEG_INTERNALS */ +#include "jinclude.h" +#include "radiant_jpeglib.h" +#include "jerror.h" + +//extern int leo_buf_size; // FIXME ? merged in from Alpha - replaced by my_source_mgr->src_size + +/* Expanded data source object for stdio input */ + +typedef struct { + struct jpeg_source_mgr pub; /* public fields */ + int src_size; // FIXME ? merged from Alpha + unsigned char *infile; /* source stream */ + JOCTET * buffer; /* start of buffer */ + boolean start_of_file; /* have we gotten any data yet? */ +} my_source_mgr; + +typedef my_source_mgr * my_src_ptr; + +#define INPUT_BUF_SIZE 4096 /* choose an efficiently fread'able size */ + + +/* + * Initialize source --- called by jpeg_read_header + * before any data is actually read. + */ + +METHODDEF void +init_source (j_decompress_ptr cinfo) +{ + my_src_ptr src = (my_src_ptr) cinfo->src; + + /* We reset the empty-input-file flag for each image, + * but we don't clear the input buffer. + * This is correct behavior for reading a series of images from one source. + */ + src->start_of_file = TRUE; +} + + +/* + * Fill the input buffer --- called whenever buffer is emptied. + * + * In typical applications, this should read fresh data into the buffer + * (ignoring the current state of next_input_byte & bytes_in_buffer), + * reset the pointer & count to the start of the buffer, and return TRUE + * indicating that the buffer has been reloaded. It is not necessary to + * fill the buffer entirely, only to obtain at least one more byte. + * + * There is no such thing as an EOF return. If the end of the file has been + * reached, the routine has a choice of ERREXIT() or inserting fake data into + * the buffer. In most cases, generating a warning message and inserting a + * fake EOI marker is the best course of action --- this will allow the + * decompressor to output however much of the image is there. However, + * the resulting error message is misleading if the real problem is an empty + * input file, so we handle that case specially. + * + * In applications that need to be able to suspend compression due to input + * not being available yet, a FALSE return indicates that no more data can be + * obtained right now, but more may be forthcoming later. In this situation, + * the decompressor will return to its caller (with an indication of the + * number of scanlines it has read, if any). The application should resume + * decompression after it has loaded more data into the input buffer. Note + * that there are substantial restrictions on the use of suspension --- see + * the documentation. + * + * When suspending, the decompressor will back up to a convenient restart point + * (typically the start of the current MCU). next_input_byte & bytes_in_buffer + * indicate where the restart point will be if the current call returns FALSE. + * Data beyond this point must be rescanned after resumption, so move it to + * the front of the buffer rather than discarding it. + */ + +METHODDEF boolean +// FIXME ? merged in from Alpha +fill_input_buffer (j_decompress_ptr cinfo) +{ + my_src_ptr src = (my_src_ptr) cinfo->src; + size_t nbytes; + + if (src->src_size > INPUT_BUF_SIZE) + nbytes = INPUT_BUF_SIZE; + else + nbytes = src->src_size; + + memcpy (src->buffer, src->infile, nbytes); + + src->infile += nbytes; + src->src_size -= nbytes; + + src->pub.next_input_byte = src->buffer; + src->pub.bytes_in_buffer = nbytes; + src->start_of_file = FALSE; + + return TRUE; +} + + +/* + * Skip data --- used to skip over a potentially large amount of + * uninteresting data (such as an APPn marker). + * + * Writers of suspendable-input applications must note that skip_input_data + * is not granted the right to give a suspension return. If the skip extends + * beyond the data currently in the buffer, the buffer can be marked empty so + * that the next read will cause a fill_input_buffer call that can suspend. + * Arranging for additional bytes to be discarded before reloading the input + * buffer is the application writer's problem. + */ + +METHODDEF void +skip_input_data (j_decompress_ptr cinfo, long num_bytes) +{ + my_src_ptr src = (my_src_ptr) cinfo->src; + + /* Just a dumb implementation for now. Could use fseek() except + * it doesn't work on pipes. Not clear that being smart is worth + * any trouble anyway --- large skips are infrequent. + */ + if (num_bytes > 0) { + while (num_bytes > (long) src->pub.bytes_in_buffer) { + num_bytes -= (long) src->pub.bytes_in_buffer; + (void) fill_input_buffer(cinfo); + /* note we assume that fill_input_buffer will never return FALSE, + * so suspension need not be handled. + */ + } + src->pub.next_input_byte += (size_t) num_bytes; + src->pub.bytes_in_buffer -= (size_t) num_bytes; + } +} + + +/* + * An additional method that can be provided by data source modules is the + * resync_to_restart method for error recovery in the presence of RST markers. + * For the moment, this source module just uses the default resync method + * provided by the JPEG library. That method assumes that no backtracking + * is possible. + */ + + +/* + * Terminate source --- called by jpeg_finish_decompress + * after all data has been read. Often a no-op. + * + * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding + * application must deal with any cleanup that should happen even + * for error exit. + */ + +METHODDEF void +term_source (j_decompress_ptr cinfo) +{ + /* no work necessary here */ +} + + +/* + * Prepare for input from a stdio stream. + * The caller must have already opened the stream, and is responsible + * for closing it after finishing decompression. + */ + +GLOBAL void +jpeg_stdio_src (j_decompress_ptr cinfo, unsigned char *infile, int bufsize) +{ + my_src_ptr src; + + /* The source object and input buffer are made permanent so that a series + * of JPEG images can be read from the same file by calling jpeg_stdio_src + * only before the first one. (If we discarded the buffer at the end of + * one image, we'd likely lose the start of the next one.) + * This makes it unsafe to use this manager and a different source + * manager serially with the same JPEG object. Caveat programmer. + */ + if (cinfo->src == NULL) { /* first time for this JPEG object? */ + cinfo->src = (struct jpeg_source_mgr *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + SIZEOF(my_source_mgr)); + src = (my_src_ptr) cinfo->src; + src->buffer = (JOCTET *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + INPUT_BUF_SIZE * SIZEOF(JOCTET)); + } + + src = (my_src_ptr) cinfo->src; + src->pub.init_source = init_source; + src->pub.fill_input_buffer = fill_input_buffer; + src->pub.skip_input_data = skip_input_data; + src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */ + src->pub.term_source = term_source; + src->infile = infile; + src->src_size = bufsize; // FIXME ? merged from Alpha + src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */ + src->pub.next_input_byte = NULL; /* until buffer loaded */ +} + diff --git a/tools/urt/libs/jpeg6/jdcoefct.cpp b/tools/urt/libs/jpeg6/jdcoefct.cpp new file mode 100644 index 00000000..f9a1f7ec --- /dev/null +++ b/tools/urt/libs/jpeg6/jdcoefct.cpp @@ -0,0 +1,1450 @@ +/* + + * jdcoefct.c + + * + + * Copyright (C) 1994-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains the coefficient buffer controller for decompression. + + * This controller is the top level of the JPEG decompressor proper. + + * The coefficient buffer lies between entropy decoding and inverse-DCT steps. + + * + + * In buffered-image mode, this controller is the interface between + + * input-oriented processing and output-oriented processing. + + * Also, the input side (only) is used when reading a file for transcoding. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "radiant_jpeglib.h" + + + +/* Block smoothing is only applicable for progressive JPEG, so: */ + +#ifndef D_PROGRESSIVE_SUPPORTED + +#undef BLOCK_SMOOTHING_SUPPORTED + +#endif + + + +/* Private buffer controller object */ + + + +typedef struct { + + struct jpeg_d_coef_controller pub; /* public fields */ + + + + /* These variables keep track of the current location of the input side. */ + + /* cinfo->input_iMCU_row is also used for this. */ + + JDIMENSION MCU_ctr; /* counts MCUs processed in current row */ + + int MCU_vert_offset; /* counts MCU rows within iMCU row */ + + int MCU_rows_per_iMCU_row; /* number of such rows needed */ + + + + /* The output side's location is represented by cinfo->output_iMCU_row. */ + + + + /* In single-pass modes, it's sufficient to buffer just one MCU. + + * We allocate a workspace of D_MAX_BLOCKS_IN_MCU coefficient blocks, + + * and let the entropy decoder write into that workspace each time. + + * (On 80x86, the workspace is FAR even though it's not really very big; + + * this is to keep the module interfaces unchanged when a large coefficient + + * buffer is necessary.) + + * In multi-pass modes, this array points to the current MCU's blocks + + * within the virtual arrays; it is used only by the input side. + + */ + + JBLOCKROW MCU_buffer[D_MAX_BLOCKS_IN_MCU]; + + + +#ifdef D_MULTISCAN_FILES_SUPPORTED + + /* In multi-pass modes, we need a virtual block array for each component. */ + + jvirt_barray_ptr whole_image[MAX_COMPONENTS]; + +#endif + + + +#ifdef BLOCK_SMOOTHING_SUPPORTED + + /* When doing block smoothing, we latch coefficient Al values here */ + + int * coef_bits_latch; + +#define SAVED_COEFS 6 /* we save coef_bits[0..5] */ + +#endif + +} my_coef_controller; + + + +typedef my_coef_controller * my_coef_ptr; + + + +/* Forward declarations */ + +METHODDEF int decompress_onepass + + JPP((j_decompress_ptr cinfo, JSAMPIMAGE output_buf)); + +#ifdef D_MULTISCAN_FILES_SUPPORTED + +METHODDEF int decompress_data + + JPP((j_decompress_ptr cinfo, JSAMPIMAGE output_buf)); + +#endif + +#ifdef BLOCK_SMOOTHING_SUPPORTED + +LOCAL boolean smoothing_ok JPP((j_decompress_ptr cinfo)); + +METHODDEF int decompress_smooth_data + + JPP((j_decompress_ptr cinfo, JSAMPIMAGE output_buf)); + +#endif + + + + + +LOCAL void + +start_iMCU_row (j_decompress_ptr cinfo) + +/* Reset within-iMCU-row counters for a new row (input side) */ + +{ + + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + + + /* In an interleaved scan, an MCU row is the same as an iMCU row. + + * In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. + + * But at the bottom of the image, process only what's left. + + */ + + if (cinfo->comps_in_scan > 1) { + + coef->MCU_rows_per_iMCU_row = 1; + + } else { + + if (cinfo->input_iMCU_row < (cinfo->total_iMCU_rows-1)) + + coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->v_samp_factor; + + else + + coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->last_row_height; + + } + + + + coef->MCU_ctr = 0; + + coef->MCU_vert_offset = 0; + +} + + + + + +/* + + * Initialize for an input processing pass. + + */ + + + +METHODDEF void + +start_input_pass (j_decompress_ptr cinfo) + +{ + + cinfo->input_iMCU_row = 0; + + start_iMCU_row(cinfo); + +} + + + + + +/* + + * Initialize for an output processing pass. + + */ + + + +METHODDEF void + +start_output_pass (j_decompress_ptr cinfo) + +{ + +#ifdef BLOCK_SMOOTHING_SUPPORTED + + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + + + /* If multipass, check to see whether to use block smoothing on this pass */ + + if (coef->pub.coef_arrays != NULL) { + + if (cinfo->do_block_smoothing && smoothing_ok(cinfo)) + + coef->pub.decompress_data = decompress_smooth_data; + + else + + coef->pub.decompress_data = decompress_data; + + } + +#endif + + cinfo->output_iMCU_row = 0; + +} + + + + + +/* + + * Decompress and return some data in the single-pass case. + + * Always attempts to emit one fully interleaved MCU row ("iMCU" row). + + * Input and output must run in lockstep since we have only a one-MCU buffer. + + * Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED. + + * + + * NB: output_buf contains a plane for each component in image. + + * For single pass, this is the same as the components in the scan. + + */ + + + +METHODDEF int + +decompress_onepass (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) + +{ + + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + JDIMENSION MCU_col_num; /* index of current MCU within row */ + + JDIMENSION last_MCU_col = cinfo->MCUs_per_row - 1; + + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + + int blkn, ci, xindex, yindex, yoffset, useful_width; + + JSAMPARRAY output_ptr; + + JDIMENSION start_col, output_col; + + jpeg_component_info *compptr; + + inverse_DCT_method_ptr inverse_DCT; + + + + /* Loop to process as much as one whole iMCU row */ + + for (yoffset = coef->MCU_vert_offset; yoffset < coef->MCU_rows_per_iMCU_row; + + yoffset++) { + + for (MCU_col_num = coef->MCU_ctr; MCU_col_num <= last_MCU_col; + + MCU_col_num++) { + + /* Try to fetch an MCU. Entropy decoder expects buffer to be zeroed. */ + + jzero_far((void FAR *) coef->MCU_buffer[0], + + (size_t) (cinfo->blocks_in_MCU * SIZEOF(JBLOCK))); + + if (! (*cinfo->entropy->decode_mcu) (cinfo, coef->MCU_buffer)) { + + /* Suspension forced; update state counters and exit */ + + coef->MCU_vert_offset = yoffset; + + coef->MCU_ctr = MCU_col_num; + + return JPEG_SUSPENDED; + + } + + /* Determine where data should go in output_buf and do the IDCT thing. + + * We skip dummy blocks at the right and bottom edges (but blkn gets + + * incremented past them!). Note the inner loop relies on having + + * allocated the MCU_buffer[] blocks sequentially. + + */ + + blkn = 0; /* index of current DCT block within MCU */ + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + + compptr = cinfo->cur_comp_info[ci]; + + /* Don't bother to IDCT an uninteresting component. */ + + if (! compptr->component_needed) { + + blkn += compptr->MCU_blocks; + + continue; + + } + + inverse_DCT = cinfo->idct->inverse_DCT[compptr->component_index]; + + useful_width = (MCU_col_num < last_MCU_col) ? compptr->MCU_width + + : compptr->last_col_width; + + output_ptr = output_buf[ci] + yoffset * compptr->DCT_scaled_size; + + start_col = MCU_col_num * compptr->MCU_sample_width; + + for (yindex = 0; yindex < compptr->MCU_height; yindex++) { + + if (cinfo->input_iMCU_row < last_iMCU_row || + + yoffset+yindex < compptr->last_row_height) { + + output_col = start_col; + + for (xindex = 0; xindex < useful_width; xindex++) { + + (*inverse_DCT) (cinfo, compptr, + + (JCOEFPTR) coef->MCU_buffer[blkn+xindex], + + output_ptr, output_col); + + output_col += compptr->DCT_scaled_size; + + } + + } + + blkn += compptr->MCU_width; + + output_ptr += compptr->DCT_scaled_size; + + } + + } + + } + + /* Completed an MCU row, but perhaps not an iMCU row */ + + coef->MCU_ctr = 0; + + } + + /* Completed the iMCU row, advance counters for next one */ + + cinfo->output_iMCU_row++; + + if (++(cinfo->input_iMCU_row) < cinfo->total_iMCU_rows) { + + start_iMCU_row(cinfo); + + return JPEG_ROW_COMPLETED; + + } + + /* Completed the scan */ + + (*cinfo->inputctl->finish_input_pass) (cinfo); + + return JPEG_SCAN_COMPLETED; + +} + + + + + +/* + + * Dummy consume-input routine for single-pass operation. + + */ + + + +METHODDEF int + +dummy_consume_data (j_decompress_ptr cinfo) + +{ + + return JPEG_SUSPENDED; /* Always indicate nothing was done */ + +} + + + + + +#ifdef D_MULTISCAN_FILES_SUPPORTED + + + +/* + + * Consume input data and store it in the full-image coefficient buffer. + + * We read as much as one fully interleaved MCU row ("iMCU" row) per call, + + * ie, v_samp_factor block rows for each component in the scan. + + * Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED. + + */ + + + +METHODDEF int + +consume_data (j_decompress_ptr cinfo) + +{ + + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + JDIMENSION MCU_col_num; /* index of current MCU within row */ + + int blkn, ci, xindex, yindex, yoffset; + + JDIMENSION start_col; + + JBLOCKARRAY buffer[MAX_COMPS_IN_SCAN]; + + JBLOCKROW buffer_ptr; + + jpeg_component_info *compptr; + + + + /* Align the virtual buffers for the components used in this scan. */ + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + + compptr = cinfo->cur_comp_info[ci]; + + buffer[ci] = (*cinfo->mem->access_virt_barray) + + ((j_common_ptr) cinfo, coef->whole_image[compptr->component_index], + + cinfo->input_iMCU_row * compptr->v_samp_factor, + + (JDIMENSION) compptr->v_samp_factor, TRUE); + + /* Note: entropy decoder expects buffer to be zeroed, + + * but this is handled automatically by the memory manager + + * because we requested a pre-zeroed array. + + */ + + } + + + + /* Loop to process one whole iMCU row */ + + for (yoffset = coef->MCU_vert_offset; yoffset < coef->MCU_rows_per_iMCU_row; + + yoffset++) { + + for (MCU_col_num = coef->MCU_ctr; MCU_col_num < cinfo->MCUs_per_row; + + MCU_col_num++) { + + /* Construct list of pointers to DCT blocks belonging to this MCU */ + + blkn = 0; /* index of current DCT block within MCU */ + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + + compptr = cinfo->cur_comp_info[ci]; + + start_col = MCU_col_num * compptr->MCU_width; + + for (yindex = 0; yindex < compptr->MCU_height; yindex++) { + + buffer_ptr = buffer[ci][yindex+yoffset] + start_col; + + for (xindex = 0; xindex < compptr->MCU_width; xindex++) { + + coef->MCU_buffer[blkn++] = buffer_ptr++; + + } + + } + + } + + /* Try to fetch the MCU. */ + + if (! (*cinfo->entropy->decode_mcu) (cinfo, coef->MCU_buffer)) { + + /* Suspension forced; update state counters and exit */ + + coef->MCU_vert_offset = yoffset; + + coef->MCU_ctr = MCU_col_num; + + return JPEG_SUSPENDED; + + } + + } + + /* Completed an MCU row, but perhaps not an iMCU row */ + + coef->MCU_ctr = 0; + + } + + /* Completed the iMCU row, advance counters for next one */ + + if (++(cinfo->input_iMCU_row) < cinfo->total_iMCU_rows) { + + start_iMCU_row(cinfo); + + return JPEG_ROW_COMPLETED; + + } + + /* Completed the scan */ + + (*cinfo->inputctl->finish_input_pass) (cinfo); + + return JPEG_SCAN_COMPLETED; + +} + + + + + +/* + + * Decompress and return some data in the multi-pass case. + + * Always attempts to emit one fully interleaved MCU row ("iMCU" row). + + * Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED. + + * + + * NB: output_buf contains a plane for each component in image. + + */ + + + +METHODDEF int + +decompress_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) + +{ + + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + + JDIMENSION block_num; + + int ci, block_row, block_rows; + + JBLOCKARRAY buffer; + + JBLOCKROW buffer_ptr; + + JSAMPARRAY output_ptr; + + JDIMENSION output_col; + + jpeg_component_info *compptr; + + inverse_DCT_method_ptr inverse_DCT; + + + + /* Force some input to be done if we are getting ahead of the input. */ + + while (cinfo->input_scan_number < cinfo->output_scan_number || + + (cinfo->input_scan_number == cinfo->output_scan_number && + + cinfo->input_iMCU_row <= cinfo->output_iMCU_row)) { + + if ((*cinfo->inputctl->consume_input)(cinfo) == JPEG_SUSPENDED) + + return JPEG_SUSPENDED; + + } + + + + /* OK, output from the virtual arrays. */ + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + /* Don't bother to IDCT an uninteresting component. */ + + if (! compptr->component_needed) + + continue; + + /* Align the virtual buffer for this component. */ + + buffer = (*cinfo->mem->access_virt_barray) + + ((j_common_ptr) cinfo, coef->whole_image[ci], + + cinfo->output_iMCU_row * compptr->v_samp_factor, + + (JDIMENSION) compptr->v_samp_factor, FALSE); + + /* Count non-dummy DCT block rows in this iMCU row. */ + + if (cinfo->output_iMCU_row < last_iMCU_row) + + block_rows = compptr->v_samp_factor; + + else { + + /* NB: can't use last_row_height here; it is input-side-dependent! */ + + block_rows = (int) (compptr->height_in_blocks % compptr->v_samp_factor); + + if (block_rows == 0) block_rows = compptr->v_samp_factor; + + } + + inverse_DCT = cinfo->idct->inverse_DCT[ci]; + + output_ptr = output_buf[ci]; + + /* Loop over all DCT blocks to be processed. */ + + for (block_row = 0; block_row < block_rows; block_row++) { + + buffer_ptr = buffer[block_row]; + + output_col = 0; + + for (block_num = 0; block_num < compptr->width_in_blocks; block_num++) { + + (*inverse_DCT) (cinfo, compptr, (JCOEFPTR) buffer_ptr, + + output_ptr, output_col); + + buffer_ptr++; + + output_col += compptr->DCT_scaled_size; + + } + + output_ptr += compptr->DCT_scaled_size; + + } + + } + + + + if (++(cinfo->output_iMCU_row) < cinfo->total_iMCU_rows) + + return JPEG_ROW_COMPLETED; + + return JPEG_SCAN_COMPLETED; + +} + + + +#endif /* D_MULTISCAN_FILES_SUPPORTED */ + + + + + +#ifdef BLOCK_SMOOTHING_SUPPORTED + + + +/* + + * This code applies interblock smoothing as described by section K.8 + + * of the JPEG standard: the first 5 AC coefficients are estimated from + + * the DC values of a DCT block and its 8 neighboring blocks. + + * We apply smoothing only for progressive JPEG decoding, and only if + + * the coefficients it can estimate are not yet known to full precision. + + */ + + + +/* + + * Determine whether block smoothing is applicable and safe. + + * We also latch the current states of the coef_bits[] entries for the + + * AC coefficients; otherwise, if the input side of the decompressor + + * advances into a new scan, we might think the coefficients are known + + * more accurately than they really are. + + */ + + + +LOCAL boolean + +smoothing_ok (j_decompress_ptr cinfo) + +{ + + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + boolean smoothing_useful = FALSE; + + int ci, coefi; + + jpeg_component_info *compptr; + + JQUANT_TBL * qtable; + + int * coef_bits; + + int * coef_bits_latch; + + + + if (! cinfo->progressive_mode || cinfo->coef_bits == NULL) + + return FALSE; + + + + /* Allocate latch area if not already done */ + + if (coef->coef_bits_latch == NULL) + + coef->coef_bits_latch = (int *) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + cinfo->num_components * + + (SAVED_COEFS * SIZEOF(int))); + + coef_bits_latch = coef->coef_bits_latch; + + + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + /* All components' quantization values must already be latched. */ + + if ((qtable = compptr->quant_table) == NULL) + + return FALSE; + + /* Verify DC & first 5 AC quantizers are nonzero to avoid zero-divide. */ + + for (coefi = 0; coefi <= 5; coefi++) { + + if (qtable->quantval[coefi] == 0) + + return FALSE; + + } + + /* DC values must be at least partly known for all components. */ + + coef_bits = cinfo->coef_bits[ci]; + + if (coef_bits[0] < 0) + + return FALSE; + + /* Block smoothing is helpful if some AC coefficients remain inaccurate. */ + + for (coefi = 1; coefi <= 5; coefi++) { + + coef_bits_latch[coefi] = coef_bits[coefi]; + + if (coef_bits[coefi] != 0) + + smoothing_useful = TRUE; + + } + + coef_bits_latch += SAVED_COEFS; + + } + + + + return smoothing_useful; + +} + + + + + +/* + + * Variant of decompress_data for use when doing block smoothing. + + */ + + + +METHODDEF int + +decompress_smooth_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) + +{ + + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + + JDIMENSION block_num, last_block_column; + + int ci, block_row, block_rows, access_rows; + + JBLOCKARRAY buffer; + + JBLOCKROW buffer_ptr, prev_block_row, next_block_row; + + JSAMPARRAY output_ptr; + + JDIMENSION output_col; + + jpeg_component_info *compptr; + + inverse_DCT_method_ptr inverse_DCT; + + boolean first_row, last_row; + + JBLOCK workspace; + + int *coef_bits; + + JQUANT_TBL *quanttbl; + + INT32 Q00,Q01,Q02,Q10,Q11,Q20, num; + + int DC1,DC2,DC3,DC4,DC5,DC6,DC7,DC8,DC9; + + int Al, pred; + + + + /* Force some input to be done if we are getting ahead of the input. */ + + while (cinfo->input_scan_number <= cinfo->output_scan_number && + + ! cinfo->inputctl->eoi_reached) { + + if (cinfo->input_scan_number == cinfo->output_scan_number) { + + /* If input is working on current scan, we ordinarily want it to + + * have completed the current row. But if input scan is DC, + + * we want it to keep one row ahead so that next block row's DC + + * values are up to date. + + */ + + JDIMENSION delta = (cinfo->Ss == 0) ? 1 : 0; + + if (cinfo->input_iMCU_row > cinfo->output_iMCU_row+delta) + + break; + + } + + if ((*cinfo->inputctl->consume_input)(cinfo) == JPEG_SUSPENDED) + + return JPEG_SUSPENDED; + + } + + + + /* OK, output from the virtual arrays. */ + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + /* Don't bother to IDCT an uninteresting component. */ + + if (! compptr->component_needed) + + continue; + + /* Count non-dummy DCT block rows in this iMCU row. */ + + if (cinfo->output_iMCU_row < last_iMCU_row) { + + block_rows = compptr->v_samp_factor; + + access_rows = block_rows * 2; /* this and next iMCU row */ + + last_row = FALSE; + + } else { + + /* NB: can't use last_row_height here; it is input-side-dependent! */ + + block_rows = (int) (compptr->height_in_blocks % compptr->v_samp_factor); + + if (block_rows == 0) block_rows = compptr->v_samp_factor; + + access_rows = block_rows; /* this iMCU row only */ + + last_row = TRUE; + + } + + /* Align the virtual buffer for this component. */ + + if (cinfo->output_iMCU_row > 0) { + + access_rows += compptr->v_samp_factor; /* prior iMCU row too */ + + buffer = (*cinfo->mem->access_virt_barray) + + ((j_common_ptr) cinfo, coef->whole_image[ci], + + (cinfo->output_iMCU_row - 1) * compptr->v_samp_factor, + + (JDIMENSION) access_rows, FALSE); + + buffer += compptr->v_samp_factor; /* point to current iMCU row */ + + first_row = FALSE; + + } else { + + buffer = (*cinfo->mem->access_virt_barray) + + ((j_common_ptr) cinfo, coef->whole_image[ci], + + (JDIMENSION) 0, (JDIMENSION) access_rows, FALSE); + + first_row = TRUE; + + } + + /* Fetch component-dependent info */ + + coef_bits = coef->coef_bits_latch + (ci * SAVED_COEFS); + + quanttbl = compptr->quant_table; + + Q00 = quanttbl->quantval[0]; + + Q01 = quanttbl->quantval[1]; + + Q10 = quanttbl->quantval[2]; + + Q20 = quanttbl->quantval[3]; + + Q11 = quanttbl->quantval[4]; + + Q02 = quanttbl->quantval[5]; + + inverse_DCT = cinfo->idct->inverse_DCT[ci]; + + output_ptr = output_buf[ci]; + + /* Loop over all DCT blocks to be processed. */ + + for (block_row = 0; block_row < block_rows; block_row++) { + + buffer_ptr = buffer[block_row]; + + if (first_row && block_row == 0) + + prev_block_row = buffer_ptr; + + else + + prev_block_row = buffer[block_row-1]; + + if (last_row && block_row == block_rows-1) + + next_block_row = buffer_ptr; + + else + + next_block_row = buffer[block_row+1]; + + /* We fetch the surrounding DC values using a sliding-register approach. + + * Initialize all nine here so as to do the right thing on narrow pics. + + */ + + DC1 = DC2 = DC3 = (int) prev_block_row[0][0]; + + DC4 = DC5 = DC6 = (int) buffer_ptr[0][0]; + + DC7 = DC8 = DC9 = (int) next_block_row[0][0]; + + output_col = 0; + + last_block_column = compptr->width_in_blocks - 1; + + for (block_num = 0; block_num <= last_block_column; block_num++) { + + /* Fetch current DCT block into workspace so we can modify it. */ + + jcopy_block_row(buffer_ptr, (JBLOCKROW) workspace, (JDIMENSION) 1); + + /* Update DC values */ + + if (block_num < last_block_column) { + + DC3 = (int) prev_block_row[1][0]; + + DC6 = (int) buffer_ptr[1][0]; + + DC9 = (int) next_block_row[1][0]; + + } + + /* Compute coefficient estimates per K.8. + + * An estimate is applied only if coefficient is still zero, + + * and is not known to be fully accurate. + + */ + + /* AC01 */ + + if ((Al=coef_bits[1]) != 0 && workspace[1] == 0) { + + num = 36 * Q00 * (DC4 - DC6); + + if (num >= 0) { + + pred = (int) (((Q01<<7) + num) / (Q01<<8)); + + if (Al > 0 && pred >= (1< 0 && pred >= (1<= 0) { + + pred = (int) (((Q10<<7) + num) / (Q10<<8)); + + if (Al > 0 && pred >= (1< 0 && pred >= (1<= 0) { + + pred = (int) (((Q20<<7) + num) / (Q20<<8)); + + if (Al > 0 && pred >= (1< 0 && pred >= (1<= 0) { + + pred = (int) (((Q11<<7) + num) / (Q11<<8)); + + if (Al > 0 && pred >= (1< 0 && pred >= (1<= 0) { + + pred = (int) (((Q02<<7) + num) / (Q02<<8)); + + if (Al > 0 && pred >= (1< 0 && pred >= (1<DCT_scaled_size; + + } + + output_ptr += compptr->DCT_scaled_size; + + } + + } + + + + if (++(cinfo->output_iMCU_row) < cinfo->total_iMCU_rows) + + return JPEG_ROW_COMPLETED; + + return JPEG_SCAN_COMPLETED; + +} + + + +#endif /* BLOCK_SMOOTHING_SUPPORTED */ + + + + + +/* + + * Initialize coefficient buffer controller. + + */ + + + +GLOBAL void + +jinit_d_coef_controller (j_decompress_ptr cinfo, boolean need_full_buffer) + +{ + + my_coef_ptr coef; + + + + coef = (my_coef_ptr) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(my_coef_controller)); + + cinfo->coef = (struct jpeg_d_coef_controller *) coef; + + coef->pub.start_input_pass = start_input_pass; + + coef->pub.start_output_pass = start_output_pass; + +#ifdef BLOCK_SMOOTHING_SUPPORTED + + coef->coef_bits_latch = NULL; + +#endif + + + + /* Create the coefficient buffer. */ + + if (need_full_buffer) { + +#ifdef D_MULTISCAN_FILES_SUPPORTED + + /* Allocate a full-image virtual array for each component, */ + + /* padded to a multiple of samp_factor DCT blocks in each direction. */ + + /* Note we ask for a pre-zeroed array. */ + + int ci, access_rows; + + jpeg_component_info *compptr; + + + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + access_rows = compptr->v_samp_factor; + +#ifdef BLOCK_SMOOTHING_SUPPORTED + + /* If block smoothing could be used, need a bigger window */ + + if (cinfo->progressive_mode) + + access_rows *= 3; + +#endif + + coef->whole_image[ci] = (*cinfo->mem->request_virt_barray) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, TRUE, + + (JDIMENSION) jround_up((long) compptr->width_in_blocks, + + (long) compptr->h_samp_factor), + + (JDIMENSION) jround_up((long) compptr->height_in_blocks, + + (long) compptr->v_samp_factor), + + (JDIMENSION) access_rows); + + } + + coef->pub.consume_data = consume_data; + + coef->pub.decompress_data = decompress_data; + + coef->pub.coef_arrays = coef->whole_image; /* link to virtual arrays */ + +#else + + ERREXIT(cinfo, JERR_NOT_COMPILED); + +#endif + + } else { + + /* We only need a single-MCU buffer. */ + + JBLOCKROW buffer; + + int i; + + + + buffer = (JBLOCKROW) + + (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + D_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK)); + + for (i = 0; i < D_MAX_BLOCKS_IN_MCU; i++) { + + coef->MCU_buffer[i] = buffer + i; + + } + + coef->pub.consume_data = dummy_consume_data; + + coef->pub.decompress_data = decompress_onepass; + + coef->pub.coef_arrays = NULL; /* flag for no virtual arrays */ + + } + +} + diff --git a/tools/urt/libs/jpeg6/jdcolor.cpp b/tools/urt/libs/jpeg6/jdcolor.cpp new file mode 100644 index 00000000..5c173597 --- /dev/null +++ b/tools/urt/libs/jpeg6/jdcolor.cpp @@ -0,0 +1,734 @@ +/* + + * jdcolor.c + + * + + * Copyright (C) 1991-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains output colorspace conversion routines. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "radiant_jpeglib.h" + + + + + +/* Private subobject */ + + + +typedef struct { + + struct jpeg_color_deconverter pub; /* public fields */ + + + + /* Private state for YCC->RGB conversion */ + + int * Cr_r_tab; /* => table for Cr to R conversion */ + + int * Cb_b_tab; /* => table for Cb to B conversion */ + + INT32 * Cr_g_tab; /* => table for Cr to G conversion */ + + INT32 * Cb_g_tab; /* => table for Cb to G conversion */ + +} my_color_deconverter; + + + +typedef my_color_deconverter * my_cconvert_ptr; + + + + + +/**************** YCbCr -> RGB conversion: most common case **************/ + + + +/* + + * YCbCr is defined per CCIR 601-1, except that Cb and Cr are + + * normalized to the range 0..MAXJSAMPLE rather than -0.5 .. 0.5. + + * The conversion equations to be implemented are therefore + + * R = Y + 1.40200 * Cr + + * G = Y - 0.34414 * Cb - 0.71414 * Cr + + * B = Y + 1.77200 * Cb + + * where Cb and Cr represent the incoming values less CENTERJSAMPLE. + + * (These numbers are derived from TIFF 6.0 section 21, dated 3-June-92.) + + * + + * To avoid floating-point arithmetic, we represent the fractional constants + + * as integers scaled up by 2^16 (about 4 digits precision); we have to divide + + * the products by 2^16, with appropriate rounding, to get the correct answer. + + * Notice that Y, being an integral input, does not contribute any fraction + + * so it need not participate in the rounding. + + * + + * For even more speed, we avoid doing any multiplications in the inner loop + + * by precalculating the constants times Cb and Cr for all possible values. + + * For 8-bit JSAMPLEs this is very reasonable (only 256 entries per table); + + * for 12-bit samples it is still acceptable. It's not very reasonable for + + * 16-bit samples, but if you want lossless storage you shouldn't be changing + + * colorspace anyway. + + * The Cr=>R and Cb=>B values can be rounded to integers in advance; the + + * values for the G calculation are left scaled up, since we must add them + + * together before rounding. + + */ + + + +#define SCALEBITS 16 /* speediest right-shift on some machines */ + +#define ONE_HALF ((INT32) 1 << (SCALEBITS-1)) + +#define FIX(x) ((INT32) ((x) * (1L<RGB colorspace conversion. + + */ + + + +LOCAL void + +build_ycc_rgb_table (j_decompress_ptr cinfo) + +{ + + my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; + + int i; + + INT32 x; + + SHIFT_TEMPS + + + + cconvert->Cr_r_tab = (int *) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + (MAXJSAMPLE+1) * SIZEOF(int)); + + cconvert->Cb_b_tab = (int *) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + (MAXJSAMPLE+1) * SIZEOF(int)); + + cconvert->Cr_g_tab = (INT32 *) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + (MAXJSAMPLE+1) * SIZEOF(INT32)); + + cconvert->Cb_g_tab = (INT32 *) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + (MAXJSAMPLE+1) * SIZEOF(INT32)); + + + + for (i = 0, x = -CENTERJSAMPLE; i <= MAXJSAMPLE; i++, x++) { + + /* i is the actual input pixel value, in the range 0..MAXJSAMPLE */ + + /* The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE */ + + /* Cr=>R value is nearest int to 1.40200 * x */ + + cconvert->Cr_r_tab[i] = (int) + + RIGHT_SHIFT(FIX(1.40200) * x + ONE_HALF, SCALEBITS); + + /* Cb=>B value is nearest int to 1.77200 * x */ + + cconvert->Cb_b_tab[i] = (int) + + RIGHT_SHIFT(FIX(1.77200) * x + ONE_HALF, SCALEBITS); + + /* Cr=>G value is scaled-up -0.71414 * x */ + + cconvert->Cr_g_tab[i] = (- FIX(0.71414)) * x; + + /* Cb=>G value is scaled-up -0.34414 * x */ + + /* We also add in ONE_HALF so that need not do it in inner loop */ + + cconvert->Cb_g_tab[i] = (- FIX(0.34414)) * x + ONE_HALF; + + } + +} + + + + + +/* + + * Convert some rows of samples to the output colorspace. + + * + + * Note that we change from noninterleaved, one-plane-per-component format + + * to interleaved-pixel format. The output buffer is therefore three times + + * as wide as the input buffer. + + * A starting row offset is provided only for the input buffer. The caller + + * can easily adjust the passed output_buf value to accommodate any row + + * offset required on that side. + + */ + + + +METHODDEF void + +ycc_rgb_convert (j_decompress_ptr cinfo, + + JSAMPIMAGE input_buf, JDIMENSION input_row, + + JSAMPARRAY output_buf, int num_rows) + +{ + + my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; + + register int y, cb, cr; + + register JSAMPROW outptr; + + register JSAMPROW inptr0, inptr1, inptr2; + + register JDIMENSION col; + + JDIMENSION num_cols = cinfo->output_width; + + /* copy these pointers into registers if possible */ + + register JSAMPLE * range_limit = cinfo->sample_range_limit; + + register int * Crrtab = cconvert->Cr_r_tab; + + register int * Cbbtab = cconvert->Cb_b_tab; + + register INT32 * Crgtab = cconvert->Cr_g_tab; + + register INT32 * Cbgtab = cconvert->Cb_g_tab; + + SHIFT_TEMPS + + + + while (--num_rows >= 0) { + + inptr0 = input_buf[0][input_row]; + + inptr1 = input_buf[1][input_row]; + + inptr2 = input_buf[2][input_row]; + + input_row++; + + outptr = *output_buf++; + + for (col = 0; col < num_cols; col++) { + + y = GETJSAMPLE(inptr0[col]); + + cb = GETJSAMPLE(inptr1[col]); + + cr = GETJSAMPLE(inptr2[col]); + + /* Range-limiting is essential due to noise introduced by DCT losses. */ + + outptr[RGB_RED] = range_limit[y + Crrtab[cr]]; + + outptr[RGB_GREEN] = range_limit[y + + + ((int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], + + SCALEBITS))]; + + outptr[RGB_BLUE] = range_limit[y + Cbbtab[cb]]; + + outptr += RGB_PIXELSIZE; + + } + + } + +} + + + + + +/**************** Cases other than YCbCr -> RGB **************/ + + + + + +/* + + * Color conversion for no colorspace change: just copy the data, + + * converting from separate-planes to interleaved representation. + + */ + + + +METHODDEF void + +null_convert (j_decompress_ptr cinfo, + + JSAMPIMAGE input_buf, JDIMENSION input_row, + + JSAMPARRAY output_buf, int num_rows) + +{ + + register JSAMPROW inptr, outptr; + + register JDIMENSION count; + + register int num_components = cinfo->num_components; + + JDIMENSION num_cols = cinfo->output_width; + + int ci; + + + + while (--num_rows >= 0) { + + for (ci = 0; ci < num_components; ci++) { + + inptr = input_buf[ci][input_row]; + + outptr = output_buf[0] + ci; + + for (count = num_cols; count > 0; count--) { + + *outptr = *inptr++; /* needn't bother with GETJSAMPLE() here */ + + outptr += num_components; + + } + + } + + input_row++; + + output_buf++; + + } + +} + + + + + +/* + + * Color conversion for grayscale: just copy the data. + + * This also works for YCbCr -> grayscale conversion, in which + + * we just copy the Y (luminance) component and ignore chrominance. + + */ + + + +METHODDEF void + +grayscale_convert (j_decompress_ptr cinfo, + + JSAMPIMAGE input_buf, JDIMENSION input_row, + + JSAMPARRAY output_buf, int num_rows) + +{ + + jcopy_sample_rows(input_buf[0], (int) input_row, output_buf, 0, + + num_rows, cinfo->output_width); + +} + + + + + +/* + + * Adobe-style YCCK->CMYK conversion. + + * We convert YCbCr to R=1-C, G=1-M, and B=1-Y using the same + + * conversion as above, while passing K (black) unchanged. + + * We assume build_ycc_rgb_table has been called. + + */ + + + +METHODDEF void + +ycck_cmyk_convert (j_decompress_ptr cinfo, + + JSAMPIMAGE input_buf, JDIMENSION input_row, + + JSAMPARRAY output_buf, int num_rows) + +{ + + my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; + + register int y, cb, cr; + + register JSAMPROW outptr; + + register JSAMPROW inptr0, inptr1, inptr2, inptr3; + + register JDIMENSION col; + + JDIMENSION num_cols = cinfo->output_width; + + /* copy these pointers into registers if possible */ + + register JSAMPLE * range_limit = cinfo->sample_range_limit; + + register int * Crrtab = cconvert->Cr_r_tab; + + register int * Cbbtab = cconvert->Cb_b_tab; + + register INT32 * Crgtab = cconvert->Cr_g_tab; + + register INT32 * Cbgtab = cconvert->Cb_g_tab; + + SHIFT_TEMPS + + + + while (--num_rows >= 0) { + + inptr0 = input_buf[0][input_row]; + + inptr1 = input_buf[1][input_row]; + + inptr2 = input_buf[2][input_row]; + + inptr3 = input_buf[3][input_row]; + + input_row++; + + outptr = *output_buf++; + + for (col = 0; col < num_cols; col++) { + + y = GETJSAMPLE(inptr0[col]); + + cb = GETJSAMPLE(inptr1[col]); + + cr = GETJSAMPLE(inptr2[col]); + + /* Range-limiting is essential due to noise introduced by DCT losses. */ + + outptr[0] = range_limit[MAXJSAMPLE - (y + Crrtab[cr])]; /* red */ + + outptr[1] = range_limit[MAXJSAMPLE - (y + /* green */ + + ((int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], + + SCALEBITS)))]; + + outptr[2] = range_limit[MAXJSAMPLE - (y + Cbbtab[cb])]; /* blue */ + + /* K passes through unchanged */ + + outptr[3] = inptr3[col]; /* don't need GETJSAMPLE here */ + + outptr += 4; + + } + + } + +} + + + + + +/* + + * Empty method for start_pass. + + */ + + + +METHODDEF void + +start_pass_dcolor (j_decompress_ptr cinfo) + +{ + + /* no work needed */ + +} + + + + + +/* + + * Module initialization routine for output colorspace conversion. + + */ + + + +GLOBAL void + +jinit_color_deconverter (j_decompress_ptr cinfo) + +{ + + my_cconvert_ptr cconvert; + + int ci; + + + + cconvert = (my_cconvert_ptr) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(my_color_deconverter)); + + cinfo->cconvert = (struct jpeg_color_deconverter *) cconvert; + + cconvert->pub.start_pass = start_pass_dcolor; + + + + /* Make sure num_components agrees with jpeg_color_space */ + + switch (cinfo->jpeg_color_space) { + + case JCS_GRAYSCALE: + + if (cinfo->num_components != 1) + + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + + break; + + + + case JCS_RGB: + + case JCS_YCbCr: + + if (cinfo->num_components != 3) + + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + + break; + + + + case JCS_CMYK: + + case JCS_YCCK: + + if (cinfo->num_components != 4) + + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + + break; + + + + default: /* JCS_UNKNOWN can be anything */ + + if (cinfo->num_components < 1) + + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + + break; + + } + + + + /* Set out_color_components and conversion method based on requested space. + + * Also clear the component_needed flags for any unused components, + + * so that earlier pipeline stages can avoid useless computation. + + */ + + + + switch (cinfo->out_color_space) { + + case JCS_GRAYSCALE: + + cinfo->out_color_components = 1; + + if (cinfo->jpeg_color_space == JCS_GRAYSCALE || + + cinfo->jpeg_color_space == JCS_YCbCr) { + + cconvert->pub.color_convert = grayscale_convert; + + /* For color->grayscale conversion, only the Y (0) component is needed */ + + for (ci = 1; ci < cinfo->num_components; ci++) + + cinfo->comp_info[ci].component_needed = FALSE; + + } else + + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + + break; + + + + case JCS_RGB: + + cinfo->out_color_components = RGB_PIXELSIZE; + + if (cinfo->jpeg_color_space == JCS_YCbCr) { + + cconvert->pub.color_convert = ycc_rgb_convert; + + build_ycc_rgb_table(cinfo); + + } else if (cinfo->jpeg_color_space == JCS_RGB && RGB_PIXELSIZE == 3) { + + cconvert->pub.color_convert = null_convert; + + } else + + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + + break; + + + + case JCS_CMYK: + + cinfo->out_color_components = 4; + + if (cinfo->jpeg_color_space == JCS_YCCK) { + + cconvert->pub.color_convert = ycck_cmyk_convert; + + build_ycc_rgb_table(cinfo); + + } else if (cinfo->jpeg_color_space == JCS_CMYK) { + + cconvert->pub.color_convert = null_convert; + + } else + + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + + break; + + + + default: + + /* Permit null conversion to same output space */ + + if (cinfo->out_color_space == cinfo->jpeg_color_space) { + + cinfo->out_color_components = cinfo->num_components; + + cconvert->pub.color_convert = null_convert; + + } else /* unsupported non-null conversion */ + + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + + break; + + } + + + + if (cinfo->quantize_colors) + + cinfo->output_components = 1; /* single colormapped output component */ + + else + + cinfo->output_components = cinfo->out_color_components; + +} + diff --git a/tools/urt/libs/jpeg6/jdct.h b/tools/urt/libs/jpeg6/jdct.h new file mode 100644 index 00000000..cebb118e --- /dev/null +++ b/tools/urt/libs/jpeg6/jdct.h @@ -0,0 +1,352 @@ +/* + + * jdct.h + + * + + * Copyright (C) 1994, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This include file contains common declarations for the forward and + + * inverse DCT modules. These declarations are private to the DCT managers + + * (jcdctmgr.c, jddctmgr.c) and the individual DCT algorithms. + + * The individual DCT algorithms are kept in separate files to ease + + * machine-dependent tuning (e.g., assembly coding). + + */ + + + + + +/* + + * A forward DCT routine is given a pointer to a work area of type DCTELEM[]; + + * the DCT is to be performed in-place in that buffer. Type DCTELEM is int + + * for 8-bit samples, INT32 for 12-bit samples. (NOTE: Floating-point DCT + + * implementations use an array of type FAST_FLOAT, instead.) + + * The DCT inputs are expected to be signed (range +-CENTERJSAMPLE). + + * The DCT outputs are returned scaled up by a factor of 8; they therefore + + * have a range of +-8K for 8-bit data, +-128K for 12-bit data. This + + * convention improves accuracy in integer implementations and saves some + + * work in floating-point ones. + + * Quantization of the output coefficients is done by jcdctmgr.c. + + */ + + + +#if BITS_IN_JSAMPLE == 8 + +typedef int DCTELEM; /* 16 or 32 bits is fine */ + +#else + +typedef INT32 DCTELEM; /* must have 32 bits */ + +#endif + + + +typedef JMETHOD(void, forward_DCT_method_ptr, (DCTELEM * data)); + +typedef JMETHOD(void, float_DCT_method_ptr, (FAST_FLOAT * data)); + + + + + +/* + + * An inverse DCT routine is given a pointer to the input JBLOCK and a pointer + + * to an output sample array. The routine must dequantize the input data as + + * well as perform the IDCT; for dequantization, it uses the multiplier table + + * pointed to by compptr->dct_table. The output data is to be placed into the + + * sample array starting at a specified column. (Any row offset needed will + + * be applied to the array pointer before it is passed to the IDCT code.) + + * Note that the number of samples emitted by the IDCT routine is + + * DCT_scaled_size * DCT_scaled_size. + + */ + + + +/* typedef inverse_DCT_method_ptr is declared in jpegint.h */ + + + +/* + + * Each IDCT routine has its own ideas about the best dct_table element type. + + */ + + + +typedef MULTIPLIER ISLOW_MULT_TYPE; /* short or int, whichever is faster */ + +#if BITS_IN_JSAMPLE == 8 + +typedef MULTIPLIER IFAST_MULT_TYPE; /* 16 bits is OK, use short if faster */ + +#define IFAST_SCALE_BITS 2 /* fractional bits in scale factors */ + +#else + +typedef INT32 IFAST_MULT_TYPE; /* need 32 bits for scaled quantizers */ + +#define IFAST_SCALE_BITS 13 /* fractional bits in scale factors */ + +#endif + +typedef FAST_FLOAT FLOAT_MULT_TYPE; /* preferred floating type */ + + + + + +/* + + * Each IDCT routine is responsible for range-limiting its results and + + * converting them to unsigned form (0..MAXJSAMPLE). The raw outputs could + + * be quite far out of range if the input data is corrupt, so a bulletproof + + * range-limiting step is required. We use a mask-and-table-lookup method + + * to do the combined operations quickly. See the comments with + + * prepare_range_limit_table (in jdmaster.c) for more info. + + */ + + + +#define IDCT_range_limit(cinfo) ((cinfo)->sample_range_limit + CENTERJSAMPLE) + + + +#define RANGE_MASK (MAXJSAMPLE * 4 + 3) /* 2 bits wider than legal samples */ + + + + + +/* Short forms of external names for systems with brain-damaged linkers. */ + + + +#ifdef NEED_SHORT_EXTERNAL_NAMES + +#define jpeg_fdct_islow jFDislow + +#define jpeg_fdct_ifast jFDifast + +#define jpeg_fdct_float jFDfloat + +#define jpeg_idct_islow jRDislow + +#define jpeg_idct_ifast jRDifast + +#define jpeg_idct_float jRDfloat + +#define jpeg_idct_4x4 jRD4x4 + +#define jpeg_idct_2x2 jRD2x2 + +#define jpeg_idct_1x1 jRD1x1 + +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + + +/* Extern declarations for the forward and inverse DCT routines. */ + + + +EXTERN void jpeg_fdct_islow JPP((DCTELEM * data)); + +EXTERN void jpeg_fdct_ifast JPP((DCTELEM * data)); + +EXTERN void jpeg_fdct_float JPP((FAST_FLOAT * data)); + + + +EXTERN void jpeg_idct_islow + + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); + +EXTERN void jpeg_idct_ifast + + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); + +EXTERN void jpeg_idct_float + + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); + +EXTERN void jpeg_idct_4x4 + + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); + +EXTERN void jpeg_idct_2x2 + + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); + +EXTERN void jpeg_idct_1x1 + + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); + + + + + +/* + + * Macros for handling fixed-point arithmetic; these are used by many + + * but not all of the DCT/IDCT modules. + + * + + * All values are expected to be of type INT32. + + * Fractional constants are scaled left by CONST_BITS bits. + + * CONST_BITS is defined within each module using these macros, + + * and may differ from one module to the next. + + */ + + + +#define ONE ((INT32) 1) + +#define CONST_SCALE (ONE << CONST_BITS) + + + +/* Convert a positive real constant to an integer scaled by CONST_SCALE. + + * Caution: some C compilers fail to reduce "FIX(constant)" at compile time, + + * thus causing a lot of useless floating-point operations at run time. + + */ + + + +#define FIX(x) ((INT32) ((x) * CONST_SCALE + 0.5)) + + + +/* Descale and correctly round an INT32 value that's scaled by N bits. + + * We assume RIGHT_SHIFT rounds towards minus infinity, so adding + + * the fudge factor is correct for either sign of X. + + */ + + + +#define DESCALE(x,n) RIGHT_SHIFT((x) + (ONE << ((n)-1)), n) + + + +/* Multiply an INT32 variable by an INT32 constant to yield an INT32 result. + + * This macro is used only when the two inputs will actually be no more than + + * 16 bits wide, so that a 16x16->32 bit multiply can be used instead of a + + * full 32x32 multiply. This provides a useful speedup on many machines. + + * Unfortunately there is no way to specify a 16x16->32 multiply portably + + * in C, but some C compilers will do the right thing if you provide the + + * correct combination of casts. + + */ + + + +#ifdef SHORTxSHORT_32 /* may work if 'int' is 32 bits */ + +#define MULTIPLY16C16(var,const) (((INT16) (var)) * ((INT16) (const))) + +#endif + +#ifdef SHORTxLCONST_32 /* known to work with Microsoft C 6.0 */ + +#define MULTIPLY16C16(var,const) (((INT16) (var)) * ((INT32) (const))) + +#endif + + + +#ifndef MULTIPLY16C16 /* default definition */ + +#define MULTIPLY16C16(var,const) ((var) * (const)) + +#endif + + + +/* Same except both inputs are variables. */ + + + +#ifdef SHORTxSHORT_32 /* may work if 'int' is 32 bits */ + +#define MULTIPLY16V16(var1,var2) (((INT16) (var1)) * ((INT16) (var2))) + +#endif + + + +#ifndef MULTIPLY16V16 /* default definition */ + +#define MULTIPLY16V16(var1,var2) ((var1) * (var2)) + +#endif + diff --git a/tools/urt/libs/jpeg6/jddctmgr.cpp b/tools/urt/libs/jpeg6/jddctmgr.cpp new file mode 100644 index 00000000..cdf107e0 --- /dev/null +++ b/tools/urt/libs/jpeg6/jddctmgr.cpp @@ -0,0 +1,540 @@ +/* + + * jddctmgr.c + + * + + * Copyright (C) 1994-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains the inverse-DCT management logic. + + * This code selects a particular IDCT implementation to be used, + + * and it performs related housekeeping chores. No code in this file + + * is executed per IDCT step, only during output pass setup. + + * + + * Note that the IDCT routines are responsible for performing coefficient + + * dequantization as well as the IDCT proper. This module sets up the + + * dequantization multiplier table needed by the IDCT routine. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "radiant_jpeglib.h" + +#include "jdct.h" /* Private declarations for DCT subsystem */ + + + + + +/* + + * The decompressor input side (jdinput.c) saves away the appropriate + + * quantization table for each component at the start of the first scan + + * involving that component. (This is necessary in order to correctly + + * decode files that reuse Q-table slots.) + + * When we are ready to make an output pass, the saved Q-table is converted + + * to a multiplier table that will actually be used by the IDCT routine. + + * The multiplier table contents are IDCT-method-dependent. To support + + * application changes in IDCT method between scans, we can remake the + + * multiplier tables if necessary. + + * In buffered-image mode, the first output pass may occur before any data + + * has been seen for some components, and thus before their Q-tables have + + * been saved away. To handle this case, multiplier tables are preset + + * to zeroes; the result of the IDCT will be a neutral gray level. + + */ + + + + + +/* Private subobject for this module */ + + + +typedef struct { + + struct jpeg_inverse_dct pub; /* public fields */ + + + + /* This array contains the IDCT method code that each multiplier table + + * is currently set up for, or -1 if it's not yet set up. + + * The actual multiplier tables are pointed to by dct_table in the + + * per-component comp_info structures. + + */ + + int cur_method[MAX_COMPONENTS]; + +} my_idct_controller; + + + +typedef my_idct_controller * my_idct_ptr; + + + + + +/* Allocated multiplier tables: big enough for any supported variant */ + + + +typedef union { + + ISLOW_MULT_TYPE islow_array[DCTSIZE2]; + +#ifdef DCT_IFAST_SUPPORTED + + IFAST_MULT_TYPE ifast_array[DCTSIZE2]; + +#endif + +#ifdef DCT_FLOAT_SUPPORTED + + FLOAT_MULT_TYPE float_array[DCTSIZE2]; + +#endif + +} multiplier_table; + + + + + +/* The current scaled-IDCT routines require ISLOW-style multiplier tables, + + * so be sure to compile that code if either ISLOW or SCALING is requested. + + */ + +#ifdef DCT_ISLOW_SUPPORTED + +#define PROVIDE_ISLOW_TABLES + +#else + +#ifdef IDCT_SCALING_SUPPORTED + +#define PROVIDE_ISLOW_TABLES + +#endif + +#endif + + + + + +/* + + * Prepare for an output pass. + + * Here we select the proper IDCT routine for each component and build + + * a matching multiplier table. + + */ + + + +METHODDEF void + +start_pass (j_decompress_ptr cinfo) + +{ + + my_idct_ptr idct = (my_idct_ptr) cinfo->idct; + + int ci, i; + + jpeg_component_info *compptr; + + int method = 0; + + inverse_DCT_method_ptr method_ptr = NULL; + + JQUANT_TBL * qtbl; + + + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + /* Select the proper IDCT routine for this component's scaling */ + + switch (compptr->DCT_scaled_size) { + +#ifdef IDCT_SCALING_SUPPORTED + + case 1: + + method_ptr = jpeg_idct_1x1; + + method = JDCT_ISLOW; /* jidctred uses islow-style table */ + + break; + + case 2: + + method_ptr = jpeg_idct_2x2; + + method = JDCT_ISLOW; /* jidctred uses islow-style table */ + + break; + + case 4: + + method_ptr = jpeg_idct_4x4; + + method = JDCT_ISLOW; /* jidctred uses islow-style table */ + + break; + +#endif + + case DCTSIZE: + + switch (cinfo->dct_method) { + +#ifdef DCT_ISLOW_SUPPORTED + + case JDCT_ISLOW: + + method_ptr = jpeg_idct_islow; + + method = JDCT_ISLOW; + + break; + +#endif + +#ifdef DCT_IFAST_SUPPORTED + + case JDCT_IFAST: + + method_ptr = jpeg_idct_ifast; + + method = JDCT_IFAST; + + break; + +#endif + +#ifdef DCT_FLOAT_SUPPORTED + + case JDCT_FLOAT: + + method_ptr = jpeg_idct_float; + + method = JDCT_FLOAT; + + break; + +#endif + + default: + + ERREXIT(cinfo, JERR_NOT_COMPILED); + + break; + + } + + break; + + default: + + ERREXIT1(cinfo, JERR_BAD_DCTSIZE, compptr->DCT_scaled_size); + + break; + + } + + idct->pub.inverse_DCT[ci] = method_ptr; + + /* Create multiplier table from quant table. + + * However, we can skip this if the component is uninteresting + + * or if we already built the table. Also, if no quant table + + * has yet been saved for the component, we leave the + + * multiplier table all-zero; we'll be reading zeroes from the + + * coefficient controller's buffer anyway. + + */ + + if (! compptr->component_needed || idct->cur_method[ci] == method) + + continue; + + qtbl = compptr->quant_table; + + if (qtbl == NULL) /* happens if no data yet for component */ + + continue; + + idct->cur_method[ci] = method; + + switch (method) { + +#ifdef PROVIDE_ISLOW_TABLES + + case JDCT_ISLOW: + + { + + /* For LL&M IDCT method, multipliers are equal to raw quantization + + * coefficients, but are stored in natural order as ints. + + */ + + ISLOW_MULT_TYPE * ismtbl = (ISLOW_MULT_TYPE *) compptr->dct_table; + + for (i = 0; i < DCTSIZE2; i++) { + + ismtbl[i] = (ISLOW_MULT_TYPE) qtbl->quantval[jpeg_zigzag_order[i]]; + + } + + } + + break; + +#endif + +#ifdef DCT_IFAST_SUPPORTED + + case JDCT_IFAST: + + { + + /* For AA&N IDCT method, multipliers are equal to quantization + + * coefficients scaled by scalefactor[row]*scalefactor[col], where + + * scalefactor[0] = 1 + + * scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 + + * For integer operation, the multiplier table is to be scaled by + + * IFAST_SCALE_BITS. The multipliers are stored in natural order. + + */ + + IFAST_MULT_TYPE * ifmtbl = (IFAST_MULT_TYPE *) compptr->dct_table; + +#define CONST_BITS 14 + + static const INT16 aanscales[DCTSIZE2] = { + + /* precomputed values scaled up by 14 bits */ + + 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, + + 22725, 31521, 29692, 26722, 22725, 17855, 12299, 6270, + + 21407, 29692, 27969, 25172, 21407, 16819, 11585, 5906, + + 19266, 26722, 25172, 22654, 19266, 15137, 10426, 5315, + + 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, + + 12873, 17855, 16819, 15137, 12873, 10114, 6967, 3552, + + 8867, 12299, 11585, 10426, 8867, 6967, 4799, 2446, + + 4520, 6270, 5906, 5315, 4520, 3552, 2446, 1247 + + }; + + SHIFT_TEMPS + + + + for (i = 0; i < DCTSIZE2; i++) { + + ifmtbl[i] = (IFAST_MULT_TYPE) + + DESCALE(MULTIPLY16V16((INT32) qtbl->quantval[jpeg_zigzag_order[i]], + + (INT32) aanscales[i]), + + CONST_BITS-IFAST_SCALE_BITS); + + } + + } + + break; + +#endif + +#ifdef DCT_FLOAT_SUPPORTED + + case JDCT_FLOAT: + + { + + /* For float AA&N IDCT method, multipliers are equal to quantization + + * coefficients scaled by scalefactor[row]*scalefactor[col], where + + * scalefactor[0] = 1 + + * scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 + + * The multipliers are stored in natural order. + + */ + + FLOAT_MULT_TYPE * fmtbl = (FLOAT_MULT_TYPE *) compptr->dct_table; + + int row, col; + + static const double aanscalefactor[DCTSIZE] = { + + 1.0, 1.387039845, 1.306562965, 1.175875602, + + 1.0, 0.785694958, 0.541196100, 0.275899379 + + }; + + + + i = 0; + + for (row = 0; row < DCTSIZE; row++) { + + for (col = 0; col < DCTSIZE; col++) { + + fmtbl[i] = (FLOAT_MULT_TYPE) + + ((double) qtbl->quantval[jpeg_zigzag_order[i]] * + + aanscalefactor[row] * aanscalefactor[col]); + + i++; + + } + + } + + } + + break; + +#endif + + default: + + ERREXIT(cinfo, JERR_NOT_COMPILED); + + break; + + } + + } + +} + + + + + +/* + + * Initialize IDCT manager. + + */ + + + +GLOBAL void + +jinit_inverse_dct (j_decompress_ptr cinfo) + +{ + + my_idct_ptr idct; + + int ci; + + jpeg_component_info *compptr; + + + + idct = (my_idct_ptr) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(my_idct_controller)); + + cinfo->idct = (struct jpeg_inverse_dct *) idct; + + idct->pub.start_pass = start_pass; + + + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + /* Allocate and pre-zero a multiplier table for each component */ + + compptr->dct_table = + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(multiplier_table)); + + MEMZERO(compptr->dct_table, SIZEOF(multiplier_table)); + + /* Mark multiplier table not yet set up for any method */ + + idct->cur_method[ci] = -1; + + } + +} + diff --git a/tools/urt/libs/jpeg6/jdhuff.cpp b/tools/urt/libs/jpeg6/jdhuff.cpp new file mode 100644 index 00000000..4ed8bc3a --- /dev/null +++ b/tools/urt/libs/jpeg6/jdhuff.cpp @@ -0,0 +1,574 @@ +/* + * jdhuff.c + * + * Copyright (C) 1991-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains Huffman entropy decoding routines. + * + * Much of the complexity here has to do with supporting input suspension. + * If the data source module demands suspension, we want to be able to back + * up to the start of the current MCU. To do this, we copy state variables + * into local working storage, and update them back to the permanent + * storage only upon successful completion of an MCU. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "radiant_jpeglib.h" +#include "jdhuff.h" /* Declarations shared with jdphuff.c */ + + +/* + * Expanded entropy decoder object for Huffman decoding. + * + * The savable_state subrecord contains fields that change within an MCU, + * but must not be updated permanently until we complete the MCU. + */ + +typedef struct { + int last_dc_val[MAX_COMPS_IN_SCAN]; /* last DC coef for each component */ +} savable_state; + +/* This macro is to work around compilers with missing or broken + * structure assignment. You'll need to fix this code if you have + * such a compiler and you change MAX_COMPS_IN_SCAN. + */ + +#ifndef NO_STRUCT_ASSIGN +#define ASSIGN_STATE(dest,src) ((dest) = (src)) +#else +#if MAX_COMPS_IN_SCAN == 4 +#define ASSIGN_STATE(dest,src) \ + ((dest).last_dc_val[0] = (src).last_dc_val[0], \ + (dest).last_dc_val[1] = (src).last_dc_val[1], \ + (dest).last_dc_val[2] = (src).last_dc_val[2], \ + (dest).last_dc_val[3] = (src).last_dc_val[3]) +#endif +#endif + + +typedef struct { + struct jpeg_entropy_decoder pub; /* public fields */ + + /* These fields are loaded into local variables at start of each MCU. + * In case of suspension, we exit WITHOUT updating them. + */ + bitread_perm_state bitstate; /* Bit buffer at start of MCU */ + savable_state saved; /* Other state at start of MCU */ + + /* These fields are NOT loaded into local working state. */ + unsigned int restarts_to_go; /* MCUs left in this restart interval */ + + /* Pointers to derived tables (these workspaces have image lifespan) */ + d_derived_tbl * dc_derived_tbls[NUM_HUFF_TBLS]; + d_derived_tbl * ac_derived_tbls[NUM_HUFF_TBLS]; +} huff_entropy_decoder; + +typedef huff_entropy_decoder * huff_entropy_ptr; + + +/* + * Initialize for a Huffman-compressed scan. + */ + +METHODDEF void +start_pass_huff_decoder (j_decompress_ptr cinfo) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + int ci, dctbl, actbl; + jpeg_component_info * compptr; + + /* Check that the scan parameters Ss, Se, Ah/Al are OK for sequential JPEG. + * This ought to be an error condition, but we make it a warning because + * there are some baseline files out there with all zeroes in these bytes. + */ + if (cinfo->Ss != 0 || cinfo->Se != DCTSIZE2-1 || + cinfo->Ah != 0 || cinfo->Al != 0) + WARNMS(cinfo, JWRN_NOT_SEQUENTIAL); + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + dctbl = compptr->dc_tbl_no; + actbl = compptr->ac_tbl_no; + /* Make sure requested tables are present */ + if (dctbl < 0 || dctbl >= NUM_HUFF_TBLS || + cinfo->dc_huff_tbl_ptrs[dctbl] == NULL) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, dctbl); + if (actbl < 0 || actbl >= NUM_HUFF_TBLS || + cinfo->ac_huff_tbl_ptrs[actbl] == NULL) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, actbl); + /* Compute derived values for Huffman tables */ + /* We may do this more than once for a table, but it's not expensive */ + jpeg_make_d_derived_tbl(cinfo, cinfo->dc_huff_tbl_ptrs[dctbl], + & entropy->dc_derived_tbls[dctbl]); + jpeg_make_d_derived_tbl(cinfo, cinfo->ac_huff_tbl_ptrs[actbl], + & entropy->ac_derived_tbls[actbl]); + /* Initialize DC predictions to 0 */ + entropy->saved.last_dc_val[ci] = 0; + } + + /* Initialize bitread state variables */ + entropy->bitstate.bits_left = 0; + entropy->bitstate.get_buffer = 0; /* unnecessary, but keeps Purify quiet */ + entropy->bitstate.printed_eod = FALSE; + + /* Initialize restart counter */ + entropy->restarts_to_go = cinfo->restart_interval; +} + + +/* + * Compute the derived values for a Huffman table. + * Note this is also used by jdphuff.c. + */ + +GLOBAL void +jpeg_make_d_derived_tbl (j_decompress_ptr cinfo, JHUFF_TBL * htbl, + d_derived_tbl ** pdtbl) +{ + d_derived_tbl *dtbl; + int p, i, l, si; + int lookbits, ctr; + char huffsize[257]; + unsigned int huffcode[257]; + unsigned int code; + + /* Allocate a workspace if we haven't already done so. */ + if (*pdtbl == NULL) + *pdtbl = (d_derived_tbl *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(d_derived_tbl)); + dtbl = *pdtbl; + dtbl->pub = htbl; /* fill in back link */ + + /* Figure C.1: make table of Huffman code length for each symbol */ + /* Note that this is in code-length order. */ + + p = 0; + for (l = 1; l <= 16; l++) { + for (i = 1; i <= (int) htbl->bits[l]; i++) + huffsize[p++] = (char) l; + } + huffsize[p] = 0; + + /* Figure C.2: generate the codes themselves */ + /* Note that this is in code-length order. */ + + code = 0; + si = huffsize[0]; + p = 0; + while (huffsize[p]) { + while (((int) huffsize[p]) == si) { + huffcode[p++] = code; + code++; + } + code <<= 1; + si++; + } + + /* Figure F.15: generate decoding tables for bit-sequential decoding */ + + p = 0; + for (l = 1; l <= 16; l++) { + if (htbl->bits[l]) { + dtbl->valptr[l] = p; /* huffval[] index of 1st symbol of code length l */ + dtbl->mincode[l] = huffcode[p]; /* minimum code of length l */ + p += htbl->bits[l]; + dtbl->maxcode[l] = huffcode[p-1]; /* maximum code of length l */ + } else { + dtbl->maxcode[l] = -1; /* -1 if no codes of this length */ + } + } + dtbl->maxcode[17] = 0xFFFFFL; /* ensures jpeg_huff_decode terminates */ + + /* Compute lookahead tables to speed up decoding. + * First we set all the table entries to 0, indicating "too long"; + * then we iterate through the Huffman codes that are short enough and + * fill in all the entries that correspond to bit sequences starting + * with that code. + */ + + MEMZERO(dtbl->look_nbits, SIZEOF(dtbl->look_nbits)); + + p = 0; + for (l = 1; l <= HUFF_LOOKAHEAD; l++) { + for (i = 1; i <= (int) htbl->bits[l]; i++, p++) { + /* l = current code's length, p = its index in huffcode[] & huffval[]. */ + /* Generate left-justified code followed by all possible bit sequences */ + lookbits = huffcode[p] << (HUFF_LOOKAHEAD-l); + for (ctr = 1 << (HUFF_LOOKAHEAD-l); ctr > 0; ctr--) { + dtbl->look_nbits[lookbits] = l; + dtbl->look_sym[lookbits] = htbl->huffval[p]; + lookbits++; + } + } + } +} + + +/* + * Out-of-line code for bit fetching (shared with jdphuff.c). + * See jdhuff.h for info about usage. + * Note: current values of get_buffer and bits_left are passed as parameters, + * but are returned in the corresponding fields of the state struct. + * + * On most machines MIN_GET_BITS should be 25 to allow the full 32-bit width + * of get_buffer to be used. (On machines with wider words, an even larger + * buffer could be used.) However, on some machines 32-bit shifts are + * quite slow and take time proportional to the number of places shifted. + * (This is true with most PC compilers, for instance.) In this case it may + * be a win to set MIN_GET_BITS to the minimum value of 15. This reduces the + * average shift distance at the cost of more calls to jpeg_fill_bit_buffer. + */ + +#ifdef SLOW_SHIFT_32 +#define MIN_GET_BITS 15 /* minimum allowable value */ +#else +#define MIN_GET_BITS (BIT_BUF_SIZE-7) +#endif + + +GLOBAL boolean +jpeg_fill_bit_buffer (bitread_working_state * state, + register bit_buf_type get_buffer, register int bits_left, + int nbits) +/* Load up the bit buffer to a depth of at least nbits */ +{ + /* Copy heavily used state fields into locals (hopefully registers) */ + register const JOCTET * next_input_byte = state->next_input_byte; + register size_t bytes_in_buffer = state->bytes_in_buffer; + register int c; + + /* Attempt to load at least MIN_GET_BITS bits into get_buffer. */ + /* (It is assumed that no request will be for more than that many bits.) */ + + while (bits_left < MIN_GET_BITS) { + /* Attempt to read a byte */ + if (state->unread_marker != 0) + goto no_more_data; /* can't advance past a marker */ + + if (bytes_in_buffer == 0) { + if (! (*state->cinfo->src->fill_input_buffer) (state->cinfo)) + return FALSE; + next_input_byte = state->cinfo->src->next_input_byte; + bytes_in_buffer = state->cinfo->src->bytes_in_buffer; + } + bytes_in_buffer--; + c = GETJOCTET(*next_input_byte++); + + /* If it's 0xFF, check and discard stuffed zero byte */ + if (c == 0xFF) { + do { + if (bytes_in_buffer == 0) { + if (! (*state->cinfo->src->fill_input_buffer) (state->cinfo)) + return FALSE; + next_input_byte = state->cinfo->src->next_input_byte; + bytes_in_buffer = state->cinfo->src->bytes_in_buffer; + } + bytes_in_buffer--; + c = GETJOCTET(*next_input_byte++); + } while (c == 0xFF); + + if (c == 0) { + /* Found FF/00, which represents an FF data byte */ + c = 0xFF; + } else { + /* Oops, it's actually a marker indicating end of compressed data. */ + /* Better put it back for use later */ + state->unread_marker = c; + + no_more_data: + /* There should be enough bits still left in the data segment; */ + /* if so, just break out of the outer while loop. */ + if (bits_left >= nbits) + break; + /* Uh-oh. Report corrupted data to user and stuff zeroes into + * the data stream, so that we can produce some kind of image. + * Note that this code will be repeated for each byte demanded + * for the rest of the segment. We use a nonvolatile flag to ensure + * that only one warning message appears. + */ + if (! *(state->printed_eod_ptr)) { + WARNMS(state->cinfo, JWRN_HIT_MARKER); + *(state->printed_eod_ptr) = TRUE; + } + c = 0; /* insert a zero byte into bit buffer */ + } + } + + /* OK, load c into get_buffer */ + get_buffer = (get_buffer << 8) | c; + bits_left += 8; + } + + /* Unload the local registers */ + state->next_input_byte = next_input_byte; + state->bytes_in_buffer = bytes_in_buffer; + state->get_buffer = get_buffer; + state->bits_left = bits_left; + + return TRUE; +} + + +/* + * Out-of-line code for Huffman code decoding. + * See jdhuff.h for info about usage. + */ + +GLOBAL int +jpeg_huff_decode (bitread_working_state * state, + register bit_buf_type get_buffer, register int bits_left, + d_derived_tbl * htbl, int min_bits) +{ + register int l = min_bits; + register INT32 code; + + /* HUFF_DECODE has determined that the code is at least min_bits */ + /* bits long, so fetch that many bits in one swoop. */ + + CHECK_BIT_BUFFER(*state, l, return -1); + code = GET_BITS(l); + + /* Collect the rest of the Huffman code one bit at a time. */ + /* This is per Figure F.16 in the JPEG spec. */ + + while (code > htbl->maxcode[l]) { + code <<= 1; + CHECK_BIT_BUFFER(*state, 1, return -1); + code |= GET_BITS(1); + l++; + } + + /* Unload the local registers */ + state->get_buffer = get_buffer; + state->bits_left = bits_left; + + /* With garbage input we may reach the sentinel value l = 17. */ + + if (l > 16) { + WARNMS(state->cinfo, JWRN_HUFF_BAD_CODE); + return 0; /* fake a zero as the safest result */ + } + + return htbl->pub->huffval[ htbl->valptr[l] + + ((int) (code - htbl->mincode[l])) ]; +} + + +/* + * Figure F.12: extend sign bit. + * On some machines, a shift and add will be faster than a table lookup. + */ + +#ifdef AVOID_TABLES + +#define HUFF_EXTEND(x,s) ((x) < (1<<((s)-1)) ? (x) + (((-1)<<(s)) + 1) : (x)) + +#else + +#define HUFF_EXTEND(x,s) ((x) < extend_test[s] ? (x) + extend_offset[s] : (x)) + +static const int extend_test[16] = /* entry n is 2**(n-1) */ + { 0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, + 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000 }; + +static const int extend_offset[16] = /* entry n is (-1 << n) + 1 */ + { 0, ((-1)<<1) + 1, ((-1)<<2) + 1, ((-1)<<3) + 1, ((-1)<<4) + 1, + ((-1)<<5) + 1, ((-1)<<6) + 1, ((-1)<<7) + 1, ((-1)<<8) + 1, + ((-1)<<9) + 1, ((-1)<<10) + 1, ((-1)<<11) + 1, ((-1)<<12) + 1, + ((-1)<<13) + 1, ((-1)<<14) + 1, ((-1)<<15) + 1 }; + +#endif /* AVOID_TABLES */ + + +/* + * Check for a restart marker & resynchronize decoder. + * Returns FALSE if must suspend. + */ + +LOCAL boolean +process_restart (j_decompress_ptr cinfo) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + int ci; + + /* Throw away any unused bits remaining in bit buffer; */ + /* include any full bytes in next_marker's count of discarded bytes */ + cinfo->marker->discarded_bytes += entropy->bitstate.bits_left / 8; + entropy->bitstate.bits_left = 0; + + /* Advance past the RSTn marker */ + if (! (*cinfo->marker->read_restart_marker) (cinfo)) + return FALSE; + + /* Re-initialize DC predictions to 0 */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) + entropy->saved.last_dc_val[ci] = 0; + + /* Reset restart counter */ + entropy->restarts_to_go = cinfo->restart_interval; + + /* Next segment can get another out-of-data warning */ + entropy->bitstate.printed_eod = FALSE; + + return TRUE; +} + + +/* + * Decode and return one MCU's worth of Huffman-compressed coefficients. + * The coefficients are reordered from zigzag order into natural array order, + * but are not dequantized. + * + * The i'th block of the MCU is stored into the block pointed to by + * MCU_data[i]. WE ASSUME THIS AREA HAS BEEN ZEROED BY THE CALLER. + * (Wholesale zeroing is usually a little faster than retail...) + * + * Returns FALSE if data source requested suspension. In that case no + * changes have been made to permanent state. (Exception: some output + * coefficients may already have been assigned. This is harmless for + * this module, since we'll just re-assign them on the next call.) + */ + +METHODDEF boolean +decode_mcu (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + register int s, k, r; + int blkn, ci; + JBLOCKROW block; + BITREAD_STATE_VARS; + savable_state state; + d_derived_tbl * dctbl; + d_derived_tbl * actbl; + jpeg_component_info * compptr; + + /* Process restart marker if needed; may have to suspend */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) + if (! process_restart(cinfo)) + return FALSE; + } + + /* Load up working state */ + BITREAD_LOAD_STATE(cinfo,entropy->bitstate); + ASSIGN_STATE(state, entropy->saved); + + /* Outer loop handles each block in the MCU */ + + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + block = MCU_data[blkn]; + ci = cinfo->MCU_membership[blkn]; + compptr = cinfo->cur_comp_info[ci]; + dctbl = entropy->dc_derived_tbls[compptr->dc_tbl_no]; + actbl = entropy->ac_derived_tbls[compptr->ac_tbl_no]; + + /* Decode a single block's worth of coefficients */ + + /* Section F.2.2.1: decode the DC coefficient difference */ + HUFF_DECODE(s, br_state, dctbl, return FALSE, label1); + if (s) { + CHECK_BIT_BUFFER(br_state, s, return FALSE); + r = GET_BITS(s); + s = HUFF_EXTEND(r, s); + } + + /* Shortcut if component's values are not interesting */ + if (! compptr->component_needed) + goto skip_ACs; + + /* Convert DC difference to actual value, update last_dc_val */ + s += state.last_dc_val[ci]; + state.last_dc_val[ci] = s; + /* Output the DC coefficient (assumes jpeg_natural_order[0] = 0) */ + (*block)[0] = (JCOEF) s; + + /* Do we need to decode the AC coefficients for this component? */ + if (compptr->DCT_scaled_size > 1) { + + /* Section F.2.2.2: decode the AC coefficients */ + /* Since zeroes are skipped, output area must be cleared beforehand */ + for (k = 1; k < DCTSIZE2; k++) { + HUFF_DECODE(s, br_state, actbl, return FALSE, label2); + + r = s >> 4; + s &= 15; + + if (s) { + k += r; + CHECK_BIT_BUFFER(br_state, s, return FALSE); + r = GET_BITS(s); + s = HUFF_EXTEND(r, s); + /* Output coefficient in natural (dezigzagged) order. + * Note: the extra entries in jpeg_natural_order[] will save us + * if k >= DCTSIZE2, which could happen if the data is corrupted. + */ + (*block)[jpeg_natural_order[k]] = (JCOEF) s; + } else { + if (r != 15) + break; + k += 15; + } + } + + } else { +skip_ACs: + + /* Section F.2.2.2: decode the AC coefficients */ + /* In this path we just discard the values */ + for (k = 1; k < DCTSIZE2; k++) { + HUFF_DECODE(s, br_state, actbl, return FALSE, label3); + + r = s >> 4; + s &= 15; + + if (s) { + k += r; + CHECK_BIT_BUFFER(br_state, s, return FALSE); + DROP_BITS(s); + } else { + if (r != 15) + break; + k += 15; + } + } + + } + } + + /* Completed MCU, so update state */ + BITREAD_SAVE_STATE(cinfo,entropy->bitstate); + ASSIGN_STATE(entropy->saved, state); + + /* Account for restart interval (no-op if not using restarts) */ + entropy->restarts_to_go--; + + return TRUE; +} + + +/* + * Module initialization routine for Huffman entropy decoding. + */ + +GLOBAL void +jinit_huff_decoder (j_decompress_ptr cinfo) +{ + huff_entropy_ptr entropy; + int i; + + entropy = (huff_entropy_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(huff_entropy_decoder)); + cinfo->entropy = (struct jpeg_entropy_decoder *) entropy; + entropy->pub.start_pass = start_pass_huff_decoder; + entropy->pub.decode_mcu = decode_mcu; + + /* Mark tables unallocated */ + for (i = 0; i < NUM_HUFF_TBLS; i++) { + entropy->dc_derived_tbls[i] = entropy->ac_derived_tbls[i] = NULL; + } +} diff --git a/tools/urt/libs/jpeg6/jdhuff.h b/tools/urt/libs/jpeg6/jdhuff.h new file mode 100644 index 00000000..65f3054f --- /dev/null +++ b/tools/urt/libs/jpeg6/jdhuff.h @@ -0,0 +1,202 @@ +/* + * jdhuff.h + * + * Copyright (C) 1991-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains declarations for Huffman entropy decoding routines + * that are shared between the sequential decoder (jdhuff.c) and the + * progressive decoder (jdphuff.c). No other modules need to see these. + */ + +/* Short forms of external names for systems with brain-damaged linkers. */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_make_d_derived_tbl jMkDDerived +#define jpeg_fill_bit_buffer jFilBitBuf +#define jpeg_huff_decode jHufDecode +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + +/* Derived data constructed for each Huffman table */ + +#define HUFF_LOOKAHEAD 8 /* # of bits of lookahead */ + +typedef struct { + /* Basic tables: (element [0] of each array is unused) */ + INT32 mincode[17]; /* smallest code of length k */ + INT32 maxcode[18]; /* largest code of length k (-1 if none) */ + /* (maxcode[17] is a sentinel to ensure jpeg_huff_decode terminates) */ + int valptr[17]; /* huffval[] index of 1st symbol of length k */ + + /* Link to public Huffman table (needed only in jpeg_huff_decode) */ + JHUFF_TBL *pub; + + /* Lookahead tables: indexed by the next HUFF_LOOKAHEAD bits of + * the input data stream. If the next Huffman code is no more + * than HUFF_LOOKAHEAD bits long, we can obtain its length and + * the corresponding symbol directly from these tables. + */ + int look_nbits[1< 32 bits on your machine, and shifting/masking longs is + * reasonably fast, making bit_buf_type be long and setting BIT_BUF_SIZE + * appropriately should be a win. Unfortunately we can't do this with + * something like #define BIT_BUF_SIZE (sizeof(bit_buf_type)*8) + * because not all machines measure sizeof in 8-bit bytes. + */ + +typedef struct { /* Bitreading state saved across MCUs */ + bit_buf_type get_buffer; /* current bit-extraction buffer */ + int bits_left; /* # of unused bits in it */ + boolean printed_eod; /* flag to suppress multiple warning msgs */ +} bitread_perm_state; + +typedef struct { /* Bitreading working state within an MCU */ + /* current data source state */ + const JOCTET * next_input_byte; /* => next byte to read from source */ + size_t bytes_in_buffer; /* # of bytes remaining in source buffer */ + int unread_marker; /* nonzero if we have hit a marker */ + /* bit input buffer --- note these values are kept in register variables, + * not in this struct, inside the inner loops. + */ + bit_buf_type get_buffer; /* current bit-extraction buffer */ + int bits_left; /* # of unused bits in it */ + /* pointers needed by jpeg_fill_bit_buffer */ + j_decompress_ptr cinfo; /* back link to decompress master record */ + boolean * printed_eod_ptr; /* => flag in permanent state */ +} bitread_working_state; + +/* Macros to declare and load/save bitread local variables. */ +#define BITREAD_STATE_VARS \ + register bit_buf_type get_buffer; \ + register int bits_left; \ + bitread_working_state br_state + +#define BITREAD_LOAD_STATE(cinfop,permstate) \ + br_state.cinfo = cinfop; \ + br_state.next_input_byte = cinfop->src->next_input_byte; \ + br_state.bytes_in_buffer = cinfop->src->bytes_in_buffer; \ + br_state.unread_marker = cinfop->unread_marker; \ + get_buffer = permstate.get_buffer; \ + bits_left = permstate.bits_left; \ + br_state.printed_eod_ptr = & permstate.printed_eod + +#define BITREAD_SAVE_STATE(cinfop,permstate) \ + cinfop->src->next_input_byte = br_state.next_input_byte; \ + cinfop->src->bytes_in_buffer = br_state.bytes_in_buffer; \ + cinfop->unread_marker = br_state.unread_marker; \ + permstate.get_buffer = get_buffer; \ + permstate.bits_left = bits_left + +/* + * These macros provide the in-line portion of bit fetching. + * Use CHECK_BIT_BUFFER to ensure there are N bits in get_buffer + * before using GET_BITS, PEEK_BITS, or DROP_BITS. + * The variables get_buffer and bits_left are assumed to be locals, + * but the state struct might not be (jpeg_huff_decode needs this). + * CHECK_BIT_BUFFER(state,n,action); + * Ensure there are N bits in get_buffer; if suspend, take action. + * val = GET_BITS(n); + * Fetch next N bits. + * val = PEEK_BITS(n); + * Fetch next N bits without removing them from the buffer. + * DROP_BITS(n); + * Discard next N bits. + * The value N should be a simple variable, not an expression, because it + * is evaluated multiple times. + */ + +#define CHECK_BIT_BUFFER(state,nbits,action) \ + { if (bits_left < (nbits)) { \ + if (! jpeg_fill_bit_buffer(&(state),get_buffer,bits_left,nbits)) \ + { action; } \ + get_buffer = (state).get_buffer; bits_left = (state).bits_left; } } + +#define GET_BITS(nbits) \ + (((int) (get_buffer >> (bits_left -= (nbits)))) & ((1<<(nbits))-1)) + +#define PEEK_BITS(nbits) \ + (((int) (get_buffer >> (bits_left - (nbits)))) & ((1<<(nbits))-1)) + +#define DROP_BITS(nbits) \ + (bits_left -= (nbits)) + +/* Load up the bit buffer to a depth of at least nbits */ +EXTERN boolean jpeg_fill_bit_buffer JPP((bitread_working_state * state, + register bit_buf_type get_buffer, register int bits_left, + int nbits)); + + +/* + * Code for extracting next Huffman-coded symbol from input bit stream. + * Again, this is time-critical and we make the main paths be macros. + * + * We use a lookahead table to process codes of up to HUFF_LOOKAHEAD bits + * without looping. Usually, more than 95% of the Huffman codes will be 8 + * or fewer bits long. The few overlength codes are handled with a loop, + * which need not be inline code. + * + * Notes about the HUFF_DECODE macro: + * 1. Near the end of the data segment, we may fail to get enough bits + * for a lookahead. In that case, we do it the hard way. + * 2. If the lookahead table contains no entry, the next code must be + * more than HUFF_LOOKAHEAD bits long. + * 3. jpeg_huff_decode returns -1 if forced to suspend. + */ + +#define HUFF_DECODE(result,state,htbl,failaction,slowlabel) \ +{ register int nb, look; \ + if (bits_left < HUFF_LOOKAHEAD) { \ + if (! jpeg_fill_bit_buffer(&state,get_buffer,bits_left, 0)) {failaction;} \ + get_buffer = state.get_buffer; bits_left = state.bits_left; \ + if (bits_left < HUFF_LOOKAHEAD) { \ + nb = 1; goto slowlabel; \ + } \ + } \ + look = PEEK_BITS(HUFF_LOOKAHEAD); \ + if ((nb = htbl->look_nbits[look]) != 0) { \ + DROP_BITS(nb); \ + result = htbl->look_sym[look]; \ + } else { \ + nb = HUFF_LOOKAHEAD+1; \ +slowlabel: \ + if ((result=jpeg_huff_decode(&state,get_buffer,bits_left,htbl,nb)) < 0) \ + { failaction; } \ + get_buffer = state.get_buffer; bits_left = state.bits_left; \ + } \ +} + +/* Out-of-line case for Huffman code fetching */ +EXTERN int jpeg_huff_decode JPP((bitread_working_state * state, + register bit_buf_type get_buffer, register int bits_left, + d_derived_tbl * htbl, int min_bits)); diff --git a/tools/urt/libs/jpeg6/jdinput.cpp b/tools/urt/libs/jpeg6/jdinput.cpp new file mode 100644 index 00000000..4def2162 --- /dev/null +++ b/tools/urt/libs/jpeg6/jdinput.cpp @@ -0,0 +1,762 @@ +/* + + * jdinput.c + + * + + * Copyright (C) 1991-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains input control logic for the JPEG decompressor. + + * These routines are concerned with controlling the decompressor's input + + * processing (marker reading and coefficient decoding). The actual input + + * reading is done in jdmarker.c, jdhuff.c, and jdphuff.c. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "radiant_jpeglib.h" + + + + + +/* Private state */ + + + +typedef struct { + + struct jpeg_input_controller pub; /* public fields */ + + + + boolean inheaders; /* TRUE until first SOS is reached */ + +} my_input_controller; + + + +typedef my_input_controller * my_inputctl_ptr; + + + + + +/* Forward declarations */ + +METHODDEF int consume_markers JPP((j_decompress_ptr cinfo)); + + + + + +/* + + * Routines to calculate various quantities related to the size of the image. + + */ + + + +LOCAL void + +initial_setup (j_decompress_ptr cinfo) + +/* Called once, when first SOS marker is reached */ + +{ + + int ci; + + jpeg_component_info *compptr; + + + + /* Make sure image isn't bigger than I can handle */ + + if ((long) cinfo->image_height > (long) JPEG_MAX_DIMENSION || + + (long) cinfo->image_width > (long) JPEG_MAX_DIMENSION) + + ERREXIT1(cinfo, JERR_IMAGE_TOO_BIG, (unsigned int) JPEG_MAX_DIMENSION); + + + + /* For now, precision must match compiled-in value... */ + + if (cinfo->data_precision != BITS_IN_JSAMPLE) + + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + + + + /* Check that number of components won't exceed internal array sizes */ + + if (cinfo->num_components > MAX_COMPONENTS) + + ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->num_components, + + MAX_COMPONENTS); + + + + /* Compute maximum sampling factors; check factor validity */ + + cinfo->max_h_samp_factor = 1; + + cinfo->max_v_samp_factor = 1; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + if (compptr->h_samp_factor<=0 || compptr->h_samp_factor>MAX_SAMP_FACTOR || + + compptr->v_samp_factor<=0 || compptr->v_samp_factor>MAX_SAMP_FACTOR) + + ERREXIT(cinfo, JERR_BAD_SAMPLING); + + cinfo->max_h_samp_factor = MAX(cinfo->max_h_samp_factor, + + compptr->h_samp_factor); + + cinfo->max_v_samp_factor = MAX(cinfo->max_v_samp_factor, + + compptr->v_samp_factor); + + } + + + + /* We initialize DCT_scaled_size and min_DCT_scaled_size to DCTSIZE. + + * In the full decompressor, this will be overridden by jdmaster.c; + + * but in the transcoder, jdmaster.c is not used, so we must do it here. + + */ + + cinfo->min_DCT_scaled_size = DCTSIZE; + + + + /* Compute dimensions of components */ + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + compptr->DCT_scaled_size = DCTSIZE; + + /* Size in DCT blocks */ + + compptr->width_in_blocks = (JDIMENSION) + + jdiv_round_up((long) cinfo->image_width * (long) compptr->h_samp_factor, + + (long) (cinfo->max_h_samp_factor * DCTSIZE)); + + compptr->height_in_blocks = (JDIMENSION) + + jdiv_round_up((long) cinfo->image_height * (long) compptr->v_samp_factor, + + (long) (cinfo->max_v_samp_factor * DCTSIZE)); + + /* downsampled_width and downsampled_height will also be overridden by + + * jdmaster.c if we are doing full decompression. The transcoder library + + * doesn't use these values, but the calling application might. + + */ + + /* Size in samples */ + + compptr->downsampled_width = (JDIMENSION) + + jdiv_round_up((long) cinfo->image_width * (long) compptr->h_samp_factor, + + (long) cinfo->max_h_samp_factor); + + compptr->downsampled_height = (JDIMENSION) + + jdiv_round_up((long) cinfo->image_height * (long) compptr->v_samp_factor, + + (long) cinfo->max_v_samp_factor); + + /* Mark component needed, until color conversion says otherwise */ + + compptr->component_needed = TRUE; + + /* Mark no quantization table yet saved for component */ + + compptr->quant_table = NULL; + + } + + + + /* Compute number of fully interleaved MCU rows. */ + + cinfo->total_iMCU_rows = (JDIMENSION) + + jdiv_round_up((long) cinfo->image_height, + + (long) (cinfo->max_v_samp_factor*DCTSIZE)); + + + + /* Decide whether file contains multiple scans */ + + if (cinfo->comps_in_scan < cinfo->num_components || cinfo->progressive_mode) + + cinfo->inputctl->has_multiple_scans = TRUE; + + else + + cinfo->inputctl->has_multiple_scans = FALSE; + +} + + + + + +LOCAL void + +per_scan_setup (j_decompress_ptr cinfo) + +/* Do computations that are needed before processing a JPEG scan */ + +/* cinfo->comps_in_scan and cinfo->cur_comp_info[] were set from SOS marker */ + +{ + + int ci, mcublks, tmp; + + jpeg_component_info *compptr; + + + + if (cinfo->comps_in_scan == 1) { + + + + /* Noninterleaved (single-component) scan */ + + compptr = cinfo->cur_comp_info[0]; + + + + /* Overall image size in MCUs */ + + cinfo->MCUs_per_row = compptr->width_in_blocks; + + cinfo->MCU_rows_in_scan = compptr->height_in_blocks; + + + + /* For noninterleaved scan, always one block per MCU */ + + compptr->MCU_width = 1; + + compptr->MCU_height = 1; + + compptr->MCU_blocks = 1; + + compptr->MCU_sample_width = compptr->DCT_scaled_size; + + compptr->last_col_width = 1; + + /* For noninterleaved scans, it is convenient to define last_row_height + + * as the number of block rows present in the last iMCU row. + + */ + + tmp = (int) (compptr->height_in_blocks % compptr->v_samp_factor); + + if (tmp == 0) tmp = compptr->v_samp_factor; + + compptr->last_row_height = tmp; + + + + /* Prepare array describing MCU composition */ + + cinfo->blocks_in_MCU = 1; + + cinfo->MCU_membership[0] = 0; + + + + } else { + + + + /* Interleaved (multi-component) scan */ + + if (cinfo->comps_in_scan <= 0 || cinfo->comps_in_scan > MAX_COMPS_IN_SCAN) + + ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->comps_in_scan, + + MAX_COMPS_IN_SCAN); + + + + /* Overall image size in MCUs */ + + cinfo->MCUs_per_row = (JDIMENSION) + + jdiv_round_up((long) cinfo->image_width, + + (long) (cinfo->max_h_samp_factor*DCTSIZE)); + + cinfo->MCU_rows_in_scan = (JDIMENSION) + + jdiv_round_up((long) cinfo->image_height, + + (long) (cinfo->max_v_samp_factor*DCTSIZE)); + + + + cinfo->blocks_in_MCU = 0; + + + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + + compptr = cinfo->cur_comp_info[ci]; + + /* Sampling factors give # of blocks of component in each MCU */ + + compptr->MCU_width = compptr->h_samp_factor; + + compptr->MCU_height = compptr->v_samp_factor; + + compptr->MCU_blocks = compptr->MCU_width * compptr->MCU_height; + + compptr->MCU_sample_width = compptr->MCU_width * compptr->DCT_scaled_size; + + /* Figure number of non-dummy blocks in last MCU column & row */ + + tmp = (int) (compptr->width_in_blocks % compptr->MCU_width); + + if (tmp == 0) tmp = compptr->MCU_width; + + compptr->last_col_width = tmp; + + tmp = (int) (compptr->height_in_blocks % compptr->MCU_height); + + if (tmp == 0) tmp = compptr->MCU_height; + + compptr->last_row_height = tmp; + + /* Prepare array describing MCU composition */ + + mcublks = compptr->MCU_blocks; + + if (cinfo->blocks_in_MCU + mcublks > D_MAX_BLOCKS_IN_MCU) + + ERREXIT(cinfo, JERR_BAD_MCU_SIZE); + + while (mcublks-- > 0) { + + cinfo->MCU_membership[cinfo->blocks_in_MCU++] = ci; + + } + + } + + + + } + +} + + + + + +/* + + * Save away a copy of the Q-table referenced by each component present + + * in the current scan, unless already saved during a prior scan. + + * + + * In a multiple-scan JPEG file, the encoder could assign different components + + * the same Q-table slot number, but change table definitions between scans + + * so that each component uses a different Q-table. (The IJG encoder is not + + * currently capable of doing this, but other encoders might.) Since we want + + * to be able to dequantize all the components at the end of the file, this + + * means that we have to save away the table actually used for each component. + + * We do this by copying the table at the start of the first scan containing + + * the component. + + * The JPEG spec prohibits the encoder from changing the contents of a Q-table + + * slot between scans of a component using that slot. If the encoder does so + + * anyway, this decoder will simply use the Q-table values that were current + + * at the start of the first scan for the component. + + * + + * The decompressor output side looks only at the saved quant tables, + + * not at the current Q-table slots. + + */ + + + +LOCAL void + +latch_quant_tables (j_decompress_ptr cinfo) + +{ + + int ci, qtblno; + + jpeg_component_info *compptr; + + JQUANT_TBL * qtbl; + + + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + + compptr = cinfo->cur_comp_info[ci]; + + /* No work if we already saved Q-table for this component */ + + if (compptr->quant_table != NULL) + + continue; + + /* Make sure specified quantization table is present */ + + qtblno = compptr->quant_tbl_no; + + if (qtblno < 0 || qtblno >= NUM_QUANT_TBLS || + + cinfo->quant_tbl_ptrs[qtblno] == NULL) + + ERREXIT1(cinfo, JERR_NO_QUANT_TABLE, qtblno); + + /* OK, save away the quantization table */ + + qtbl = (JQUANT_TBL *) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(JQUANT_TBL)); + + MEMCOPY(qtbl, cinfo->quant_tbl_ptrs[qtblno], SIZEOF(JQUANT_TBL)); + + compptr->quant_table = qtbl; + + } + +} + + + + + +/* + + * Initialize the input modules to read a scan of compressed data. + + * The first call to this is done by jdmaster.c after initializing + + * the entire decompressor (during jpeg_start_decompress). + + * Subsequent calls come from consume_markers, below. + + */ + + + +METHODDEF void + +start_input_pass (j_decompress_ptr cinfo) + +{ + + per_scan_setup(cinfo); + + latch_quant_tables(cinfo); + + (*cinfo->entropy->start_pass) (cinfo); + + (*cinfo->coef->start_input_pass) (cinfo); + + cinfo->inputctl->consume_input = cinfo->coef->consume_data; + +} + + + + + +/* + + * Finish up after inputting a compressed-data scan. + + * This is called by the coefficient controller after it's read all + + * the expected data of the scan. + + */ + + + +METHODDEF void + +finish_input_pass (j_decompress_ptr cinfo) + +{ + + cinfo->inputctl->consume_input = consume_markers; + +} + + + + + +/* + + * Read JPEG markers before, between, or after compressed-data scans. + + * Change state as necessary when a new scan is reached. + + * Return value is JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI. + + * + + * The consume_input method pointer points either here or to the + + * coefficient controller's consume_data routine, depending on whether + + * we are reading a compressed data segment or inter-segment markers. + + */ + + + +METHODDEF int + +consume_markers (j_decompress_ptr cinfo) + +{ + + my_inputctl_ptr inputctl = (my_inputctl_ptr) cinfo->inputctl; + + int val; + + + + if (inputctl->pub.eoi_reached) /* After hitting EOI, read no further */ + + return JPEG_REACHED_EOI; + + + + val = (*cinfo->marker->read_markers) (cinfo); + + + + switch (val) { + + case JPEG_REACHED_SOS: /* Found SOS */ + + if (inputctl->inheaders) { /* 1st SOS */ + + initial_setup(cinfo); + + inputctl->inheaders = FALSE; + + /* Note: start_input_pass must be called by jdmaster.c + + * before any more input can be consumed. jdapi.c is + + * responsible for enforcing this sequencing. + + */ + + } else { /* 2nd or later SOS marker */ + + if (! inputctl->pub.has_multiple_scans) + + ERREXIT(cinfo, JERR_EOI_EXPECTED); /* Oops, I wasn't expecting this! */ + + start_input_pass(cinfo); + + } + + break; + + case JPEG_REACHED_EOI: /* Found EOI */ + + inputctl->pub.eoi_reached = TRUE; + + if (inputctl->inheaders) { /* Tables-only datastream, apparently */ + + if (cinfo->marker->saw_SOF) + + ERREXIT(cinfo, JERR_SOF_NO_SOS); + + } else { + + /* Prevent infinite loop in coef ctlr's decompress_data routine + + * if user set output_scan_number larger than number of scans. + + */ + + if (cinfo->output_scan_number > cinfo->input_scan_number) + + cinfo->output_scan_number = cinfo->input_scan_number; + + } + + break; + + case JPEG_SUSPENDED: + + break; + + } + + + + return val; + +} + + + + + +/* + + * Reset state to begin a fresh datastream. + + */ + + + +METHODDEF void + +reset_input_controller (j_decompress_ptr cinfo) + +{ + + my_inputctl_ptr inputctl = (my_inputctl_ptr) cinfo->inputctl; + + + + inputctl->pub.consume_input = consume_markers; + + inputctl->pub.has_multiple_scans = FALSE; /* "unknown" would be better */ + + inputctl->pub.eoi_reached = FALSE; + + inputctl->inheaders = TRUE; + + /* Reset other modules */ + + (*cinfo->err->reset_error_mgr) ((j_common_ptr) cinfo); + + (*cinfo->marker->reset_marker_reader) (cinfo); + + /* Reset progression state -- would be cleaner if entropy decoder did this */ + + cinfo->coef_bits = NULL; + +} + + + + + +/* + + * Initialize the input controller module. + + * This is called only once, when the decompression object is created. + + */ + + + +GLOBAL void + +jinit_input_controller (j_decompress_ptr cinfo) + +{ + + my_inputctl_ptr inputctl; + + + + /* Create subobject in permanent pool */ + + inputctl = (my_inputctl_ptr) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + + SIZEOF(my_input_controller)); + + cinfo->inputctl = (struct jpeg_input_controller *) inputctl; + + /* Initialize method pointers */ + + inputctl->pub.consume_input = consume_markers; + + inputctl->pub.reset_input_controller = reset_input_controller; + + inputctl->pub.start_input_pass = start_input_pass; + + inputctl->pub.finish_input_pass = finish_input_pass; + + /* Initialize state: can't use reset_input_controller since we don't + + * want to try to reset other modules yet. + + */ + + inputctl->pub.has_multiple_scans = FALSE; /* "unknown" would be better */ + + inputctl->pub.eoi_reached = FALSE; + + inputctl->inheaders = TRUE; + +} + diff --git a/tools/urt/libs/jpeg6/jdmainct.cpp b/tools/urt/libs/jpeg6/jdmainct.cpp new file mode 100644 index 00000000..7b4c2559 --- /dev/null +++ b/tools/urt/libs/jpeg6/jdmainct.cpp @@ -0,0 +1,1024 @@ +/* + + * jdmainct.c + + * + + * Copyright (C) 1994-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains the main buffer controller for decompression. + + * The main buffer lies between the JPEG decompressor proper and the + + * post-processor; it holds downsampled data in the JPEG colorspace. + + * + + * Note that this code is bypassed in raw-data mode, since the application + + * supplies the equivalent of the main buffer in that case. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "radiant_jpeglib.h" + + + + + +/* + + * In the current system design, the main buffer need never be a full-image + + * buffer; any full-height buffers will be found inside the coefficient or + + * postprocessing controllers. Nonetheless, the main controller is not + + * trivial. Its responsibility is to provide context rows for upsampling/ + + * rescaling, and doing this in an efficient fashion is a bit tricky. + + * + + * Postprocessor input data is counted in "row groups". A row group + + * is defined to be (v_samp_factor * DCT_scaled_size / min_DCT_scaled_size) + + * sample rows of each component. (We require DCT_scaled_size values to be + + * chosen such that these numbers are integers. In practice DCT_scaled_size + + * values will likely be powers of two, so we actually have the stronger + + * condition that DCT_scaled_size / min_DCT_scaled_size is an integer.) + + * Upsampling will typically produce max_v_samp_factor pixel rows from each + + * row group (times any additional scale factor that the upsampler is + + * applying). + + * + + * The coefficient controller will deliver data to us one iMCU row at a time; + + * each iMCU row contains v_samp_factor * DCT_scaled_size sample rows, or + + * exactly min_DCT_scaled_size row groups. (This amount of data corresponds + + * to one row of MCUs when the image is fully interleaved.) Note that the + + * number of sample rows varies across components, but the number of row + + * groups does not. Some garbage sample rows may be included in the last iMCU + + * row at the bottom of the image. + + * + + * Depending on the vertical scaling algorithm used, the upsampler may need + + * access to the sample row(s) above and below its current input row group. + + * The upsampler is required to set need_context_rows TRUE at global selection + + * time if so. When need_context_rows is FALSE, this controller can simply + + * obtain one iMCU row at a time from the coefficient controller and dole it + + * out as row groups to the postprocessor. + + * + + * When need_context_rows is TRUE, this controller guarantees that the buffer + + * passed to postprocessing contains at least one row group's worth of samples + + * above and below the row group(s) being processed. Note that the context + + * rows "above" the first passed row group appear at negative row offsets in + + * the passed buffer. At the top and bottom of the image, the required + + * context rows are manufactured by duplicating the first or last real sample + + * row; this avoids having special cases in the upsampling inner loops. + + * + + * The amount of context is fixed at one row group just because that's a + + * convenient number for this controller to work with. The existing + + * upsamplers really only need one sample row of context. An upsampler + + * supporting arbitrary output rescaling might wish for more than one row + + * group of context when shrinking the image; tough, we don't handle that. + + * (This is justified by the assumption that downsizing will be handled mostly + + * by adjusting the DCT_scaled_size values, so that the actual scale factor at + + * the upsample step needn't be much less than one.) + + * + + * To provide the desired context, we have to retain the last two row groups + + * of one iMCU row while reading in the next iMCU row. (The last row group + + * can't be processed until we have another row group for its below-context, + + * and so we have to save the next-to-last group too for its above-context.) + + * We could do this most simply by copying data around in our buffer, but + + * that'd be very slow. We can avoid copying any data by creating a rather + + * strange pointer structure. Here's how it works. We allocate a workspace + + * consisting of M+2 row groups (where M = min_DCT_scaled_size is the number + + * of row groups per iMCU row). We create two sets of redundant pointers to + + * the workspace. Labeling the physical row groups 0 to M+1, the synthesized + + * pointer lists look like this: + + * M+1 M-1 + + * master pointer --> 0 master pointer --> 0 + + * 1 1 + + * ... ... + + * M-3 M-3 + + * M-2 M + + * M-1 M+1 + + * M M-2 + + * M+1 M-1 + + * 0 0 + + * We read alternate iMCU rows using each master pointer; thus the last two + + * row groups of the previous iMCU row remain un-overwritten in the workspace. + + * The pointer lists are set up so that the required context rows appear to + + * be adjacent to the proper places when we pass the pointer lists to the + + * upsampler. + + * + + * The above pictures describe the normal state of the pointer lists. + + * At top and bottom of the image, we diddle the pointer lists to duplicate + + * the first or last sample row as necessary (this is cheaper than copying + + * sample rows around). + + * + + * This scheme breaks down if M < 2, ie, min_DCT_scaled_size is 1. In that + + * situation each iMCU row provides only one row group so the buffering logic + + * must be different (eg, we must read two iMCU rows before we can emit the + + * first row group). For now, we simply do not support providing context + + * rows when min_DCT_scaled_size is 1. That combination seems unlikely to + + * be worth providing --- if someone wants a 1/8th-size preview, they probably + + * want it quick and dirty, so a context-free upsampler is sufficient. + + */ + + + + + +/* Private buffer controller object */ + + + +typedef struct { + + struct jpeg_d_main_controller pub; /* public fields */ + + + + /* Pointer to allocated workspace (M or M+2 row groups). */ + + JSAMPARRAY buffer[MAX_COMPONENTS]; + + + + boolean buffer_full; /* Have we gotten an iMCU row from decoder? */ + + JDIMENSION rowgroup_ctr; /* counts row groups output to postprocessor */ + + + + /* Remaining fields are only used in the context case. */ + + + + /* These are the master pointers to the funny-order pointer lists. */ + + JSAMPIMAGE xbuffer[2]; /* pointers to weird pointer lists */ + + + + int whichptr; /* indicates which pointer set is now in use */ + + int context_state; /* process_data state machine status */ + + JDIMENSION rowgroups_avail; /* row groups available to postprocessor */ + + JDIMENSION iMCU_row_ctr; /* counts iMCU rows to detect image top/bot */ + +} my_main_controller; + + + +typedef my_main_controller * my_main_ptr; + + + +/* context_state values: */ + +#define CTX_PREPARE_FOR_IMCU 0 /* need to prepare for MCU row */ + +#define CTX_PROCESS_IMCU 1 /* feeding iMCU to postprocessor */ + +#define CTX_POSTPONED_ROW 2 /* feeding postponed row group */ + + + + + +/* Forward declarations */ + +METHODDEF void process_data_simple_main + + JPP((j_decompress_ptr cinfo, JSAMPARRAY output_buf, + + JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail)); + +METHODDEF void process_data_context_main + + JPP((j_decompress_ptr cinfo, JSAMPARRAY output_buf, + + JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail)); + +#ifdef QUANT_2PASS_SUPPORTED + +METHODDEF void process_data_crank_post + + JPP((j_decompress_ptr cinfo, JSAMPARRAY output_buf, + + JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail)); + +#endif + + + + + +LOCAL void + +alloc_funny_pointers (j_decompress_ptr cinfo) + +/* Allocate space for the funny pointer lists. + + * This is done only once, not once per pass. + + */ + +{ + + my_main_ptr main = (my_main_ptr) cinfo->main; + + int ci, rgroup; + + int M = cinfo->min_DCT_scaled_size; + + jpeg_component_info *compptr; + + JSAMPARRAY xbuf; + + + + /* Get top-level space for component array pointers. + + * We alloc both arrays with one call to save a few cycles. + + */ + + main->xbuffer[0] = (JSAMPIMAGE) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + cinfo->num_components * 2 * SIZEOF(JSAMPARRAY)); + + main->xbuffer[1] = main->xbuffer[0] + cinfo->num_components; + + + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + rgroup = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + + cinfo->min_DCT_scaled_size; /* height of a row group of component */ + + /* Get space for pointer lists --- M+4 row groups in each list. + + * We alloc both pointer lists with one call to save a few cycles. + + */ + + xbuf = (JSAMPARRAY) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + 2 * (rgroup * (M + 4)) * SIZEOF(JSAMPROW)); + + xbuf += rgroup; /* want one row group at negative offsets */ + + main->xbuffer[0][ci] = xbuf; + + xbuf += rgroup * (M + 4); + + main->xbuffer[1][ci] = xbuf; + + } + +} + + + + + +LOCAL void + +make_funny_pointers (j_decompress_ptr cinfo) + +/* Create the funny pointer lists discussed in the comments above. + + * The actual workspace is already allocated (in main->buffer), + + * and the space for the pointer lists is allocated too. + + * This routine just fills in the curiously ordered lists. + + * This will be repeated at the beginning of each pass. + + */ + +{ + + my_main_ptr main = (my_main_ptr) cinfo->main; + + int ci, i, rgroup; + + int M = cinfo->min_DCT_scaled_size; + + jpeg_component_info *compptr; + + JSAMPARRAY buf, xbuf0, xbuf1; + + + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + rgroup = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + + cinfo->min_DCT_scaled_size; /* height of a row group of component */ + + xbuf0 = main->xbuffer[0][ci]; + + xbuf1 = main->xbuffer[1][ci]; + + /* First copy the workspace pointers as-is */ + + buf = main->buffer[ci]; + + for (i = 0; i < rgroup * (M + 2); i++) { + + xbuf0[i] = xbuf1[i] = buf[i]; + + } + + /* In the second list, put the last four row groups in swapped order */ + + for (i = 0; i < rgroup * 2; i++) { + + xbuf1[rgroup*(M-2) + i] = buf[rgroup*M + i]; + + xbuf1[rgroup*M + i] = buf[rgroup*(M-2) + i]; + + } + + /* The wraparound pointers at top and bottom will be filled later + + * (see set_wraparound_pointers, below). Initially we want the "above" + + * pointers to duplicate the first actual data line. This only needs + + * to happen in xbuffer[0]. + + */ + + for (i = 0; i < rgroup; i++) { + + xbuf0[i - rgroup] = xbuf0[0]; + + } + + } + +} + + + + + +LOCAL void + +set_wraparound_pointers (j_decompress_ptr cinfo) + +/* Set up the "wraparound" pointers at top and bottom of the pointer lists. + + * This changes the pointer list state from top-of-image to the normal state. + + */ + +{ + + my_main_ptr main = (my_main_ptr) cinfo->main; + + int ci, i, rgroup; + + int M = cinfo->min_DCT_scaled_size; + + jpeg_component_info *compptr; + + JSAMPARRAY xbuf0, xbuf1; + + + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + rgroup = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + + cinfo->min_DCT_scaled_size; /* height of a row group of component */ + + xbuf0 = main->xbuffer[0][ci]; + + xbuf1 = main->xbuffer[1][ci]; + + for (i = 0; i < rgroup; i++) { + + xbuf0[i - rgroup] = xbuf0[rgroup*(M+1) + i]; + + xbuf1[i - rgroup] = xbuf1[rgroup*(M+1) + i]; + + xbuf0[rgroup*(M+2) + i] = xbuf0[i]; + + xbuf1[rgroup*(M+2) + i] = xbuf1[i]; + + } + + } + +} + + + + + +LOCAL void + +set_bottom_pointers (j_decompress_ptr cinfo) + +/* Change the pointer lists to duplicate the last sample row at the bottom + + * of the image. whichptr indicates which xbuffer holds the final iMCU row. + + * Also sets rowgroups_avail to indicate number of nondummy row groups in row. + + */ + +{ + + my_main_ptr main = (my_main_ptr) cinfo->main; + + int ci, i, rgroup, iMCUheight, rows_left; + + jpeg_component_info *compptr; + + JSAMPARRAY xbuf; + + + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + /* Count sample rows in one iMCU row and in one row group */ + + iMCUheight = compptr->v_samp_factor * compptr->DCT_scaled_size; + + rgroup = iMCUheight / cinfo->min_DCT_scaled_size; + + /* Count nondummy sample rows remaining for this component */ + + rows_left = (int) (compptr->downsampled_height % (JDIMENSION) iMCUheight); + + if (rows_left == 0) rows_left = iMCUheight; + + /* Count nondummy row groups. Should get same answer for each component, + + * so we need only do it once. + + */ + + if (ci == 0) { + + main->rowgroups_avail = (JDIMENSION) ((rows_left-1) / rgroup + 1); + + } + + /* Duplicate the last real sample row rgroup*2 times; this pads out the + + * last partial rowgroup and ensures at least one full rowgroup of context. + + */ + + xbuf = main->xbuffer[main->whichptr][ci]; + + for (i = 0; i < rgroup * 2; i++) { + + xbuf[rows_left + i] = xbuf[rows_left-1]; + + } + + } + +} + + + + + +/* + + * Initialize for a processing pass. + + */ + + + +METHODDEF void + +start_pass_main (j_decompress_ptr cinfo, J_BUF_MODE pass_mode) + +{ + + my_main_ptr main = (my_main_ptr) cinfo->main; + + + + switch (pass_mode) { + + case JBUF_PASS_THRU: + + if (cinfo->upsample->need_context_rows) { + + main->pub.process_data = process_data_context_main; + + make_funny_pointers(cinfo); /* Create the xbuffer[] lists */ + + main->whichptr = 0; /* Read first iMCU row into xbuffer[0] */ + + main->context_state = CTX_PREPARE_FOR_IMCU; + + main->iMCU_row_ctr = 0; + + } else { + + /* Simple case with no context needed */ + + main->pub.process_data = process_data_simple_main; + + } + + main->buffer_full = FALSE; /* Mark buffer empty */ + + main->rowgroup_ctr = 0; + + break; + +#ifdef QUANT_2PASS_SUPPORTED + + case JBUF_CRANK_DEST: + + /* For last pass of 2-pass quantization, just crank the postprocessor */ + + main->pub.process_data = process_data_crank_post; + + break; + +#endif + + default: + + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + + break; + + } + +} + + + + + +/* + + * Process some data. + + * This handles the simple case where no context is required. + + */ + + + +METHODDEF void + +process_data_simple_main (j_decompress_ptr cinfo, + + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + + JDIMENSION out_rows_avail) + +{ + + my_main_ptr main = (my_main_ptr) cinfo->main; + + JDIMENSION rowgroups_avail; + + + + /* Read input data if we haven't filled the main buffer yet */ + + if (! main->buffer_full) { + + if (! (*cinfo->coef->decompress_data) (cinfo, main->buffer)) + + return; /* suspension forced, can do nothing more */ + + main->buffer_full = TRUE; /* OK, we have an iMCU row to work with */ + + } + + + + /* There are always min_DCT_scaled_size row groups in an iMCU row. */ + + rowgroups_avail = (JDIMENSION) cinfo->min_DCT_scaled_size; + + /* Note: at the bottom of the image, we may pass extra garbage row groups + + * to the postprocessor. The postprocessor has to check for bottom + + * of image anyway (at row resolution), so no point in us doing it too. + + */ + + + + /* Feed the postprocessor */ + + (*cinfo->post->post_process_data) (cinfo, main->buffer, + + &main->rowgroup_ctr, rowgroups_avail, + + output_buf, out_row_ctr, out_rows_avail); + + + + /* Has postprocessor consumed all the data yet? If so, mark buffer empty */ + + if (main->rowgroup_ctr >= rowgroups_avail) { + + main->buffer_full = FALSE; + + main->rowgroup_ctr = 0; + + } + +} + + + + + +/* + + * Process some data. + + * This handles the case where context rows must be provided. + + */ + + + +METHODDEF void + +process_data_context_main (j_decompress_ptr cinfo, + + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + + JDIMENSION out_rows_avail) + +{ + + my_main_ptr main = (my_main_ptr) cinfo->main; + + + + /* Read input data if we haven't filled the main buffer yet */ + + if (! main->buffer_full) { + + if (! (*cinfo->coef->decompress_data) (cinfo, + + main->xbuffer[main->whichptr])) + + return; /* suspension forced, can do nothing more */ + + main->buffer_full = TRUE; /* OK, we have an iMCU row to work with */ + + main->iMCU_row_ctr++; /* count rows received */ + + } + + + + /* Postprocessor typically will not swallow all the input data it is handed + + * in one call (due to filling the output buffer first). Must be prepared + + * to exit and restart. This switch lets us keep track of how far we got. + + * Note that each case falls through to the next on successful completion. + + */ + + switch (main->context_state) { + + case CTX_POSTPONED_ROW: + + /* Call postprocessor using previously set pointers for postponed row */ + + (*cinfo->post->post_process_data) (cinfo, main->xbuffer[main->whichptr], + + &main->rowgroup_ctr, main->rowgroups_avail, + + output_buf, out_row_ctr, out_rows_avail); + + if (main->rowgroup_ctr < main->rowgroups_avail) + + return; /* Need to suspend */ + + main->context_state = CTX_PREPARE_FOR_IMCU; + + if (*out_row_ctr >= out_rows_avail) + + return; /* Postprocessor exactly filled output buf */ + + /*FALLTHROUGH*/ + + case CTX_PREPARE_FOR_IMCU: + + /* Prepare to process first M-1 row groups of this iMCU row */ + + main->rowgroup_ctr = 0; + + main->rowgroups_avail = (JDIMENSION) (cinfo->min_DCT_scaled_size - 1); + + /* Check for bottom of image: if so, tweak pointers to "duplicate" + + * the last sample row, and adjust rowgroups_avail to ignore padding rows. + + */ + + if (main->iMCU_row_ctr == cinfo->total_iMCU_rows) + + set_bottom_pointers(cinfo); + + main->context_state = CTX_PROCESS_IMCU; + + /*FALLTHROUGH*/ + + case CTX_PROCESS_IMCU: + + /* Call postprocessor using previously set pointers */ + + (*cinfo->post->post_process_data) (cinfo, main->xbuffer[main->whichptr], + + &main->rowgroup_ctr, main->rowgroups_avail, + + output_buf, out_row_ctr, out_rows_avail); + + if (main->rowgroup_ctr < main->rowgroups_avail) + + return; /* Need to suspend */ + + /* After the first iMCU, change wraparound pointers to normal state */ + + if (main->iMCU_row_ctr == 1) + + set_wraparound_pointers(cinfo); + + /* Prepare to load new iMCU row using other xbuffer list */ + + main->whichptr ^= 1; /* 0=>1 or 1=>0 */ + + main->buffer_full = FALSE; + + /* Still need to process last row group of this iMCU row, */ + + /* which is saved at index M+1 of the other xbuffer */ + + main->rowgroup_ctr = (JDIMENSION) (cinfo->min_DCT_scaled_size + 1); + + main->rowgroups_avail = (JDIMENSION) (cinfo->min_DCT_scaled_size + 2); + + main->context_state = CTX_POSTPONED_ROW; + + } + +} + + + + + +/* + + * Process some data. + + * Final pass of two-pass quantization: just call the postprocessor. + + * Source data will be the postprocessor controller's internal buffer. + + */ + + + +#ifdef QUANT_2PASS_SUPPORTED + + + +METHODDEF void + +process_data_crank_post (j_decompress_ptr cinfo, + + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + + JDIMENSION out_rows_avail) + +{ + + (*cinfo->post->post_process_data) (cinfo, (JSAMPIMAGE) NULL, + + (JDIMENSION *) NULL, (JDIMENSION) 0, + + output_buf, out_row_ctr, out_rows_avail); + +} + + + +#endif /* QUANT_2PASS_SUPPORTED */ + + + + + +/* + + * Initialize main buffer controller. + + */ + + + +GLOBAL void + +jinit_d_main_controller (j_decompress_ptr cinfo, boolean need_full_buffer) + +{ + + my_main_ptr main; + + int ci, rgroup, ngroups; + + jpeg_component_info *compptr; + + + + main = (my_main_ptr) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(my_main_controller)); + + cinfo->main = (struct jpeg_d_main_controller *) main; + + main->pub.start_pass = start_pass_main; + + + + if (need_full_buffer) /* shouldn't happen */ + + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + + + + /* Allocate the workspace. + + * ngroups is the number of row groups we need. + + */ + + if (cinfo->upsample->need_context_rows) { + + if (cinfo->min_DCT_scaled_size < 2) /* unsupported, see comments above */ + + ERREXIT(cinfo, JERR_NOTIMPL); + + alloc_funny_pointers(cinfo); /* Alloc space for xbuffer[] lists */ + + ngroups = cinfo->min_DCT_scaled_size + 2; + + } else { + + ngroups = cinfo->min_DCT_scaled_size; + + } + + + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + rgroup = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + + cinfo->min_DCT_scaled_size; /* height of a row group of component */ + + main->buffer[ci] = (*cinfo->mem->alloc_sarray) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, + + compptr->width_in_blocks * compptr->DCT_scaled_size, + + (JDIMENSION) (rgroup * ngroups)); + + } + +} + diff --git a/tools/urt/libs/jpeg6/jdmarker.cpp b/tools/urt/libs/jpeg6/jdmarker.cpp new file mode 100644 index 00000000..15760981 --- /dev/null +++ b/tools/urt/libs/jpeg6/jdmarker.cpp @@ -0,0 +1,1052 @@ +/* + * jdmarker.c + * + * Copyright (C) 1991-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains routines to decode JPEG datastream markers. + * Most of the complexity arises from our desire to support input + * suspension: if not all of the data for a marker is available, + * we must exit back to the application. On resumption, we reprocess + * the marker. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "radiant_jpeglib.h" + + +typedef enum { /* JPEG marker codes */ + M_SOF0 = 0xc0, + M_SOF1 = 0xc1, + M_SOF2 = 0xc2, + M_SOF3 = 0xc3, + + M_SOF5 = 0xc5, + M_SOF6 = 0xc6, + M_SOF7 = 0xc7, + + M_JPG = 0xc8, + M_SOF9 = 0xc9, + M_SOF10 = 0xca, + M_SOF11 = 0xcb, + + M_SOF13 = 0xcd, + M_SOF14 = 0xce, + M_SOF15 = 0xcf, + + M_DHT = 0xc4, + + M_DAC = 0xcc, + + M_RST0 = 0xd0, + M_RST1 = 0xd1, + M_RST2 = 0xd2, + M_RST3 = 0xd3, + M_RST4 = 0xd4, + M_RST5 = 0xd5, + M_RST6 = 0xd6, + M_RST7 = 0xd7, + + M_SOI = 0xd8, + M_EOI = 0xd9, + M_SOS = 0xda, + M_DQT = 0xdb, + M_DNL = 0xdc, + M_DRI = 0xdd, + M_DHP = 0xde, + M_EXP = 0xdf, + + M_APP0 = 0xe0, + M_APP1 = 0xe1, + M_APP2 = 0xe2, + M_APP3 = 0xe3, + M_APP4 = 0xe4, + M_APP5 = 0xe5, + M_APP6 = 0xe6, + M_APP7 = 0xe7, + M_APP8 = 0xe8, + M_APP9 = 0xe9, + M_APP10 = 0xea, + M_APP11 = 0xeb, + M_APP12 = 0xec, + M_APP13 = 0xed, + M_APP14 = 0xee, + M_APP15 = 0xef, + + M_JPG0 = 0xf0, + M_JPG13 = 0xfd, + M_COM = 0xfe, + + M_TEM = 0x01, + + M_ERROR = 0x100 +} JPEG_MARKER; + + +/* + * Macros for fetching data from the data source module. + * + * At all times, cinfo->src->next_input_byte and ->bytes_in_buffer reflect + * the current restart point; we update them only when we have reached a + * suitable place to restart if a suspension occurs. + */ + +/* Declare and initialize local copies of input pointer/count */ +#define INPUT_VARS(cinfo) \ + struct jpeg_source_mgr * datasrc = (cinfo)->src; \ + const JOCTET * next_input_byte = datasrc->next_input_byte; \ + size_t bytes_in_buffer = datasrc->bytes_in_buffer + +/* Unload the local copies --- do this only at a restart boundary */ +#define INPUT_SYNC(cinfo) \ + ( datasrc->next_input_byte = next_input_byte, \ + datasrc->bytes_in_buffer = bytes_in_buffer ) + +/* Reload the local copies --- seldom used except in MAKE_BYTE_AVAIL */ +#define INPUT_RELOAD(cinfo) \ + ( next_input_byte = datasrc->next_input_byte, \ + bytes_in_buffer = datasrc->bytes_in_buffer ) + +/* Internal macro for INPUT_BYTE and INPUT_2BYTES: make a byte available. + * Note we do *not* do INPUT_SYNC before calling fill_input_buffer, + * but we must reload the local copies after a successful fill. + */ +#define MAKE_BYTE_AVAIL(cinfo,action) \ + if (bytes_in_buffer == 0) { \ + if (! (*datasrc->fill_input_buffer) (cinfo)) \ + { action; } \ + INPUT_RELOAD(cinfo); \ + } \ + bytes_in_buffer-- + +/* Read a byte into variable V. + * If must suspend, take the specified action (typically "return FALSE"). + */ +#define INPUT_BYTE(cinfo,V,action) \ + MAKESTMT( MAKE_BYTE_AVAIL(cinfo,action); \ + V = GETJOCTET(*next_input_byte++); ) + +/* As above, but read two bytes interpreted as an unsigned 16-bit integer. + * V should be declared unsigned int or perhaps INT32. + */ +#define INPUT_2BYTES(cinfo,V,action) \ + MAKESTMT( MAKE_BYTE_AVAIL(cinfo,action); \ + V = ((unsigned int) GETJOCTET(*next_input_byte++)) << 8; \ + MAKE_BYTE_AVAIL(cinfo,action); \ + V += GETJOCTET(*next_input_byte++); ) + + +/* + * Routines to process JPEG markers. + * + * Entry condition: JPEG marker itself has been read and its code saved + * in cinfo->unread_marker; input restart point is just after the marker. + * + * Exit: if return TRUE, have read and processed any parameters, and have + * updated the restart point to point after the parameters. + * If return FALSE, was forced to suspend before reaching end of + * marker parameters; restart point has not been moved. Same routine + * will be called again after application supplies more input data. + * + * This approach to suspension assumes that all of a marker's parameters can + * fit into a single input bufferload. This should hold for "normal" + * markers. Some COM/APPn markers might have large parameter segments, + * but we use skip_input_data to get past those, and thereby put the problem + * on the source manager's shoulders. + * + * Note that we don't bother to avoid duplicate trace messages if a + * suspension occurs within marker parameters. Other side effects + * require more care. + */ + + +LOCAL boolean +get_soi (j_decompress_ptr cinfo) +/* Process an SOI marker */ +{ + int i; + + TRACEMS(cinfo, 1, JTRC_SOI); + + if (cinfo->marker->saw_SOI) + ERREXIT(cinfo, JERR_SOI_DUPLICATE); + + /* Reset all parameters that are defined to be reset by SOI */ + + for (i = 0; i < NUM_ARITH_TBLS; i++) { + cinfo->arith_dc_L[i] = 0; + cinfo->arith_dc_U[i] = 1; + cinfo->arith_ac_K[i] = 5; + } + cinfo->restart_interval = 0; + + /* Set initial assumptions for colorspace etc */ + + cinfo->jpeg_color_space = JCS_UNKNOWN; + cinfo->CCIR601_sampling = FALSE; /* Assume non-CCIR sampling??? */ + + cinfo->saw_JFIF_marker = FALSE; + cinfo->density_unit = 0; /* set default JFIF APP0 values */ + cinfo->X_density = 1; + cinfo->Y_density = 1; + cinfo->saw_Adobe_marker = FALSE; + cinfo->Adobe_transform = 0; + + cinfo->marker->saw_SOI = TRUE; + + return TRUE; +} + + +LOCAL boolean +get_sof (j_decompress_ptr cinfo, boolean is_prog, boolean is_arith) +/* Process a SOFn marker */ +{ + INT32 length; + int c, ci; + jpeg_component_info * compptr; + INPUT_VARS(cinfo); + + cinfo->progressive_mode = is_prog; + cinfo->arith_code = is_arith; + + INPUT_2BYTES(cinfo, length, return FALSE); + + INPUT_BYTE(cinfo, cinfo->data_precision, return FALSE); + INPUT_2BYTES(cinfo, cinfo->image_height, return FALSE); + INPUT_2BYTES(cinfo, cinfo->image_width, return FALSE); + INPUT_BYTE(cinfo, cinfo->num_components, return FALSE); + + length -= 8; + + TRACEMS4(cinfo, 1, JTRC_SOF, cinfo->unread_marker, + (int) cinfo->image_width, (int) cinfo->image_height, + cinfo->num_components); + + if (cinfo->marker->saw_SOF) + ERREXIT(cinfo, JERR_SOF_DUPLICATE); + + /* We don't support files in which the image height is initially specified */ + /* as 0 and is later redefined by DNL. As long as we have to check that, */ + /* might as well have a general sanity check. */ + if (cinfo->image_height <= 0 || cinfo->image_width <= 0 + || cinfo->num_components <= 0) + ERREXIT(cinfo, JERR_EMPTY_IMAGE); + + if (length != (cinfo->num_components * 3)) + ERREXIT(cinfo, JERR_BAD_LENGTH); + + if (cinfo->comp_info == NULL) /* do only once, even if suspend */ + cinfo->comp_info = (jpeg_component_info *) (*cinfo->mem->alloc_small) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + cinfo->num_components * SIZEOF(jpeg_component_info)); + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + compptr->component_index = ci; + INPUT_BYTE(cinfo, compptr->component_id, return FALSE); + INPUT_BYTE(cinfo, c, return FALSE); + compptr->h_samp_factor = (c >> 4) & 15; + compptr->v_samp_factor = (c ) & 15; + INPUT_BYTE(cinfo, compptr->quant_tbl_no, return FALSE); + + TRACEMS4(cinfo, 1, JTRC_SOF_COMPONENT, + compptr->component_id, compptr->h_samp_factor, + compptr->v_samp_factor, compptr->quant_tbl_no); + } + + cinfo->marker->saw_SOF = TRUE; + + INPUT_SYNC(cinfo); + return TRUE; +} + + +LOCAL boolean +get_sos (j_decompress_ptr cinfo) +/* Process a SOS marker */ +{ + INT32 length; + int i, ci, n, c, cc; + jpeg_component_info * compptr; + INPUT_VARS(cinfo); + + if (! cinfo->marker->saw_SOF) + ERREXIT(cinfo, JERR_SOS_NO_SOF); + + INPUT_2BYTES(cinfo, length, return FALSE); + + INPUT_BYTE(cinfo, n, return FALSE); /* Number of components */ + + if (length != (n * 2 + 6) || n < 1 || n > MAX_COMPS_IN_SCAN) + ERREXIT(cinfo, JERR_BAD_LENGTH); + + TRACEMS1(cinfo, 1, JTRC_SOS, n); + + cinfo->comps_in_scan = n; + + /* Collect the component-spec parameters */ + + for (i = 0; i < n; i++) { + INPUT_BYTE(cinfo, cc, return FALSE); + INPUT_BYTE(cinfo, c, return FALSE); + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + if (cc == compptr->component_id) + goto id_found; + } + + ERREXIT1(cinfo, JERR_BAD_COMPONENT_ID, cc); + + id_found: + + cinfo->cur_comp_info[i] = compptr; + compptr->dc_tbl_no = (c >> 4) & 15; + compptr->ac_tbl_no = (c ) & 15; + + TRACEMS3(cinfo, 1, JTRC_SOS_COMPONENT, cc, + compptr->dc_tbl_no, compptr->ac_tbl_no); + } + + /* Collect the additional scan parameters Ss, Se, Ah/Al. */ + INPUT_BYTE(cinfo, c, return FALSE); + cinfo->Ss = c; + INPUT_BYTE(cinfo, c, return FALSE); + cinfo->Se = c; + INPUT_BYTE(cinfo, c, return FALSE); + cinfo->Ah = (c >> 4) & 15; + cinfo->Al = (c ) & 15; + + TRACEMS4(cinfo, 1, JTRC_SOS_PARAMS, cinfo->Ss, cinfo->Se, + cinfo->Ah, cinfo->Al); + + /* Prepare to scan data & restart markers */ + cinfo->marker->next_restart_num = 0; + + /* Count another SOS marker */ + cinfo->input_scan_number++; + + INPUT_SYNC(cinfo); + return TRUE; +} + + +METHODDEF boolean +get_app0 (j_decompress_ptr cinfo) +/* Process an APP0 marker */ +{ +#define JFIF_LEN 14 + INT32 length; + UINT8 b[JFIF_LEN]; + int buffp; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + length -= 2; + + /* See if a JFIF APP0 marker is present */ + + if (length >= JFIF_LEN) { + for (buffp = 0; buffp < JFIF_LEN; buffp++) + INPUT_BYTE(cinfo, b[buffp], return FALSE); + length -= JFIF_LEN; + + if (b[0]==0x4A && b[1]==0x46 && b[2]==0x49 && b[3]==0x46 && b[4]==0) { + /* Found JFIF APP0 marker: check version */ + /* Major version must be 1, anything else signals an incompatible change. + * We used to treat this as an error, but now it's a nonfatal warning, + * because some bozo at Hijaak couldn't read the spec. + * Minor version should be 0..2, but process anyway if newer. + */ + if (b[5] != 1) + WARNMS2(cinfo, JWRN_JFIF_MAJOR, b[5], b[6]); + else if (b[6] > 2) + TRACEMS2(cinfo, 1, JTRC_JFIF_MINOR, b[5], b[6]); + /* Save info */ + cinfo->saw_JFIF_marker = TRUE; + cinfo->density_unit = b[7]; + cinfo->X_density = (b[8] << 8) + b[9]; + cinfo->Y_density = (b[10] << 8) + b[11]; + TRACEMS3(cinfo, 1, JTRC_JFIF, + cinfo->X_density, cinfo->Y_density, cinfo->density_unit); + if (b[12] | b[13]) + TRACEMS2(cinfo, 1, JTRC_JFIF_THUMBNAIL, b[12], b[13]); + if (length != ((INT32) b[12] * (INT32) b[13] * (INT32) 3)) + TRACEMS1(cinfo, 1, JTRC_JFIF_BADTHUMBNAILSIZE, (int) length); + } else { + /* Start of APP0 does not match "JFIF" */ + TRACEMS1(cinfo, 1, JTRC_APP0, (int) length + JFIF_LEN); + } + } else { + /* Too short to be JFIF marker */ + TRACEMS1(cinfo, 1, JTRC_APP0, (int) length); + } + + INPUT_SYNC(cinfo); + if (length > 0) /* skip any remaining data -- could be lots */ + (*cinfo->src->skip_input_data) (cinfo, (long) length); + + return TRUE; +} + + +METHODDEF boolean +get_app14 (j_decompress_ptr cinfo) +/* Process an APP14 marker */ +{ +#define ADOBE_LEN 12 + INT32 length; + UINT8 b[ADOBE_LEN]; + int buffp; + unsigned int version, flags0, flags1, transform; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + length -= 2; + + /* See if an Adobe APP14 marker is present */ + + if (length >= ADOBE_LEN) { + for (buffp = 0; buffp < ADOBE_LEN; buffp++) + INPUT_BYTE(cinfo, b[buffp], return FALSE); + length -= ADOBE_LEN; + + if (b[0]==0x41 && b[1]==0x64 && b[2]==0x6F && b[3]==0x62 && b[4]==0x65) { + /* Found Adobe APP14 marker */ + version = (b[5] << 8) + b[6]; + flags0 = (b[7] << 8) + b[8]; + flags1 = (b[9] << 8) + b[10]; + transform = b[11]; + TRACEMS4(cinfo, 1, JTRC_ADOBE, version, flags0, flags1, transform); + cinfo->saw_Adobe_marker = TRUE; + cinfo->Adobe_transform = (UINT8) transform; + } else { + /* Start of APP14 does not match "Adobe" */ + TRACEMS1(cinfo, 1, JTRC_APP14, (int) length + ADOBE_LEN); + } + } else { + /* Too short to be Adobe marker */ + TRACEMS1(cinfo, 1, JTRC_APP14, (int) length); + } + + INPUT_SYNC(cinfo); + if (length > 0) /* skip any remaining data -- could be lots */ + (*cinfo->src->skip_input_data) (cinfo, (long) length); + + return TRUE; +} + + +LOCAL boolean +get_dac (j_decompress_ptr cinfo) +/* Process a DAC marker */ +{ + INT32 length; + int index, val; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + length -= 2; + + while (length > 0) { + INPUT_BYTE(cinfo, index, return FALSE); + INPUT_BYTE(cinfo, val, return FALSE); + + length -= 2; + + TRACEMS2(cinfo, 1, JTRC_DAC, index, val); + + if (index < 0 || index >= (2*NUM_ARITH_TBLS)) + ERREXIT1(cinfo, JERR_DAC_INDEX, index); + + if (index >= NUM_ARITH_TBLS) { /* define AC table */ + cinfo->arith_ac_K[index-NUM_ARITH_TBLS] = (UINT8) val; + } else { /* define DC table */ + cinfo->arith_dc_L[index] = (UINT8) (val & 0x0F); + cinfo->arith_dc_U[index] = (UINT8) (val >> 4); + if (cinfo->arith_dc_L[index] > cinfo->arith_dc_U[index]) + ERREXIT1(cinfo, JERR_DAC_VALUE, val); + } + } + + INPUT_SYNC(cinfo); + return TRUE; +} + + +LOCAL boolean +get_dht (j_decompress_ptr cinfo) +/* Process a DHT marker */ +{ + INT32 length; + UINT8 bits[17]; + UINT8 huffval[256]; + int i, index, count; + JHUFF_TBL **htblptr; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + length -= 2; + + while (length > 0) { + INPUT_BYTE(cinfo, index, return FALSE); + + TRACEMS1(cinfo, 1, JTRC_DHT, index); + + bits[0] = 0; + count = 0; + for (i = 1; i <= 16; i++) { + INPUT_BYTE(cinfo, bits[i], return FALSE); + count += bits[i]; + } + + length -= 1 + 16; + + TRACEMS8(cinfo, 2, JTRC_HUFFBITS, + bits[1], bits[2], bits[3], bits[4], + bits[5], bits[6], bits[7], bits[8]); + TRACEMS8(cinfo, 2, JTRC_HUFFBITS, + bits[9], bits[10], bits[11], bits[12], + bits[13], bits[14], bits[15], bits[16]); + + if (count > 256 || ((INT32) count) > length) + ERREXIT(cinfo, JERR_DHT_COUNTS); + + for (i = 0; i < count; i++) + INPUT_BYTE(cinfo, huffval[i], return FALSE); + + length -= count; + + if (index & 0x10) { /* AC table definition */ + index -= 0x10; + htblptr = &cinfo->ac_huff_tbl_ptrs[index]; + } else { /* DC table definition */ + htblptr = &cinfo->dc_huff_tbl_ptrs[index]; + } + + if (index < 0 || index >= NUM_HUFF_TBLS) + ERREXIT1(cinfo, JERR_DHT_INDEX, index); + + if (*htblptr == NULL) + *htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo); + + MEMCOPY((*htblptr)->bits, bits, SIZEOF((*htblptr)->bits)); + MEMCOPY((*htblptr)->huffval, huffval, SIZEOF((*htblptr)->huffval)); + } + + INPUT_SYNC(cinfo); + return TRUE; +} + + +LOCAL boolean +get_dqt (j_decompress_ptr cinfo) +/* Process a DQT marker */ +{ + INT32 length; + int n, i, prec; + unsigned int tmp; + JQUANT_TBL *quant_ptr; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + length -= 2; + + while (length > 0) { + INPUT_BYTE(cinfo, n, return FALSE); + prec = n >> 4; + n &= 0x0F; + + TRACEMS2(cinfo, 1, JTRC_DQT, n, prec); + + if (n >= NUM_QUANT_TBLS) + ERREXIT1(cinfo, JERR_DQT_INDEX, n); + + if (cinfo->quant_tbl_ptrs[n] == NULL) + cinfo->quant_tbl_ptrs[n] = jpeg_alloc_quant_table((j_common_ptr) cinfo); + quant_ptr = cinfo->quant_tbl_ptrs[n]; + + for (i = 0; i < DCTSIZE2; i++) { + if (prec) + INPUT_2BYTES(cinfo, tmp, return FALSE); + else + INPUT_BYTE(cinfo, tmp, return FALSE); + quant_ptr->quantval[i] = (UINT16) tmp; + } + + for (i = 0; i < DCTSIZE2; i += 8) { + TRACEMS8(cinfo, 2, JTRC_QUANTVALS, + quant_ptr->quantval[i ], quant_ptr->quantval[i+1], + quant_ptr->quantval[i+2], quant_ptr->quantval[i+3], + quant_ptr->quantval[i+4], quant_ptr->quantval[i+5], + quant_ptr->quantval[i+6], quant_ptr->quantval[i+7]); + } + + length -= DCTSIZE2+1; + if (prec) length -= DCTSIZE2; + } + + INPUT_SYNC(cinfo); + return TRUE; +} + + +LOCAL boolean +get_dri (j_decompress_ptr cinfo) +/* Process a DRI marker */ +{ + INT32 length; + unsigned int tmp; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + + if (length != 4) + ERREXIT(cinfo, JERR_BAD_LENGTH); + + INPUT_2BYTES(cinfo, tmp, return FALSE); + + TRACEMS1(cinfo, 1, JTRC_DRI, tmp); + + cinfo->restart_interval = tmp; + + INPUT_SYNC(cinfo); + return TRUE; +} + + +METHODDEF boolean +skip_variable (j_decompress_ptr cinfo) +/* Skip over an unknown or uninteresting variable-length marker */ +{ + INT32 length; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + + TRACEMS2(cinfo, 1, JTRC_MISC_MARKER, cinfo->unread_marker, (int) length); + + INPUT_SYNC(cinfo); /* do before skip_input_data */ + (*cinfo->src->skip_input_data) (cinfo, (long) length - 2L); + + return TRUE; +} + + +/* + * Find the next JPEG marker, save it in cinfo->unread_marker. + * Returns FALSE if had to suspend before reaching a marker; + * in that case cinfo->unread_marker is unchanged. + * + * Note that the result might not be a valid marker code, + * but it will never be 0 or FF. + */ + +LOCAL boolean +next_marker (j_decompress_ptr cinfo) +{ + int c; + INPUT_VARS(cinfo); + + for (;;) { + INPUT_BYTE(cinfo, c, return FALSE); + /* Skip any non-FF bytes. + * This may look a bit inefficient, but it will not occur in a valid file. + * We sync after each discarded byte so that a suspending data source + * can discard the byte from its buffer. + */ + while (c != 0xFF) { + cinfo->marker->discarded_bytes++; + INPUT_SYNC(cinfo); + INPUT_BYTE(cinfo, c, return FALSE); + } + /* This loop swallows any duplicate FF bytes. Extra FFs are legal as + * pad bytes, so don't count them in discarded_bytes. We assume there + * will not be so many consecutive FF bytes as to overflow a suspending + * data source's input buffer. + */ + do { + INPUT_BYTE(cinfo, c, return FALSE); + } while (c == 0xFF); + if (c != 0) + break; /* found a valid marker, exit loop */ + /* Reach here if we found a stuffed-zero data sequence (FF/00). + * Discard it and loop back to try again. + */ + cinfo->marker->discarded_bytes += 2; + INPUT_SYNC(cinfo); + } + + if (cinfo->marker->discarded_bytes != 0) { + WARNMS2(cinfo, JWRN_EXTRANEOUS_DATA, cinfo->marker->discarded_bytes, c); + cinfo->marker->discarded_bytes = 0; + } + + cinfo->unread_marker = c; + + INPUT_SYNC(cinfo); + return TRUE; +} + + +LOCAL boolean +first_marker (j_decompress_ptr cinfo) +/* Like next_marker, but used to obtain the initial SOI marker. */ +/* For this marker, we do not allow preceding garbage or fill; otherwise, + * we might well scan an entire input file before realizing it ain't JPEG. + * If an application wants to process non-JFIF files, it must seek to the + * SOI before calling the JPEG library. + */ +{ + int c, c2; + INPUT_VARS(cinfo); + + INPUT_BYTE(cinfo, c, return FALSE); + INPUT_BYTE(cinfo, c2, return FALSE); + if (c != 0xFF || c2 != (int) M_SOI) + ERREXIT2(cinfo, JERR_NO_SOI, c, c2); + + cinfo->unread_marker = c2; + + INPUT_SYNC(cinfo); + return TRUE; +} + + +/* + * Read markers until SOS or EOI. + * + * Returns same codes as are defined for jpeg_consume_input: + * JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI. + */ + +METHODDEF int +read_markers (j_decompress_ptr cinfo) +{ + /* Outer loop repeats once for each marker. */ + for (;;) { + /* Collect the marker proper, unless we already did. */ + /* NB: first_marker() enforces the requirement that SOI appear first. */ + if (cinfo->unread_marker == 0) { + if (! cinfo->marker->saw_SOI) { + if (! first_marker(cinfo)) + return JPEG_SUSPENDED; + } else { + if (! next_marker(cinfo)) + return JPEG_SUSPENDED; + } + } + /* At this point cinfo->unread_marker contains the marker code and the + * input point is just past the marker proper, but before any parameters. + * A suspension will cause us to return with this state still true. + */ + switch (cinfo->unread_marker) { + case M_SOI: + if (! get_soi(cinfo)) + return JPEG_SUSPENDED; + break; + + case M_SOF0: /* Baseline */ + case M_SOF1: /* Extended sequential, Huffman */ + if (! get_sof(cinfo, FALSE, FALSE)) + return JPEG_SUSPENDED; + break; + + case M_SOF2: /* Progressive, Huffman */ + if (! get_sof(cinfo, TRUE, FALSE)) + return JPEG_SUSPENDED; + break; + + case M_SOF9: /* Extended sequential, arithmetic */ + if (! get_sof(cinfo, FALSE, TRUE)) + return JPEG_SUSPENDED; + break; + + case M_SOF10: /* Progressive, arithmetic */ + if (! get_sof(cinfo, TRUE, TRUE)) + return JPEG_SUSPENDED; + break; + + /* Currently unsupported SOFn types */ + case M_SOF3: /* Lossless, Huffman */ + case M_SOF5: /* Differential sequential, Huffman */ + case M_SOF6: /* Differential progressive, Huffman */ + case M_SOF7: /* Differential lossless, Huffman */ + case M_JPG: /* Reserved for JPEG extensions */ + case M_SOF11: /* Lossless, arithmetic */ + case M_SOF13: /* Differential sequential, arithmetic */ + case M_SOF14: /* Differential progressive, arithmetic */ + case M_SOF15: /* Differential lossless, arithmetic */ + ERREXIT1(cinfo, JERR_SOF_UNSUPPORTED, cinfo->unread_marker); + break; + + case M_SOS: + if (! get_sos(cinfo)) + return JPEG_SUSPENDED; + cinfo->unread_marker = 0; /* processed the marker */ + return JPEG_REACHED_SOS; + + case M_EOI: + TRACEMS(cinfo, 1, JTRC_EOI); + cinfo->unread_marker = 0; /* processed the marker */ + return JPEG_REACHED_EOI; + + case M_DAC: + if (! get_dac(cinfo)) + return JPEG_SUSPENDED; + break; + + case M_DHT: + if (! get_dht(cinfo)) + return JPEG_SUSPENDED; + break; + + case M_DQT: + if (! get_dqt(cinfo)) + return JPEG_SUSPENDED; + break; + + case M_DRI: + if (! get_dri(cinfo)) + return JPEG_SUSPENDED; + break; + + case M_APP0: + case M_APP1: + case M_APP2: + case M_APP3: + case M_APP4: + case M_APP5: + case M_APP6: + case M_APP7: + case M_APP8: + case M_APP9: + case M_APP10: + case M_APP11: + case M_APP12: + case M_APP13: + case M_APP14: + case M_APP15: + if (! (*cinfo->marker->process_APPn[cinfo->unread_marker - (int) M_APP0]) (cinfo)) + return JPEG_SUSPENDED; + break; + + case M_COM: + if (! (*cinfo->marker->process_COM) (cinfo)) + return JPEG_SUSPENDED; + break; + + case M_RST0: /* these are all parameterless */ + case M_RST1: + case M_RST2: + case M_RST3: + case M_RST4: + case M_RST5: + case M_RST6: + case M_RST7: + case M_TEM: + TRACEMS1(cinfo, 1, JTRC_PARMLESS_MARKER, cinfo->unread_marker); + break; + + case M_DNL: /* Ignore DNL ... perhaps the wrong thing */ + if (! skip_variable(cinfo)) + return JPEG_SUSPENDED; + break; + + default: /* must be DHP, EXP, JPGn, or RESn */ + /* For now, we treat the reserved markers as fatal errors since they are + * likely to be used to signal incompatible JPEG Part 3 extensions. + * Once the JPEG 3 version-number marker is well defined, this code + * ought to change! + */ + ERREXIT1(cinfo, JERR_UNKNOWN_MARKER, cinfo->unread_marker); + break; + } + /* Successfully processed marker, so reset state variable */ + cinfo->unread_marker = 0; + } /* end loop */ +} + + +/* + * Read a restart marker, which is expected to appear next in the datastream; + * if the marker is not there, take appropriate recovery action. + * Returns FALSE if suspension is required. + * + * This is called by the entropy decoder after it has read an appropriate + * number of MCUs. cinfo->unread_marker may be nonzero if the entropy decoder + * has already read a marker from the data source. Under normal conditions + * cinfo->unread_marker will be reset to 0 before returning; if not reset, + * it holds a marker which the decoder will be unable to read past. + */ + +METHODDEF boolean +read_restart_marker (j_decompress_ptr cinfo) +{ + /* Obtain a marker unless we already did. */ + /* Note that next_marker will complain if it skips any data. */ + if (cinfo->unread_marker == 0) { + if (! next_marker(cinfo)) + return FALSE; + } + + if (cinfo->unread_marker == + ((int) M_RST0 + cinfo->marker->next_restart_num)) { + /* Normal case --- swallow the marker and let entropy decoder continue */ + TRACEMS1(cinfo, 2, JTRC_RST, cinfo->marker->next_restart_num); + cinfo->unread_marker = 0; + } else { + /* Uh-oh, the restart markers have been messed up. */ + /* Let the data source manager determine how to resync. */ + if (! (*cinfo->src->resync_to_restart) (cinfo, + cinfo->marker->next_restart_num)) + return FALSE; + } + + /* Update next-restart state */ + cinfo->marker->next_restart_num = (cinfo->marker->next_restart_num + 1) & 7; + + return TRUE; +} + + +/* + * This is the default resync_to_restart method for data source managers + * to use if they don't have any better approach. Some data source managers + * may be able to back up, or may have additional knowledge about the data + * which permits a more intelligent recovery strategy; such managers would + * presumably supply their own resync method. + * + * read_restart_marker calls resync_to_restart if it finds a marker other than + * the restart marker it was expecting. (This code is *not* used unless + * a nonzero restart interval has been declared.) cinfo->unread_marker is + * the marker code actually found (might be anything, except 0 or FF). + * The desired restart marker number (0..7) is passed as a parameter. + * This routine is supposed to apply whatever error recovery strategy seems + * appropriate in order to position the input stream to the next data segment. + * Note that cinfo->unread_marker is treated as a marker appearing before + * the current data-source input point; usually it should be reset to zero + * before returning. + * Returns FALSE if suspension is required. + * + * This implementation is substantially constrained by wanting to treat the + * input as a data stream; this means we can't back up. Therefore, we have + * only the following actions to work with: + * 1. Simply discard the marker and let the entropy decoder resume at next + * byte of file. + * 2. Read forward until we find another marker, discarding intervening + * data. (In theory we could look ahead within the current bufferload, + * without having to discard data if we don't find the desired marker. + * This idea is not implemented here, in part because it makes behavior + * dependent on buffer size and chance buffer-boundary positions.) + * 3. Leave the marker unread (by failing to zero cinfo->unread_marker). + * This will cause the entropy decoder to process an empty data segment, + * inserting dummy zeroes, and then we will reprocess the marker. + * + * #2 is appropriate if we think the desired marker lies ahead, while #3 is + * appropriate if the found marker is a future restart marker (indicating + * that we have missed the desired restart marker, probably because it got + * corrupted). + * We apply #2 or #3 if the found marker is a restart marker no more than + * two counts behind or ahead of the expected one. We also apply #2 if the + * found marker is not a legal JPEG marker code (it's certainly bogus data). + * If the found marker is a restart marker more than 2 counts away, we do #1 + * (too much risk that the marker is erroneous; with luck we will be able to + * resync at some future point). + * For any valid non-restart JPEG marker, we apply #3. This keeps us from + * overrunning the end of a scan. An implementation limited to single-scan + * files might find it better to apply #2 for markers other than EOI, since + * any other marker would have to be bogus data in that case. + */ + +GLOBAL boolean +jpeg_resync_to_restart (j_decompress_ptr cinfo, int desired) +{ + int marker = cinfo->unread_marker; + int action = 1; + + /* Always put up a warning. */ + WARNMS2(cinfo, JWRN_MUST_RESYNC, marker, desired); + + /* Outer loop handles repeated decision after scanning forward. */ + for (;;) { + if (marker < (int) M_SOF0) + action = 2; /* invalid marker */ + else if (marker < (int) M_RST0 || marker > (int) M_RST7) + action = 3; /* valid non-restart marker */ + else { + if (marker == ((int) M_RST0 + ((desired+1) & 7)) || + marker == ((int) M_RST0 + ((desired+2) & 7))) + action = 3; /* one of the next two expected restarts */ + else if (marker == ((int) M_RST0 + ((desired-1) & 7)) || + marker == ((int) M_RST0 + ((desired-2) & 7))) + action = 2; /* a prior restart, so advance */ + else + action = 1; /* desired restart or too far away */ + } + TRACEMS2(cinfo, 4, JTRC_RECOVERY_ACTION, marker, action); + switch (action) { + case 1: + /* Discard marker and let entropy decoder resume processing. */ + cinfo->unread_marker = 0; + return TRUE; + case 2: + /* Scan to the next marker, and repeat the decision loop. */ + if (! next_marker(cinfo)) + return FALSE; + marker = cinfo->unread_marker; + break; + case 3: + /* Return without advancing past this marker. */ + /* Entropy decoder will be forced to process an empty segment. */ + return TRUE; + } + } /* end loop */ +} + + +/* + * Reset marker processing state to begin a fresh datastream. + */ + +METHODDEF void +reset_marker_reader (j_decompress_ptr cinfo) +{ + cinfo->comp_info = NULL; /* until allocated by get_sof */ + cinfo->input_scan_number = 0; /* no SOS seen yet */ + cinfo->unread_marker = 0; /* no pending marker */ + cinfo->marker->saw_SOI = FALSE; /* set internal state too */ + cinfo->marker->saw_SOF = FALSE; + cinfo->marker->discarded_bytes = 0; +} + + +/* + * Initialize the marker reader module. + * This is called only once, when the decompression object is created. + */ + +GLOBAL void +jinit_marker_reader (j_decompress_ptr cinfo) +{ + int i; + + /* Create subobject in permanent pool */ + cinfo->marker = (struct jpeg_marker_reader *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + SIZEOF(struct jpeg_marker_reader)); + /* Initialize method pointers */ + cinfo->marker->reset_marker_reader = reset_marker_reader; + cinfo->marker->read_markers = read_markers; + cinfo->marker->read_restart_marker = read_restart_marker; + cinfo->marker->process_COM = skip_variable; + for (i = 0; i < 16; i++) + cinfo->marker->process_APPn[i] = skip_variable; + cinfo->marker->process_APPn[0] = get_app0; + cinfo->marker->process_APPn[14] = get_app14; + /* Reset marker processing state */ + reset_marker_reader(cinfo); +} diff --git a/tools/urt/libs/jpeg6/jdmaster.cpp b/tools/urt/libs/jpeg6/jdmaster.cpp new file mode 100644 index 00000000..9d88bb2f --- /dev/null +++ b/tools/urt/libs/jpeg6/jdmaster.cpp @@ -0,0 +1,558 @@ +/* + * jdmaster.c + * + * Copyright (C) 1991-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains master control logic for the JPEG decompressor. + * These routines are concerned with selecting the modules to be executed + * and with determining the number of passes and the work to be done in each + * pass. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "radiant_jpeglib.h" + + +/* Private state */ + +typedef struct { + struct jpeg_decomp_master pub; /* public fields */ + + int pass_number; /* # of passes completed */ + + boolean using_merged_upsample; /* TRUE if using merged upsample/cconvert */ + + /* Saved references to initialized quantizer modules, + * in case we need to switch modes. + */ + struct jpeg_color_quantizer * quantizer_1pass; + struct jpeg_color_quantizer * quantizer_2pass; +} my_decomp_master; + +typedef my_decomp_master * my_master_ptr; + + +/* + * Determine whether merged upsample/color conversion should be used. + * CRUCIAL: this must match the actual capabilities of jdmerge.c! + */ + +LOCAL boolean +use_merged_upsample (j_decompress_ptr cinfo) +{ +#ifdef UPSAMPLE_MERGING_SUPPORTED + /* Merging is the equivalent of plain box-filter upsampling */ + if (cinfo->do_fancy_upsampling || cinfo->CCIR601_sampling) + return FALSE; + /* jdmerge.c only supports YCC=>RGB color conversion */ + if (cinfo->jpeg_color_space != JCS_YCbCr || cinfo->num_components != 3 || + cinfo->out_color_space != JCS_RGB || + cinfo->out_color_components != RGB_PIXELSIZE) + return FALSE; + /* and it only handles 2h1v or 2h2v sampling ratios */ + if (cinfo->comp_info[0].h_samp_factor != 2 || + cinfo->comp_info[1].h_samp_factor != 1 || + cinfo->comp_info[2].h_samp_factor != 1 || + cinfo->comp_info[0].v_samp_factor > 2 || + cinfo->comp_info[1].v_samp_factor != 1 || + cinfo->comp_info[2].v_samp_factor != 1) + return FALSE; + /* furthermore, it doesn't work if we've scaled the IDCTs differently */ + if (cinfo->comp_info[0].DCT_scaled_size != cinfo->min_DCT_scaled_size || + cinfo->comp_info[1].DCT_scaled_size != cinfo->min_DCT_scaled_size || + cinfo->comp_info[2].DCT_scaled_size != cinfo->min_DCT_scaled_size) + return FALSE; + /* ??? also need to test for upsample-time rescaling, when & if supported */ + return TRUE; /* by golly, it'll work... */ +#else + return FALSE; +#endif +} + + +/* + * Compute output image dimensions and related values. + * NOTE: this is exported for possible use by application. + * Hence it mustn't do anything that can't be done twice. + * Also note that it may be called before the master module is initialized! + */ + +GLOBAL void +jpeg_calc_output_dimensions (j_decompress_ptr cinfo) +/* Do computations that are needed before master selection phase */ +{ +#if 0 // JDC: commented out to remove warning + int ci; + jpeg_component_info *compptr; +#endif + + /* Prevent application from calling me at wrong times */ + if (cinfo->global_state != DSTATE_READY) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + +#ifdef IDCT_SCALING_SUPPORTED + + /* Compute actual output image dimensions and DCT scaling choices. */ + if (cinfo->scale_num * 8 <= cinfo->scale_denom) { + /* Provide 1/8 scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width, 8L); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, 8L); + cinfo->min_DCT_scaled_size = 1; + } else if (cinfo->scale_num * 4 <= cinfo->scale_denom) { + /* Provide 1/4 scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width, 4L); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, 4L); + cinfo->min_DCT_scaled_size = 2; + } else if (cinfo->scale_num * 2 <= cinfo->scale_denom) { + /* Provide 1/2 scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width, 2L); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, 2L); + cinfo->min_DCT_scaled_size = 4; + } else { + /* Provide 1/1 scaling */ + cinfo->output_width = cinfo->image_width; + cinfo->output_height = cinfo->image_height; + cinfo->min_DCT_scaled_size = DCTSIZE; + } + /* In selecting the actual DCT scaling for each component, we try to + * scale up the chroma components via IDCT scaling rather than upsampling. + * This saves time if the upsampler gets to use 1:1 scaling. + * Note this code assumes that the supported DCT scalings are powers of 2. + */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + int ssize = cinfo->min_DCT_scaled_size; + while (ssize < DCTSIZE && + (compptr->h_samp_factor * ssize * 2 <= + cinfo->max_h_samp_factor * cinfo->min_DCT_scaled_size) && + (compptr->v_samp_factor * ssize * 2 <= + cinfo->max_v_samp_factor * cinfo->min_DCT_scaled_size)) { + ssize = ssize * 2; + } + compptr->DCT_scaled_size = ssize; + } + + /* Recompute downsampled dimensions of components; + * application needs to know these if using raw downsampled data. + */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Size in samples, after IDCT scaling */ + compptr->downsampled_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * + (long) (compptr->h_samp_factor * compptr->DCT_scaled_size), + (long) (cinfo->max_h_samp_factor * DCTSIZE)); + compptr->downsampled_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * + (long) (compptr->v_samp_factor * compptr->DCT_scaled_size), + (long) (cinfo->max_v_samp_factor * DCTSIZE)); + } + +#else /* !IDCT_SCALING_SUPPORTED */ + + /* Hardwire it to "no scaling" */ + cinfo->output_width = cinfo->image_width; + cinfo->output_height = cinfo->image_height; + /* jdinput.c has already initialized DCT_scaled_size to DCTSIZE, + * and has computed unscaled downsampled_width and downsampled_height. + */ + +#endif /* IDCT_SCALING_SUPPORTED */ + + /* Report number of components in selected colorspace. */ + /* Probably this should be in the color conversion module... */ + switch (cinfo->out_color_space) { + case JCS_GRAYSCALE: + cinfo->out_color_components = 1; + break; + case JCS_RGB: +#if RGB_PIXELSIZE != 3 + cinfo->out_color_components = RGB_PIXELSIZE; + break; +#endif /* else share code with YCbCr */ + case JCS_YCbCr: + cinfo->out_color_components = 3; + break; + case JCS_CMYK: + case JCS_YCCK: + cinfo->out_color_components = 4; + break; + default: /* else must be same colorspace as in file */ + cinfo->out_color_components = cinfo->num_components; + break; + } + cinfo->output_components = (cinfo->quantize_colors ? 1 : + cinfo->out_color_components); + + /* See if upsampler will want to emit more than one row at a time */ + if (use_merged_upsample(cinfo)) + cinfo->rec_outbuf_height = cinfo->max_v_samp_factor; + else + cinfo->rec_outbuf_height = 1; +} + + +/* + * Several decompression processes need to range-limit values to the range + * 0..MAXJSAMPLE; the input value may fall somewhat outside this range + * due to noise introduced by quantization, roundoff error, etc. These + * processes are inner loops and need to be as fast as possible. On most + * machines, particularly CPUs with pipelines or instruction prefetch, + * a (subscript-check-less) C table lookup + * x = sample_range_limit[x]; + * is faster than explicit tests + * if (x < 0) x = 0; + * else if (x > MAXJSAMPLE) x = MAXJSAMPLE; + * These processes all use a common table prepared by the routine below. + * + * For most steps we can mathematically guarantee that the initial value + * of x is within MAXJSAMPLE+1 of the legal range, so a table running from + * -(MAXJSAMPLE+1) to 2*MAXJSAMPLE+1 is sufficient. But for the initial + * limiting step (just after the IDCT), a wildly out-of-range value is + * possible if the input data is corrupt. To avoid any chance of indexing + * off the end of memory and getting a bad-pointer trap, we perform the + * post-IDCT limiting thus: + * x = range_limit[x & MASK]; + * where MASK is 2 bits wider than legal sample data, ie 10 bits for 8-bit + * samples. Under normal circumstances this is more than enough range and + * a correct output will be generated; with bogus input data the mask will + * cause wraparound, and we will safely generate a bogus-but-in-range output. + * For the post-IDCT step, we want to convert the data from signed to unsigned + * representation by adding CENTERJSAMPLE at the same time that we limit it. + * So the post-IDCT limiting table ends up looking like this: + * CENTERJSAMPLE,CENTERJSAMPLE+1,...,MAXJSAMPLE, + * MAXJSAMPLE (repeat 2*(MAXJSAMPLE+1)-CENTERJSAMPLE times), + * 0 (repeat 2*(MAXJSAMPLE+1)-CENTERJSAMPLE times), + * 0,1,...,CENTERJSAMPLE-1 + * Negative inputs select values from the upper half of the table after + * masking. + * + * We can save some space by overlapping the start of the post-IDCT table + * with the simpler range limiting table. The post-IDCT table begins at + * sample_range_limit + CENTERJSAMPLE. + * + * Note that the table is allocated in near data space on PCs; it's small + * enough and used often enough to justify this. + */ + +LOCAL void +prepare_range_limit_table (j_decompress_ptr cinfo) +/* Allocate and fill in the sample_range_limit table */ +{ + JSAMPLE * table; + int i; + + table = (JSAMPLE *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (5 * (MAXJSAMPLE+1) + CENTERJSAMPLE) * SIZEOF(JSAMPLE)); + table += (MAXJSAMPLE+1); /* allow negative subscripts of simple table */ + cinfo->sample_range_limit = table; + /* First segment of "simple" table: limit[x] = 0 for x < 0 */ + MEMZERO(table - (MAXJSAMPLE+1), (MAXJSAMPLE+1) * SIZEOF(JSAMPLE)); + /* Main part of "simple" table: limit[x] = x */ + for (i = 0; i <= MAXJSAMPLE; i++) + table[i] = (JSAMPLE) i; + table += CENTERJSAMPLE; /* Point to where post-IDCT table starts */ + /* End of simple table, rest of first half of post-IDCT table */ + for (i = CENTERJSAMPLE; i < 2*(MAXJSAMPLE+1); i++) + table[i] = MAXJSAMPLE; + /* Second half of post-IDCT table */ + MEMZERO(table + (2 * (MAXJSAMPLE+1)), + (2 * (MAXJSAMPLE+1) - CENTERJSAMPLE) * SIZEOF(JSAMPLE)); + MEMCOPY(table + (4 * (MAXJSAMPLE+1) - CENTERJSAMPLE), + cinfo->sample_range_limit, CENTERJSAMPLE * SIZEOF(JSAMPLE)); +} + + +/* + * Master selection of decompression modules. + * This is done once at jpeg_start_decompress time. We determine + * which modules will be used and give them appropriate initialization calls. + * We also initialize the decompressor input side to begin consuming data. + * + * Since jpeg_read_header has finished, we know what is in the SOF + * and (first) SOS markers. We also have all the application parameter + * settings. + */ + +LOCAL void +master_selection (j_decompress_ptr cinfo) +{ + my_master_ptr master = (my_master_ptr) cinfo->master; + boolean use_c_buffer; + long samplesperrow; + JDIMENSION jd_samplesperrow; + + /* Initialize dimensions and other stuff */ + jpeg_calc_output_dimensions(cinfo); + prepare_range_limit_table(cinfo); + + /* Width of an output scanline must be representable as JDIMENSION. */ + samplesperrow = (long) cinfo->output_width * (long) cinfo->out_color_components; + jd_samplesperrow = (JDIMENSION) samplesperrow; + if ((long) jd_samplesperrow != samplesperrow) + ERREXIT(cinfo, JERR_WIDTH_OVERFLOW); + + /* Initialize my private state */ + master->pass_number = 0; + master->using_merged_upsample = use_merged_upsample(cinfo); + + /* Color quantizer selection */ + master->quantizer_1pass = NULL; + master->quantizer_2pass = NULL; + /* No mode changes if not using buffered-image mode. */ + if (! cinfo->quantize_colors || ! cinfo->buffered_image) { + cinfo->enable_1pass_quant = FALSE; + cinfo->enable_external_quant = FALSE; + cinfo->enable_2pass_quant = FALSE; + } + if (cinfo->quantize_colors) { + if (cinfo->raw_data_out) + ERREXIT(cinfo, JERR_NOTIMPL); + /* 2-pass quantizer only works in 3-component color space. */ + if (cinfo->out_color_components != 3) { + cinfo->enable_1pass_quant = TRUE; + cinfo->enable_external_quant = FALSE; + cinfo->enable_2pass_quant = FALSE; + cinfo->colormap = NULL; + } else if (cinfo->colormap != NULL) { + cinfo->enable_external_quant = TRUE; + } else if (cinfo->two_pass_quantize) { + cinfo->enable_2pass_quant = TRUE; + } else { + cinfo->enable_1pass_quant = TRUE; + } + + if (cinfo->enable_1pass_quant) { +#ifdef QUANT_1PASS_SUPPORTED + jinit_1pass_quantizer(cinfo); + master->quantizer_1pass = cinfo->cquantize; +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } + + /* We use the 2-pass code to map to external colormaps. */ + if (cinfo->enable_2pass_quant || cinfo->enable_external_quant) { +#ifdef QUANT_2PASS_SUPPORTED + jinit_2pass_quantizer(cinfo); + master->quantizer_2pass = cinfo->cquantize; +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } + /* If both quantizers are initialized, the 2-pass one is left active; + * this is necessary for starting with quantization to an external map. + */ + } + + /* Post-processing: in particular, color conversion first */ + if (! cinfo->raw_data_out) { + if (master->using_merged_upsample) { +#ifdef UPSAMPLE_MERGING_SUPPORTED + jinit_merged_upsampler(cinfo); /* does color conversion too */ +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else { + jinit_color_deconverter(cinfo); + jinit_upsampler(cinfo); + } + jinit_d_post_controller(cinfo, cinfo->enable_2pass_quant); + } + /* Inverse DCT */ + jinit_inverse_dct(cinfo); + /* Entropy decoding: either Huffman or arithmetic coding. */ + if (cinfo->arith_code) { + ERREXIT(cinfo, JERR_ARITH_NOTIMPL); + } else { + if (cinfo->progressive_mode) { +#ifdef D_PROGRESSIVE_SUPPORTED + jinit_phuff_decoder(cinfo); +#else + ERREXIT(cinfo, JERR_NO_PROGRESSIVE); +#endif + } else + jinit_huff_decoder(cinfo); + } + + /* Initialize principal buffer controllers. */ + use_c_buffer = cinfo->inputctl->has_multiple_scans || cinfo->buffered_image; + jinit_d_coef_controller(cinfo, use_c_buffer); + + if (! cinfo->raw_data_out) + jinit_d_main_controller(cinfo, FALSE /* never need full buffer here */); + + /* We can now tell the memory manager to allocate virtual arrays. */ + (*cinfo->mem->realize_virt_arrays) ((j_common_ptr) cinfo); + + /* Initialize input side of decompressor to consume first scan. */ + (*cinfo->inputctl->start_input_pass) (cinfo); + +#ifdef D_MULTISCAN_FILES_SUPPORTED + /* If jpeg_start_decompress will read the whole file, initialize + * progress monitoring appropriately. The input step is counted + * as one pass. + */ + if (cinfo->progress != NULL && ! cinfo->buffered_image && + cinfo->inputctl->has_multiple_scans) { + int nscans; + /* Estimate number of scans to set pass_limit. */ + if (cinfo->progressive_mode) { + /* Arbitrarily estimate 2 interleaved DC scans + 3 AC scans/component. */ + nscans = 2 + 3 * cinfo->num_components; + } else { + /* For a nonprogressive multiscan file, estimate 1 scan per component. */ + nscans = cinfo->num_components; + } + cinfo->progress->pass_counter = 0L; + cinfo->progress->pass_limit = (long) cinfo->total_iMCU_rows * nscans; + cinfo->progress->completed_passes = 0; + cinfo->progress->total_passes = (cinfo->enable_2pass_quant ? 3 : 2); + /* Count the input pass as done */ + master->pass_number++; + } +#endif /* D_MULTISCAN_FILES_SUPPORTED */ +} + + +/* + * Per-pass setup. + * This is called at the beginning of each output pass. We determine which + * modules will be active during this pass and give them appropriate + * start_pass calls. We also set is_dummy_pass to indicate whether this + * is a "real" output pass or a dummy pass for color quantization. + * (In the latter case, jdapi.c will crank the pass to completion.) + */ + +METHODDEF void +prepare_for_output_pass (j_decompress_ptr cinfo) +{ + my_master_ptr master = (my_master_ptr) cinfo->master; + + if (master->pub.is_dummy_pass) { +#ifdef QUANT_2PASS_SUPPORTED + /* Final pass of 2-pass quantization */ + master->pub.is_dummy_pass = FALSE; + (*cinfo->cquantize->start_pass) (cinfo, FALSE); + (*cinfo->post->start_pass) (cinfo, JBUF_CRANK_DEST); + (*cinfo->main->start_pass) (cinfo, JBUF_CRANK_DEST); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif /* QUANT_2PASS_SUPPORTED */ + } else { + if (cinfo->quantize_colors && cinfo->colormap == NULL) { + /* Select new quantization method */ + if (cinfo->two_pass_quantize && cinfo->enable_2pass_quant) { + cinfo->cquantize = master->quantizer_2pass; + master->pub.is_dummy_pass = TRUE; + } else if (cinfo->enable_1pass_quant) { + cinfo->cquantize = master->quantizer_1pass; + } else { + ERREXIT(cinfo, JERR_MODE_CHANGE); + } + } + (*cinfo->idct->start_pass) (cinfo); + (*cinfo->coef->start_output_pass) (cinfo); + if (! cinfo->raw_data_out) { + if (! master->using_merged_upsample) + (*cinfo->cconvert->start_pass) (cinfo); + (*cinfo->upsample->start_pass) (cinfo); + if (cinfo->quantize_colors) + (*cinfo->cquantize->start_pass) (cinfo, master->pub.is_dummy_pass); + (*cinfo->post->start_pass) (cinfo, + (master->pub.is_dummy_pass ? JBUF_SAVE_AND_PASS : JBUF_PASS_THRU)); + (*cinfo->main->start_pass) (cinfo, JBUF_PASS_THRU); + } + } + + /* Set up progress monitor's pass info if present */ + if (cinfo->progress != NULL) { + cinfo->progress->completed_passes = master->pass_number; + cinfo->progress->total_passes = master->pass_number + + (master->pub.is_dummy_pass ? 2 : 1); + /* In buffered-image mode, we assume one more output pass if EOI not + * yet reached, but no more passes if EOI has been reached. + */ + if (cinfo->buffered_image && ! cinfo->inputctl->eoi_reached) { + cinfo->progress->total_passes += (cinfo->enable_2pass_quant ? 2 : 1); + } + } +} + + +/* + * Finish up at end of an output pass. + */ + +METHODDEF void +finish_output_pass (j_decompress_ptr cinfo) +{ + my_master_ptr master = (my_master_ptr) cinfo->master; + + if (cinfo->quantize_colors) + (*cinfo->cquantize->finish_pass) (cinfo); + master->pass_number++; +} + + +#ifdef D_MULTISCAN_FILES_SUPPORTED + +/* + * Switch to a new external colormap between output passes. + */ + +GLOBAL void +jpeg_new_colormap (j_decompress_ptr cinfo) +{ + my_master_ptr master = (my_master_ptr) cinfo->master; + + /* Prevent application from calling me at wrong times */ + if (cinfo->global_state != DSTATE_BUFIMAGE) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + if (cinfo->quantize_colors && cinfo->enable_external_quant && + cinfo->colormap != NULL) { + /* Select 2-pass quantizer for external colormap use */ + cinfo->cquantize = master->quantizer_2pass; + /* Notify quantizer of colormap change */ + (*cinfo->cquantize->new_color_map) (cinfo); + master->pub.is_dummy_pass = FALSE; /* just in case */ + } else + ERREXIT(cinfo, JERR_MODE_CHANGE); +} + +#endif /* D_MULTISCAN_FILES_SUPPORTED */ + + +/* + * Initialize master decompression control and select active modules. + * This is performed at the start of jpeg_start_decompress. + */ + +GLOBAL void +jinit_master_decompress (j_decompress_ptr cinfo) +{ + my_master_ptr master; + + master = (my_master_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_decomp_master)); + cinfo->master = (struct jpeg_decomp_master *) master; + master->pub.prepare_for_output_pass = prepare_for_output_pass; + master->pub.finish_output_pass = finish_output_pass; + + master->pub.is_dummy_pass = FALSE; + + master_selection(cinfo); +} + diff --git a/tools/urt/libs/jpeg6/jdpostct.cpp b/tools/urt/libs/jpeg6/jdpostct.cpp new file mode 100644 index 00000000..3aba0a38 --- /dev/null +++ b/tools/urt/libs/jpeg6/jdpostct.cpp @@ -0,0 +1,580 @@ +/* + + * jdpostct.c + + * + + * Copyright (C) 1994-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains the decompression postprocessing controller. + + * This controller manages the upsampling, color conversion, and color + + * quantization/reduction steps; specifically, it controls the buffering + + * between upsample/color conversion and color quantization/reduction. + + * + + * If no color quantization/reduction is required, then this module has no + + * work to do, and it just hands off to the upsample/color conversion code. + + * An integrated upsample/convert/quantize process would replace this module + + * entirely. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "radiant_jpeglib.h" + + + + + +/* Private buffer controller object */ + + + +typedef struct { + + struct jpeg_d_post_controller pub; /* public fields */ + + + + /* Color quantization source buffer: this holds output data from + + * the upsample/color conversion step to be passed to the quantizer. + + * For two-pass color quantization, we need a full-image buffer; + + * for one-pass operation, a strip buffer is sufficient. + + */ + + jvirt_sarray_ptr whole_image; /* virtual array, or NULL if one-pass */ + + JSAMPARRAY buffer; /* strip buffer, or current strip of virtual */ + + JDIMENSION strip_height; /* buffer size in rows */ + + /* for two-pass mode only: */ + + JDIMENSION starting_row; /* row # of first row in current strip */ + + JDIMENSION next_row; /* index of next row to fill/empty in strip */ + +} my_post_controller; + + + +typedef my_post_controller * my_post_ptr; + + + + + +/* Forward declarations */ + +METHODDEF void post_process_1pass + + JPP((j_decompress_ptr cinfo, + + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + + JDIMENSION in_row_groups_avail, + + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + + JDIMENSION out_rows_avail)); + +#ifdef QUANT_2PASS_SUPPORTED + +METHODDEF void post_process_prepass + + JPP((j_decompress_ptr cinfo, + + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + + JDIMENSION in_row_groups_avail, + + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + + JDIMENSION out_rows_avail)); + +METHODDEF void post_process_2pass + + JPP((j_decompress_ptr cinfo, + + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + + JDIMENSION in_row_groups_avail, + + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + + JDIMENSION out_rows_avail)); + +#endif + + + + + +/* + + * Initialize for a processing pass. + + */ + + + +METHODDEF void + +start_pass_dpost (j_decompress_ptr cinfo, J_BUF_MODE pass_mode) + +{ + + my_post_ptr post = (my_post_ptr) cinfo->post; + + + + switch (pass_mode) { + + case JBUF_PASS_THRU: + + if (cinfo->quantize_colors) { + + /* Single-pass processing with color quantization. */ + + post->pub.post_process_data = post_process_1pass; + + /* We could be doing buffered-image output before starting a 2-pass + + * color quantization; in that case, jinit_d_post_controller did not + + * allocate a strip buffer. Use the virtual-array buffer as workspace. + + */ + + if (post->buffer == NULL) { + + post->buffer = (*cinfo->mem->access_virt_sarray) + + ((j_common_ptr) cinfo, post->whole_image, + + (JDIMENSION) 0, post->strip_height, TRUE); + + } + + } else { + + /* For single-pass processing without color quantization, + + * I have no work to do; just call the upsampler directly. + + */ + + post->pub.post_process_data = cinfo->upsample->upsample; + + } + + break; + +#ifdef QUANT_2PASS_SUPPORTED + + case JBUF_SAVE_AND_PASS: + + /* First pass of 2-pass quantization */ + + if (post->whole_image == NULL) + + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + + post->pub.post_process_data = post_process_prepass; + + break; + + case JBUF_CRANK_DEST: + + /* Second pass of 2-pass quantization */ + + if (post->whole_image == NULL) + + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + + post->pub.post_process_data = post_process_2pass; + + break; + +#endif /* QUANT_2PASS_SUPPORTED */ + + default: + + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + + break; + + } + + post->starting_row = post->next_row = 0; + +} + + + + + +/* + + * Process some data in the one-pass (strip buffer) case. + + * This is used for color precision reduction as well as one-pass quantization. + + */ + + + +METHODDEF void + +post_process_1pass (j_decompress_ptr cinfo, + + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + + JDIMENSION in_row_groups_avail, + + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + + JDIMENSION out_rows_avail) + +{ + + my_post_ptr post = (my_post_ptr) cinfo->post; + + JDIMENSION num_rows, max_rows; + + + + /* Fill the buffer, but not more than what we can dump out in one go. */ + + /* Note we rely on the upsampler to detect bottom of image. */ + + max_rows = out_rows_avail - *out_row_ctr; + + if (max_rows > post->strip_height) + + max_rows = post->strip_height; + + num_rows = 0; + + (*cinfo->upsample->upsample) (cinfo, + + input_buf, in_row_group_ctr, in_row_groups_avail, + + post->buffer, &num_rows, max_rows); + + /* Quantize and emit data. */ + + (*cinfo->cquantize->color_quantize) (cinfo, + + post->buffer, output_buf + *out_row_ctr, (int) num_rows); + + *out_row_ctr += num_rows; + +} + + + + + +#ifdef QUANT_2PASS_SUPPORTED + + + +/* + + * Process some data in the first pass of 2-pass quantization. + + */ + + + +METHODDEF void + +post_process_prepass (j_decompress_ptr cinfo, + + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + + JDIMENSION in_row_groups_avail, + + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + + JDIMENSION out_rows_avail) + +{ + + my_post_ptr post = (my_post_ptr) cinfo->post; + + JDIMENSION old_next_row, num_rows; + + + + /* Reposition virtual buffer if at start of strip. */ + + if (post->next_row == 0) { + + post->buffer = (*cinfo->mem->access_virt_sarray) + + ((j_common_ptr) cinfo, post->whole_image, + + post->starting_row, post->strip_height, TRUE); + + } + + + + /* Upsample some data (up to a strip height's worth). */ + + old_next_row = post->next_row; + + (*cinfo->upsample->upsample) (cinfo, + + input_buf, in_row_group_ctr, in_row_groups_avail, + + post->buffer, &post->next_row, post->strip_height); + + + + /* Allow quantizer to scan new data. No data is emitted, */ + + /* but we advance out_row_ctr so outer loop can tell when we're done. */ + + if (post->next_row > old_next_row) { + + num_rows = post->next_row - old_next_row; + + (*cinfo->cquantize->color_quantize) (cinfo, post->buffer + old_next_row, + + (JSAMPARRAY) NULL, (int) num_rows); + + *out_row_ctr += num_rows; + + } + + + + /* Advance if we filled the strip. */ + + if (post->next_row >= post->strip_height) { + + post->starting_row += post->strip_height; + + post->next_row = 0; + + } + +} + + + + + +/* + + * Process some data in the second pass of 2-pass quantization. + + */ + + + +METHODDEF void + +post_process_2pass (j_decompress_ptr cinfo, + + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + + JDIMENSION in_row_groups_avail, + + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + + JDIMENSION out_rows_avail) + +{ + + my_post_ptr post = (my_post_ptr) cinfo->post; + + JDIMENSION num_rows, max_rows; + + + + /* Reposition virtual buffer if at start of strip. */ + + if (post->next_row == 0) { + + post->buffer = (*cinfo->mem->access_virt_sarray) + + ((j_common_ptr) cinfo, post->whole_image, + + post->starting_row, post->strip_height, FALSE); + + } + + + + /* Determine number of rows to emit. */ + + num_rows = post->strip_height - post->next_row; /* available in strip */ + + max_rows = out_rows_avail - *out_row_ctr; /* available in output area */ + + if (num_rows > max_rows) + + num_rows = max_rows; + + /* We have to check bottom of image here, can't depend on upsampler. */ + + max_rows = cinfo->output_height - post->starting_row; + + if (num_rows > max_rows) + + num_rows = max_rows; + + + + /* Quantize and emit data. */ + + (*cinfo->cquantize->color_quantize) (cinfo, + + post->buffer + post->next_row, output_buf + *out_row_ctr, + + (int) num_rows); + + *out_row_ctr += num_rows; + + + + /* Advance if we filled the strip. */ + + post->next_row += num_rows; + + if (post->next_row >= post->strip_height) { + + post->starting_row += post->strip_height; + + post->next_row = 0; + + } + +} + + + +#endif /* QUANT_2PASS_SUPPORTED */ + + + + + +/* + + * Initialize postprocessing controller. + + */ + + + +GLOBAL void + +jinit_d_post_controller (j_decompress_ptr cinfo, boolean need_full_buffer) + +{ + + my_post_ptr post; + + + + post = (my_post_ptr) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(my_post_controller)); + + cinfo->post = (struct jpeg_d_post_controller *) post; + + post->pub.start_pass = start_pass_dpost; + + post->whole_image = NULL; /* flag for no virtual arrays */ + + post->buffer = NULL; /* flag for no strip buffer */ + + + + /* Create the quantization buffer, if needed */ + + if (cinfo->quantize_colors) { + + /* The buffer strip height is max_v_samp_factor, which is typically + + * an efficient number of rows for upsampling to return. + + * (In the presence of output rescaling, we might want to be smarter?) + + */ + + post->strip_height = (JDIMENSION) cinfo->max_v_samp_factor; + + if (need_full_buffer) { + + /* Two-pass color quantization: need full-image storage. */ + + /* We round up the number of rows to a multiple of the strip height. */ + +#ifdef QUANT_2PASS_SUPPORTED + + post->whole_image = (*cinfo->mem->request_virt_sarray) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, + + cinfo->output_width * cinfo->out_color_components, + + (JDIMENSION) jround_up((long) cinfo->output_height, + + (long) post->strip_height), + + post->strip_height); + +#else + + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + +#endif /* QUANT_2PASS_SUPPORTED */ + + } else { + + /* One-pass color quantization: just make a strip buffer. */ + + post->buffer = (*cinfo->mem->alloc_sarray) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, + + cinfo->output_width * cinfo->out_color_components, + + post->strip_height); + + } + + } + +} + diff --git a/tools/urt/libs/jpeg6/jdsample.cpp b/tools/urt/libs/jpeg6/jdsample.cpp new file mode 100644 index 00000000..a15e252c --- /dev/null +++ b/tools/urt/libs/jpeg6/jdsample.cpp @@ -0,0 +1,956 @@ +/* + + * jdsample.c + + * + + * Copyright (C) 1991-1994, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains upsampling routines. + + * + + * Upsampling input data is counted in "row groups". A row group + + * is defined to be (v_samp_factor * DCT_scaled_size / min_DCT_scaled_size) + + * sample rows of each component. Upsampling will normally produce + + * max_v_samp_factor pixel rows from each row group (but this could vary + + * if the upsampler is applying a scale factor of its own). + + * + + * An excellent reference for image resampling is + + * Digital Image Warping, George Wolberg, 1990. + + * Pub. by IEEE Computer Society Press, Los Alamitos, CA. ISBN 0-8186-8944-7. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "radiant_jpeglib.h" + + + + + +/* Pointer to routine to upsample a single component */ + +typedef JMETHOD(void, upsample1_ptr, + + (j_decompress_ptr cinfo, jpeg_component_info * compptr, + + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr)); + + + +/* Private subobject */ + + + +typedef struct { + + struct jpeg_upsampler pub; /* public fields */ + + + + /* Color conversion buffer. When using separate upsampling and color + + * conversion steps, this buffer holds one upsampled row group until it + + * has been color converted and output. + + * Note: we do not allocate any storage for component(s) which are full-size, + + * ie do not need rescaling. The corresponding entry of color_buf[] is + + * simply set to point to the input data array, thereby avoiding copying. + + */ + + JSAMPARRAY color_buf[MAX_COMPONENTS]; + + + + /* Per-component upsampling method pointers */ + + upsample1_ptr methods[MAX_COMPONENTS]; + + + + int next_row_out; /* counts rows emitted from color_buf */ + + JDIMENSION rows_to_go; /* counts rows remaining in image */ + + + + /* Height of an input row group for each component. */ + + int rowgroup_height[MAX_COMPONENTS]; + + + + /* These arrays save pixel expansion factors so that int_expand need not + + * recompute them each time. They are unused for other upsampling methods. + + */ + + UINT8 h_expand[MAX_COMPONENTS]; + + UINT8 v_expand[MAX_COMPONENTS]; + +} my_upsampler; + + + +typedef my_upsampler * my_upsample_ptr; + + + + + +/* + + * Initialize for an upsampling pass. + + */ + + + +METHODDEF void + +start_pass_upsample (j_decompress_ptr cinfo) + +{ + + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + + + + /* Mark the conversion buffer empty */ + + upsample->next_row_out = cinfo->max_v_samp_factor; + + /* Initialize total-height counter for detecting bottom of image */ + + upsample->rows_to_go = cinfo->output_height; + +} + + + + + +/* + + * Control routine to do upsampling (and color conversion). + + * + + * In this version we upsample each component independently. + + * We upsample one row group into the conversion buffer, then apply + + * color conversion a row at a time. + + */ + + + +METHODDEF void + +sep_upsample (j_decompress_ptr cinfo, + + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + + JDIMENSION in_row_groups_avail, + + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + + JDIMENSION out_rows_avail) + +{ + + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + + int ci; + + jpeg_component_info * compptr; + + JDIMENSION num_rows; + + + + /* Fill the conversion buffer, if it's empty */ + + if (upsample->next_row_out >= cinfo->max_v_samp_factor) { + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + /* Invoke per-component upsample method. Notice we pass a POINTER + + * to color_buf[ci], so that fullsize_upsample can change it. + + */ + + (*upsample->methods[ci]) (cinfo, compptr, + + input_buf[ci] + (*in_row_group_ctr * upsample->rowgroup_height[ci]), + + upsample->color_buf + ci); + + } + + upsample->next_row_out = 0; + + } + + + + /* Color-convert and emit rows */ + + + + /* How many we have in the buffer: */ + + num_rows = (JDIMENSION) (cinfo->max_v_samp_factor - upsample->next_row_out); + + /* Not more than the distance to the end of the image. Need this test + + * in case the image height is not a multiple of max_v_samp_factor: + + */ + + if (num_rows > upsample->rows_to_go) + + num_rows = upsample->rows_to_go; + + /* And not more than what the client can accept: */ + + out_rows_avail -= *out_row_ctr; + + if (num_rows > out_rows_avail) + + num_rows = out_rows_avail; + + + + (*cinfo->cconvert->color_convert) (cinfo, upsample->color_buf, + + (JDIMENSION) upsample->next_row_out, + + output_buf + *out_row_ctr, + + (int) num_rows); + + + + /* Adjust counts */ + + *out_row_ctr += num_rows; + + upsample->rows_to_go -= num_rows; + + upsample->next_row_out += num_rows; + + /* When the buffer is emptied, declare this input row group consumed */ + + if (upsample->next_row_out >= cinfo->max_v_samp_factor) + + (*in_row_group_ctr)++; + +} + + + + + +/* + + * These are the routines invoked by sep_upsample to upsample pixel values + + * of a single component. One row group is processed per call. + + */ + + + + + +/* + + * For full-size components, we just make color_buf[ci] point at the + + * input buffer, and thus avoid copying any data. Note that this is + + * safe only because sep_upsample doesn't declare the input row group + + * "consumed" until we are done color converting and emitting it. + + */ + + + +METHODDEF void + +fullsize_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) + +{ + + *output_data_ptr = input_data; + +} + + + + + +/* + + * This is a no-op version used for "uninteresting" components. + + * These components will not be referenced by color conversion. + + */ + + + +METHODDEF void + +noop_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) + +{ + + *output_data_ptr = NULL; /* safety check */ + +} + + + + + +/* + + * This version handles any integral sampling ratios. + + * This is not used for typical JPEG files, so it need not be fast. + + * Nor, for that matter, is it particularly accurate: the algorithm is + + * simple replication of the input pixel onto the corresponding output + + * pixels. The hi-falutin sampling literature refers to this as a + + * "box filter". A box filter tends to introduce visible artifacts, + + * so if you are actually going to use 3:1 or 4:1 sampling ratios + + * you would be well advised to improve this code. + + */ + + + +METHODDEF void + +int_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) + +{ + + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + + JSAMPARRAY output_data = *output_data_ptr; + + register JSAMPROW inptr, outptr; + + register JSAMPLE invalue; + + register int h; + + JSAMPROW outend; + + int h_expand, v_expand; + + int inrow, outrow; + + + + h_expand = upsample->h_expand[compptr->component_index]; + + v_expand = upsample->v_expand[compptr->component_index]; + + + + inrow = outrow = 0; + + while (outrow < cinfo->max_v_samp_factor) { + + /* Generate one output row with proper horizontal expansion */ + + inptr = input_data[inrow]; + + outptr = output_data[outrow]; + + outend = outptr + cinfo->output_width; + + while (outptr < outend) { + + invalue = *inptr++; /* don't need GETJSAMPLE() here */ + + for (h = h_expand; h > 0; h--) { + + *outptr++ = invalue; + + } + + } + + /* Generate any additional output rows by duplicating the first one */ + + if (v_expand > 1) { + + jcopy_sample_rows(output_data, outrow, output_data, outrow+1, + + v_expand-1, cinfo->output_width); + + } + + inrow++; + + outrow += v_expand; + + } + +} + + + + + +/* + + * Fast processing for the common case of 2:1 horizontal and 1:1 vertical. + + * It's still a box filter. + + */ + + + +METHODDEF void + +h2v1_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) + +{ + + JSAMPARRAY output_data = *output_data_ptr; + + register JSAMPROW inptr, outptr; + + register JSAMPLE invalue; + + JSAMPROW outend; + + int inrow; + + + + for (inrow = 0; inrow < cinfo->max_v_samp_factor; inrow++) { + + inptr = input_data[inrow]; + + outptr = output_data[inrow]; + + outend = outptr + cinfo->output_width; + + while (outptr < outend) { + + invalue = *inptr++; /* don't need GETJSAMPLE() here */ + + *outptr++ = invalue; + + *outptr++ = invalue; + + } + + } + +} + + + + + +/* + + * Fast processing for the common case of 2:1 horizontal and 2:1 vertical. + + * It's still a box filter. + + */ + + + +METHODDEF void + +h2v2_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) + +{ + + JSAMPARRAY output_data = *output_data_ptr; + + register JSAMPROW inptr, outptr; + + register JSAMPLE invalue; + + JSAMPROW outend; + + int inrow, outrow; + + + + inrow = outrow = 0; + + while (outrow < cinfo->max_v_samp_factor) { + + inptr = input_data[inrow]; + + outptr = output_data[outrow]; + + outend = outptr + cinfo->output_width; + + while (outptr < outend) { + + invalue = *inptr++; /* don't need GETJSAMPLE() here */ + + *outptr++ = invalue; + + *outptr++ = invalue; + + } + + jcopy_sample_rows(output_data, outrow, output_data, outrow+1, + + 1, cinfo->output_width); + + inrow++; + + outrow += 2; + + } + +} + + + + + +/* + + * Fancy processing for the common case of 2:1 horizontal and 1:1 vertical. + + * + + * The upsampling algorithm is linear interpolation between pixel centers, + + * also known as a "triangle filter". This is a good compromise between + + * speed and visual quality. The centers of the output pixels are 1/4 and 3/4 + + * of the way between input pixel centers. + + * + + * A note about the "bias" calculations: when rounding fractional values to + + * integer, we do not want to always round 0.5 up to the next integer. + + * If we did that, we'd introduce a noticeable bias towards larger values. + + * Instead, this code is arranged so that 0.5 will be rounded up or down at + + * alternate pixel locations (a simple ordered dither pattern). + + */ + + + +METHODDEF void + +h2v1_fancy_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) + +{ + + JSAMPARRAY output_data = *output_data_ptr; + + register JSAMPROW inptr, outptr; + + register int invalue; + + register JDIMENSION colctr; + + int inrow; + + + + for (inrow = 0; inrow < cinfo->max_v_samp_factor; inrow++) { + + inptr = input_data[inrow]; + + outptr = output_data[inrow]; + + /* Special case for first column */ + + invalue = GETJSAMPLE(*inptr++); + + *outptr++ = (JSAMPLE) invalue; + + *outptr++ = (JSAMPLE) ((invalue * 3 + GETJSAMPLE(*inptr) + 2) >> 2); + + + + for (colctr = compptr->downsampled_width - 2; colctr > 0; colctr--) { + + /* General case: 3/4 * nearer pixel + 1/4 * further pixel */ + + invalue = GETJSAMPLE(*inptr++) * 3; + + *outptr++ = (JSAMPLE) ((invalue + GETJSAMPLE(inptr[-2]) + 1) >> 2); + + *outptr++ = (JSAMPLE) ((invalue + GETJSAMPLE(*inptr) + 2) >> 2); + + } + + + + /* Special case for last column */ + + invalue = GETJSAMPLE(*inptr); + + *outptr++ = (JSAMPLE) ((invalue * 3 + GETJSAMPLE(inptr[-1]) + 1) >> 2); + + *outptr++ = (JSAMPLE) invalue; + + } + +} + + + + + +/* + + * Fancy processing for the common case of 2:1 horizontal and 2:1 vertical. + + * Again a triangle filter; see comments for h2v1 case, above. + + * + + * It is OK for us to reference the adjacent input rows because we demanded + + * context from the main buffer controller (see initialization code). + + */ + + + +METHODDEF void + +h2v2_fancy_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) + +{ + + JSAMPARRAY output_data = *output_data_ptr; + + register JSAMPROW inptr0, inptr1, outptr; + +#if BITS_IN_JSAMPLE == 8 + + register int thiscolsum, lastcolsum, nextcolsum; + +#else + + register INT32 thiscolsum, lastcolsum, nextcolsum; + +#endif + + register JDIMENSION colctr; + + int inrow, outrow, v; + + + + inrow = outrow = 0; + + while (outrow < cinfo->max_v_samp_factor) { + + for (v = 0; v < 2; v++) { + + /* inptr0 points to nearest input row, inptr1 points to next nearest */ + + inptr0 = input_data[inrow]; + + if (v == 0) /* next nearest is row above */ + + inptr1 = input_data[inrow-1]; + + else /* next nearest is row below */ + + inptr1 = input_data[inrow+1]; + + outptr = output_data[outrow++]; + + + + /* Special case for first column */ + + thiscolsum = GETJSAMPLE(*inptr0++) * 3 + GETJSAMPLE(*inptr1++); + + nextcolsum = GETJSAMPLE(*inptr0++) * 3 + GETJSAMPLE(*inptr1++); + + *outptr++ = (JSAMPLE) ((thiscolsum * 4 + 8) >> 4); + + *outptr++ = (JSAMPLE) ((thiscolsum * 3 + nextcolsum + 7) >> 4); + + lastcolsum = thiscolsum; thiscolsum = nextcolsum; + + + + for (colctr = compptr->downsampled_width - 2; colctr > 0; colctr--) { + + /* General case: 3/4 * nearer pixel + 1/4 * further pixel in each */ + + /* dimension, thus 9/16, 3/16, 3/16, 1/16 overall */ + + nextcolsum = GETJSAMPLE(*inptr0++) * 3 + GETJSAMPLE(*inptr1++); + + *outptr++ = (JSAMPLE) ((thiscolsum * 3 + lastcolsum + 8) >> 4); + + *outptr++ = (JSAMPLE) ((thiscolsum * 3 + nextcolsum + 7) >> 4); + + lastcolsum = thiscolsum; thiscolsum = nextcolsum; + + } + + + + /* Special case for last column */ + + *outptr++ = (JSAMPLE) ((thiscolsum * 3 + lastcolsum + 8) >> 4); + + *outptr++ = (JSAMPLE) ((thiscolsum * 4 + 7) >> 4); + + } + + inrow++; + + } + +} + + + + + +/* + + * Module initialization routine for upsampling. + + */ + + + +GLOBAL void + +jinit_upsampler (j_decompress_ptr cinfo) + +{ + + my_upsample_ptr upsample; + + int ci; + + jpeg_component_info * compptr; + + boolean need_buffer, do_fancy; + + int h_in_group, v_in_group, h_out_group, v_out_group; + + + + upsample = (my_upsample_ptr) + + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + + SIZEOF(my_upsampler)); + + cinfo->upsample = (struct jpeg_upsampler *) upsample; + + upsample->pub.start_pass = start_pass_upsample; + + upsample->pub.upsample = sep_upsample; + + upsample->pub.need_context_rows = FALSE; /* until we find out differently */ + + + + if (cinfo->CCIR601_sampling) /* this isn't supported */ + + ERREXIT(cinfo, JERR_CCIR601_NOTIMPL); + + + + /* jdmainct.c doesn't support context rows when min_DCT_scaled_size = 1, + + * so don't ask for it. + + */ + + do_fancy = cinfo->do_fancy_upsampling && cinfo->min_DCT_scaled_size > 1; + + + + /* Verify we can handle the sampling factors, select per-component methods, + + * and create storage as needed. + + */ + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + + ci++, compptr++) { + + /* Compute size of an "input group" after IDCT scaling. This many samples + + * are to be converted to max_h_samp_factor * max_v_samp_factor pixels. + + */ + + h_in_group = (compptr->h_samp_factor * compptr->DCT_scaled_size) / + + cinfo->min_DCT_scaled_size; + + v_in_group = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + + cinfo->min_DCT_scaled_size; + + h_out_group = cinfo->max_h_samp_factor; + + v_out_group = cinfo->max_v_samp_factor; + + upsample->rowgroup_height[ci] = v_in_group; /* save for use later */ + + need_buffer = TRUE; + + if (! compptr->component_needed) { + + /* Don't bother to upsample an uninteresting component. */ + + upsample->methods[ci] = noop_upsample; + + need_buffer = FALSE; + + } else if (h_in_group == h_out_group && v_in_group == v_out_group) { + + /* Fullsize components can be processed without any work. */ + + upsample->methods[ci] = fullsize_upsample; + + need_buffer = FALSE; + + } else if (h_in_group * 2 == h_out_group && + + v_in_group == v_out_group) { + + /* Special cases for 2h1v upsampling */ + + if (do_fancy && compptr->downsampled_width > 2) + + upsample->methods[ci] = h2v1_fancy_upsample; + + else + + upsample->methods[ci] = h2v1_upsample; + + } else if (h_in_group * 2 == h_out_group && + + v_in_group * 2 == v_out_group) { + + /* Special cases for 2h2v upsampling */ + + if (do_fancy && compptr->downsampled_width > 2) { + + upsample->methods[ci] = h2v2_fancy_upsample; + + upsample->pub.need_context_rows = TRUE; + + } else + + upsample->methods[ci] = h2v2_upsample; + + } else if ((h_out_group % h_in_group) == 0 && + + (v_out_group % v_in_group) == 0) { + + /* Generic integral-factors upsampling method */ + + upsample->methods[ci] = int_upsample; + + upsample->h_expand[ci] = (UINT8) (h_out_group / h_in_group); + + upsample->v_expand[ci] = (UINT8) (v_out_group / v_in_group); + + } else + + ERREXIT(cinfo, JERR_FRACT_SAMPLE_NOTIMPL); + + if (need_buffer) { + + upsample->color_buf[ci] = (*cinfo->mem->alloc_sarray) + + ((j_common_ptr) cinfo, JPOOL_IMAGE, + + (JDIMENSION) jround_up((long) cinfo->output_width, + + (long) cinfo->max_h_samp_factor), + + (JDIMENSION) cinfo->max_v_samp_factor); + + } + + } + +} + diff --git a/tools/urt/libs/jpeg6/jdtrans.cpp b/tools/urt/libs/jpeg6/jdtrans.cpp new file mode 100644 index 00000000..70e67379 --- /dev/null +++ b/tools/urt/libs/jpeg6/jdtrans.cpp @@ -0,0 +1,244 @@ +/* + + * jdtrans.c + + * + + * Copyright (C) 1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains library routines for transcoding decompression, + + * that is, reading raw DCT coefficient arrays from an input JPEG file. + + * The routines in jdapimin.c will also be needed by a transcoder. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "radiant_jpeglib.h" + + + + + +/* Forward declarations */ + +LOCAL void transdecode_master_selection JPP((j_decompress_ptr cinfo)); + + + + + +/* + + * Read the coefficient arrays from a JPEG file. + + * jpeg_read_header must be completed before calling this. + + * + + * The entire image is read into a set of virtual coefficient-block arrays, + + * one per component. The return value is a pointer to the array of + + * virtual-array descriptors. These can be manipulated directly via the + + * JPEG memory manager, or handed off to jpeg_write_coefficients(). + + * To release the memory occupied by the virtual arrays, call + + * jpeg_finish_decompress() when done with the data. + + * + + * Returns NULL if suspended. This case need be checked only if + + * a suspending data source is used. + + */ + + + +GLOBAL jvirt_barray_ptr * + +jpeg_read_coefficients (j_decompress_ptr cinfo) + +{ + + if (cinfo->global_state == DSTATE_READY) { + + /* First call: initialize active modules */ + + transdecode_master_selection(cinfo); + + cinfo->global_state = DSTATE_RDCOEFS; + + } else if (cinfo->global_state != DSTATE_RDCOEFS) + + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + /* Absorb whole file into the coef buffer */ + + for (;;) { + + int retcode; + + /* Call progress monitor hook if present */ + + if (cinfo->progress != NULL) + + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + + /* Absorb some more input */ + + retcode = (*cinfo->inputctl->consume_input) (cinfo); + + if (retcode == JPEG_SUSPENDED) + + return NULL; + + if (retcode == JPEG_REACHED_EOI) + + break; + + /* Advance progress counter if appropriate */ + + if (cinfo->progress != NULL && + + (retcode == JPEG_ROW_COMPLETED || retcode == JPEG_REACHED_SOS)) { + + if (++cinfo->progress->pass_counter >= cinfo->progress->pass_limit) { + + /* startup underestimated number of scans; ratchet up one scan */ + + cinfo->progress->pass_limit += (long) cinfo->total_iMCU_rows; + + } + + } + + } + + /* Set state so that jpeg_finish_decompress does the right thing */ + + cinfo->global_state = DSTATE_STOPPING; + + return cinfo->coef->coef_arrays; + +} + + + + + +/* + + * Master selection of decompression modules for transcoding. + + * This substitutes for jdmaster.c's initialization of the full decompressor. + + */ + + + +LOCAL void + +transdecode_master_selection (j_decompress_ptr cinfo) + +{ + + /* Entropy decoding: either Huffman or arithmetic coding. */ + + if (cinfo->arith_code) { + + ERREXIT(cinfo, JERR_ARITH_NOTIMPL); + + } else { + + if (cinfo->progressive_mode) { + +#ifdef D_PROGRESSIVE_SUPPORTED + + jinit_phuff_decoder(cinfo); + +#else + + ERREXIT(cinfo, JERR_NOT_COMPILED); + +#endif + + } else + + jinit_huff_decoder(cinfo); + + } + + + + /* Always get a full-image coefficient buffer. */ + + jinit_d_coef_controller(cinfo, TRUE); + + + + /* We can now tell the memory manager to allocate virtual arrays. */ + + (*cinfo->mem->realize_virt_arrays) ((j_common_ptr) cinfo); + + + + /* Initialize input side of decompressor to consume first scan. */ + + (*cinfo->inputctl->start_input_pass) (cinfo); + + + + /* Initialize progress monitoring. */ + + if (cinfo->progress != NULL) { + + int nscans; + + /* Estimate number of scans to set pass_limit. */ + + if (cinfo->progressive_mode) { + + /* Arbitrarily estimate 2 interleaved DC scans + 3 AC scans/component. */ + + nscans = 2 + 3 * cinfo->num_components; + + } else if (cinfo->inputctl->has_multiple_scans) { + + /* For a nonprogressive multiscan file, estimate 1 scan per component. */ + + nscans = cinfo->num_components; + + } else { + + nscans = 1; + + } + + cinfo->progress->pass_counter = 0L; + + cinfo->progress->pass_limit = (long) cinfo->total_iMCU_rows * nscans; + + cinfo->progress->completed_passes = 0; + + cinfo->progress->total_passes = 1; + + } + +} + diff --git a/tools/urt/libs/jpeg6/jerror.cpp b/tools/urt/libs/jpeg6/jerror.cpp new file mode 100644 index 00000000..e4f65f78 --- /dev/null +++ b/tools/urt/libs/jpeg6/jerror.cpp @@ -0,0 +1,233 @@ +/* + * jerror.c + * + * Copyright (C) 1991-1994, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains simple error-reporting and trace-message routines. + * These are suitable for Unix-like systems and others where writing to + * stderr is the right thing to do. Many applications will want to replace + * some or all of these routines. + * + * These routines are used by both the compression and decompression code. + */ + +/* this is not a core library module, so it doesn't define JPEG_INTERNALS */ +#include "jinclude.h" +#include "radiant_jpeglib.h" +#include "jversion.h" +#include "jerror.h" + +#ifndef EXIT_FAILURE /* define exit() codes if not provided */ +#define EXIT_FAILURE 1 +#endif + + +/* + * Create the message string table. + * We do this from the master message list in jerror.h by re-reading + * jerror.h with a suitable definition for macro JMESSAGE. + * The message table is made an external symbol just in case any applications + * want to refer to it directly. + */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_std_message_table jMsgTable +#endif + +#define JMESSAGE(code,string) string , + +const char * const jpeg_std_message_table[] = { +#include "jerror.h" + NULL +}; + +// Rad additions, longjmp out of the LoadJPGBuff +GLOBAL jmp_buf rad_loadfailed; +GLOBAL char rad_errormsg[JMSG_LENGTH_MAX]; + +/* + * Error exit handler: must not return to caller. + * + * Applications may override this if they want to get control back after + * an error. Typically one would longjmp somewhere instead of exiting. + * The setjmp buffer can be made a private field within an expanded error + * handler object. Note that the info needed to generate an error message + * is stored in the error object, so you can generate the message now or + * later, at your convenience. + * You should make sure that the JPEG object is cleaned up (with jpeg_abort + * or jpeg_destroy) at some point. + */ + +METHODDEF void +error_exit (j_common_ptr cinfo) +{ +// char buffer[JMSG_LENGTH_MAX]; + + /* Create the message */ + (*cinfo->err->format_message) (cinfo,rad_errormsg); + + /* Let the memory manager delete any temp files before we die */ + jpeg_destroy(cinfo); + + longjmp( rad_loadfailed, -1 ); +} + + +/* + * Actual output of an error or trace message. + * Applications may override this method to send JPEG messages somewhere + * other than stderr. + */ + +METHODDEF void +output_message (j_common_ptr cinfo) +{ + char buffer[JMSG_LENGTH_MAX]; + + /* Create the message */ + (*cinfo->err->format_message) (cinfo, buffer); + + /* Send it to stderr, adding a newline */ + printf("%s\n", buffer); +} + + +/* + * Decide whether to emit a trace or warning message. + * msg_level is one of: + * -1: recoverable corrupt-data warning, may want to abort. + * 0: important advisory messages (always display to user). + * 1: first level of tracing detail. + * 2,3,...: successively more detailed tracing messages. + * An application might override this method if it wanted to abort on warnings + * or change the policy about which messages to display. + */ + +METHODDEF void +emit_message (j_common_ptr cinfo, int msg_level) +{ + struct jpeg_error_mgr * err = cinfo->err; + + if (msg_level < 0) { + /* It's a warning message. Since corrupt files may generate many warnings, + * the policy implemented here is to show only the first warning, + * unless trace_level >= 3. + */ + if (err->num_warnings == 0 || err->trace_level >= 3) + (*err->output_message) (cinfo); + /* Always count warnings in num_warnings. */ + err->num_warnings++; + } else { + /* It's a trace message. Show it if trace_level >= msg_level. */ + if (err->trace_level >= msg_level) + (*err->output_message) (cinfo); + } +} + + +/* + * Format a message string for the most recent JPEG error or message. + * The message is stored into buffer, which should be at least JMSG_LENGTH_MAX + * characters. Note that no '\n' character is added to the string. + * Few applications should need to override this method. + */ + +METHODDEF void +format_message (j_common_ptr cinfo, char * buffer) +{ + struct jpeg_error_mgr * err = cinfo->err; + int msg_code = err->msg_code; + const char * msgtext = NULL; + const char * msgptr; + char ch; + boolean isstring; + + /* Look up message string in proper table */ + if (msg_code > 0 && msg_code <= err->last_jpeg_message) { + msgtext = err->jpeg_message_table[msg_code]; + } else if (err->addon_message_table != NULL && + msg_code >= err->first_addon_message && + msg_code <= err->last_addon_message) { + msgtext = err->addon_message_table[msg_code - err->first_addon_message]; + } + + /* Defend against bogus message number */ + if (msgtext == NULL) { + err->msg_parm.i[0] = msg_code; + msgtext = err->jpeg_message_table[0]; + } + + /* Check for string parameter, as indicated by %s in the message text */ + isstring = FALSE; + msgptr = msgtext; + while ((ch = *msgptr++) != '\0') { + if (ch == '%') { + if (*msgptr == 's') isstring = TRUE; + break; + } + } + + /* Format the message into the passed buffer */ + if (isstring) + sprintf(buffer, msgtext, err->msg_parm.s); + else + sprintf(buffer, msgtext, + err->msg_parm.i[0], err->msg_parm.i[1], + err->msg_parm.i[2], err->msg_parm.i[3], + err->msg_parm.i[4], err->msg_parm.i[5], + err->msg_parm.i[6], err->msg_parm.i[7]); +} + + +/* + * Reset error state variables at start of a new image. + * This is called during compression startup to reset trace/error + * processing to default state, without losing any application-specific + * method pointers. An application might possibly want to override + * this method if it has additional error processing state. + */ + +METHODDEF void +reset_error_mgr (j_common_ptr cinfo) +{ + cinfo->err->num_warnings = 0; + /* trace_level is not reset since it is an application-supplied parameter */ + cinfo->err->msg_code = 0; /* may be useful as a flag for "no error" */ +} + + +/* + * Fill in the standard error-handling methods in a jpeg_error_mgr object. + * Typical call is: + * struct jpeg_compress_struct cinfo; + * struct jpeg_error_mgr err; + * + * cinfo.err = jpeg_std_error(&err); + * after which the application may override some of the methods. + */ + +GLOBAL struct jpeg_error_mgr * +jpeg_std_error (struct jpeg_error_mgr * err) +{ + err->error_exit = error_exit; + err->emit_message = emit_message; + err->output_message = output_message; + err->format_message = format_message; + err->reset_error_mgr = reset_error_mgr; + + err->trace_level = 0; /* default = no tracing */ + err->num_warnings = 0; /* no warnings emitted yet */ + err->msg_code = 0; /* may be useful as a flag for "no error" */ + + /* Initialize message table pointers */ + err->jpeg_message_table = jpeg_std_message_table; + err->last_jpeg_message = (int) JMSG_LASTMSGCODE - 1; + + err->addon_message_table = NULL; + err->first_addon_message = 0; /* for safety */ + err->last_addon_message = 0; + + return err; +} diff --git a/tools/urt/libs/jpeg6/jerror.h b/tools/urt/libs/jpeg6/jerror.h new file mode 100644 index 00000000..d23dfb0c --- /dev/null +++ b/tools/urt/libs/jpeg6/jerror.h @@ -0,0 +1,278 @@ +/* + * jerror.h + * + * Copyright (C) 1994-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file defines the error and message codes for the JPEG library. + * Edit this file to add new codes, or to translate the message strings to + * some other language. + * A set of error-reporting macros are defined too. Some applications using + * the JPEG library may wish to include this file to get the error codes + * and/or the macros. + */ + +/* + * To define the enum list of message codes, include this file without + * defining macro JMESSAGE. To create a message string table, include it + * again with a suitable JMESSAGE definition (see jerror.c for an example). + */ +#ifndef JMESSAGE +#ifndef JERROR_H +/* First time through, define the enum list */ +#define JMAKE_ENUM_LIST +#else +/* Repeated inclusions of this file are no-ops unless JMESSAGE is defined */ +#define JMESSAGE(code,string) +#endif /* JERROR_H */ +#endif /* JMESSAGE */ + +#ifdef JMAKE_ENUM_LIST + +typedef enum { + +#define JMESSAGE(code,string) code , + +#endif /* JMAKE_ENUM_LIST */ + +JMESSAGE(JMSG_NOMESSAGE, "Bogus message code %d") /* Must be first entry! */ + +/* For maintenance convenience, list is alphabetical by message code name */ +JMESSAGE(JERR_ARITH_NOTIMPL, + "Sorry, there are legal restrictions on arithmetic coding") +JMESSAGE(JERR_BAD_ALIGN_TYPE, "ALIGN_TYPE is wrong, please fix") +JMESSAGE(JERR_BAD_ALLOC_CHUNK, "MAX_ALLOC_CHUNK is wrong, please fix") +JMESSAGE(JERR_BAD_BUFFER_MODE, "Bogus buffer control mode") +JMESSAGE(JERR_BAD_COMPONENT_ID, "Invalid component ID %d in SOS") +JMESSAGE(JERR_BAD_DCTSIZE, "IDCT output block size %d not supported") +JMESSAGE(JERR_BAD_IN_COLORSPACE, "Bogus input colorspace") +JMESSAGE(JERR_BAD_J_COLORSPACE, "Bogus JPEG colorspace") +JMESSAGE(JERR_BAD_LENGTH, "Bogus marker length") +JMESSAGE(JERR_BAD_MCU_SIZE, "Sampling factors too large for interleaved scan") +JMESSAGE(JERR_BAD_POOL_ID, "Invalid memory pool code %d") +JMESSAGE(JERR_BAD_PRECISION, "Unsupported JPEG data precision %d") +JMESSAGE(JERR_BAD_PROGRESSION, + "Invalid progressive parameters Ss=%d Se=%d Ah=%d Al=%d") +JMESSAGE(JERR_BAD_PROG_SCRIPT, + "Invalid progressive parameters at scan script entry %d") +JMESSAGE(JERR_BAD_SAMPLING, "Bogus sampling factors") +JMESSAGE(JERR_BAD_SCAN_SCRIPT, "Invalid scan script at entry %d") +JMESSAGE(JERR_BAD_STATE, "Improper call to JPEG library in state %d") +JMESSAGE(JERR_BAD_VIRTUAL_ACCESS, "Bogus virtual array access") +JMESSAGE(JERR_BUFFER_SIZE, "Buffer passed to JPEG library is too small") +JMESSAGE(JERR_CANT_SUSPEND, "Suspension not allowed here") +JMESSAGE(JERR_CCIR601_NOTIMPL, "CCIR601 sampling not implemented yet") +JMESSAGE(JERR_COMPONENT_COUNT, "Too many color components: %d, max %d") +JMESSAGE(JERR_CONVERSION_NOTIMPL, "Unsupported color conversion request") +JMESSAGE(JERR_DAC_INDEX, "Bogus DAC index %d") +JMESSAGE(JERR_DAC_VALUE, "Bogus DAC value 0x%x") +JMESSAGE(JERR_DHT_COUNTS, "Bogus DHT counts") +JMESSAGE(JERR_DHT_INDEX, "Bogus DHT index %d") +JMESSAGE(JERR_DQT_INDEX, "Bogus DQT index %d") +JMESSAGE(JERR_EMPTY_IMAGE, "Empty JPEG image (DNL not supported)") +JMESSAGE(JERR_EMS_READ, "Read from EMS failed") +JMESSAGE(JERR_EMS_WRITE, "Write to EMS failed") +JMESSAGE(JERR_EOI_EXPECTED, "Didn't expect more than one scan") +JMESSAGE(JERR_FILE_READ, "Input file read error") +JMESSAGE(JERR_FILE_WRITE, "Output file write error --- out of disk space?") +JMESSAGE(JERR_FRACT_SAMPLE_NOTIMPL, "Fractional sampling not implemented yet") +JMESSAGE(JERR_HUFF_CLEN_OVERFLOW, "Huffman code size table overflow") +JMESSAGE(JERR_HUFF_MISSING_CODE, "Missing Huffman code table entry") +JMESSAGE(JERR_IMAGE_TOO_BIG, "Maximum supported image dimension is %u pixels") +JMESSAGE(JERR_INPUT_EMPTY, "Empty input file") +JMESSAGE(JERR_INPUT_EOF, "Premature end of input file") +JMESSAGE(JERR_MISMATCHED_QUANT_TABLE, + "Cannot transcode due to multiple use of quantization table %d") +JMESSAGE(JERR_MISSING_DATA, "Scan script does not transmit all data") +JMESSAGE(JERR_MODE_CHANGE, "Invalid color quantization mode change") +JMESSAGE(JERR_NOTIMPL, "Not implemented yet") +JMESSAGE(JERR_NOT_COMPILED, "Requested feature was omitted at compile time") +JMESSAGE(JERR_NO_PROGRESSIVE, "Progressive JPEGs not supported, use regular JPEG instead") +JMESSAGE(JERR_NO_BACKING_STORE, "Backing store not supported") +JMESSAGE(JERR_NO_HUFF_TABLE, "Huffman table 0x%02x was not defined") +JMESSAGE(JERR_NO_IMAGE, "JPEG datastream contains no image") +JMESSAGE(JERR_NO_QUANT_TABLE, "Quantization table 0x%02x was not defined") +JMESSAGE(JERR_NO_SOI, "Not a JPEG file: starts with 0x%02x 0x%02x") +JMESSAGE(JERR_OUT_OF_MEMORY, "Insufficient memory (case %d)") +JMESSAGE(JERR_QUANT_COMPONENTS, + "Cannot quantize more than %d color components") +JMESSAGE(JERR_QUANT_FEW_COLORS, "Cannot quantize to fewer than %d colors") +JMESSAGE(JERR_QUANT_MANY_COLORS, "Cannot quantize to more than %d colors") +JMESSAGE(JERR_SOF_DUPLICATE, "Invalid JPEG file structure: two SOF markers") +JMESSAGE(JERR_SOF_NO_SOS, "Invalid JPEG file structure: missing SOS marker") +JMESSAGE(JERR_SOF_UNSUPPORTED, "Unsupported JPEG process: SOF type 0x%02x") +JMESSAGE(JERR_SOI_DUPLICATE, "Invalid JPEG file structure: two SOI markers") +JMESSAGE(JERR_SOS_NO_SOF, "Invalid JPEG file structure: SOS before SOF") +JMESSAGE(JERR_TFILE_CREATE, "Failed to create temporary file %s") +JMESSAGE(JERR_TFILE_READ, "Read failed on temporary file") +JMESSAGE(JERR_TFILE_SEEK, "Seek failed on temporary file") +JMESSAGE(JERR_TFILE_WRITE, + "Write failed on temporary file --- out of disk space?") +JMESSAGE(JERR_TOO_LITTLE_DATA, "Application transferred too few scanlines") +JMESSAGE(JERR_UNKNOWN_MARKER, "Unsupported marker type 0x%02x") +JMESSAGE(JERR_VIRTUAL_BUG, "Virtual array controller messed up") +JMESSAGE(JERR_WIDTH_OVERFLOW, "Image too wide for this implementation") +JMESSAGE(JERR_XMS_READ, "Read from XMS failed") +JMESSAGE(JERR_XMS_WRITE, "Write to XMS failed") +JMESSAGE(JMSG_COPYRIGHT, JCOPYRIGHT) +JMESSAGE(JMSG_VERSION, JVERSION) +JMESSAGE(JTRC_16BIT_TABLES, + "Caution: quantization tables are too coarse for baseline JPEG") +JMESSAGE(JTRC_ADOBE, + "Adobe APP14 marker: version %d, flags 0x%04x 0x%04x, transform %d") +JMESSAGE(JTRC_APP0, "Unknown APP0 marker (not JFIF), length %u") +JMESSAGE(JTRC_APP14, "Unknown APP14 marker (not Adobe), length %u") +JMESSAGE(JTRC_DAC, "Define Arithmetic Table 0x%02x: 0x%02x") +JMESSAGE(JTRC_DHT, "Define Huffman Table 0x%02x") +JMESSAGE(JTRC_DQT, "Define Quantization Table %d precision %d") +JMESSAGE(JTRC_DRI, "Define Restart Interval %u") +JMESSAGE(JTRC_EMS_CLOSE, "Freed EMS handle %u") +JMESSAGE(JTRC_EMS_OPEN, "Obtained EMS handle %u") +JMESSAGE(JTRC_EOI, "End Of Image") +JMESSAGE(JTRC_HUFFBITS, " %3d %3d %3d %3d %3d %3d %3d %3d") +JMESSAGE(JTRC_JFIF, "JFIF APP0 marker, density %dx%d %d") +JMESSAGE(JTRC_JFIF_BADTHUMBNAILSIZE, + "Warning: thumbnail image size does not match data length %u") +JMESSAGE(JTRC_JFIF_MINOR, "Unknown JFIF minor revision number %d.%02d") +JMESSAGE(JTRC_JFIF_THUMBNAIL, " with %d x %d thumbnail image") +JMESSAGE(JTRC_MISC_MARKER, "Skipping marker 0x%02x, length %u") +JMESSAGE(JTRC_PARMLESS_MARKER, "Unexpected marker 0x%02x") +JMESSAGE(JTRC_QUANTVALS, " %4u %4u %4u %4u %4u %4u %4u %4u") +JMESSAGE(JTRC_QUANT_3_NCOLORS, "Quantizing to %d = %d*%d*%d colors") +JMESSAGE(JTRC_QUANT_NCOLORS, "Quantizing to %d colors") +JMESSAGE(JTRC_QUANT_SELECTED, "Selected %d colors for quantization") +JMESSAGE(JTRC_RECOVERY_ACTION, "At marker 0x%02x, recovery action %d") +JMESSAGE(JTRC_RST, "RST%d") +JMESSAGE(JTRC_SMOOTH_NOTIMPL, + "Smoothing not supported with nonstandard sampling ratios") +JMESSAGE(JTRC_SOF, "Start Of Frame 0x%02x: width=%u, height=%u, components=%d") +JMESSAGE(JTRC_SOF_COMPONENT, " Component %d: %dhx%dv q=%d") +JMESSAGE(JTRC_SOI, "Start of Image") +JMESSAGE(JTRC_SOS, "Start Of Scan: %d components") +JMESSAGE(JTRC_SOS_COMPONENT, " Component %d: dc=%d ac=%d") +JMESSAGE(JTRC_SOS_PARAMS, " Ss=%d, Se=%d, Ah=%d, Al=%d") +JMESSAGE(JTRC_TFILE_CLOSE, "Closed temporary file %s") +JMESSAGE(JTRC_TFILE_OPEN, "Opened temporary file %s") +JMESSAGE(JTRC_UNKNOWN_IDS, + "Unrecognized component IDs %d %d %d, assuming YCbCr") +JMESSAGE(JTRC_XMS_CLOSE, "Freed XMS handle %u") +JMESSAGE(JTRC_XMS_OPEN, "Obtained XMS handle %u") +JMESSAGE(JWRN_ADOBE_XFORM, "Unknown Adobe color transform code %d") +JMESSAGE(JWRN_BOGUS_PROGRESSION, + "Inconsistent progression sequence for component %d coefficient %d") +JMESSAGE(JWRN_EXTRANEOUS_DATA, + "Corrupt JPEG data: %u extraneous bytes before marker 0x%02x") +JMESSAGE(JWRN_HIT_MARKER, "Corrupt JPEG data: premature end of data segment") +JMESSAGE(JWRN_HUFF_BAD_CODE, "Corrupt JPEG data: bad Huffman code") +JMESSAGE(JWRN_JFIF_MAJOR, "Warning: unknown JFIF revision number %d.%02d") +JMESSAGE(JWRN_JPEG_EOF, "Premature end of JPEG file") +JMESSAGE(JWRN_MUST_RESYNC, + "Corrupt JPEG data: found marker 0x%02x instead of RST%d") +JMESSAGE(JWRN_NOT_SEQUENTIAL, "Invalid SOS parameters for sequential JPEG") +JMESSAGE(JWRN_TOO_MUCH_DATA, "Application transferred too many scanlines") + +#ifdef JMAKE_ENUM_LIST + + JMSG_LASTMSGCODE +} J_MESSAGE_CODE; + +#undef JMAKE_ENUM_LIST +#endif /* JMAKE_ENUM_LIST */ + +/* Zap JMESSAGE macro so that future re-inclusions do nothing by default */ +#undef JMESSAGE + +#ifndef JERROR_H +#define JERROR_H + +// Rad additions, using longjmp to recover from errors +#include +EXTERN jmp_buf rad_loadfailed; +EXTERN char rad_errormsg[JMSG_LENGTH_MAX]; + +/* Macros to simplify using the error and trace message stuff */ +/* The first parameter is either type of cinfo pointer */ + +/* Fatal errors (print message and exit) */ +#define ERREXIT(cinfo,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT1(cinfo,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT2(cinfo,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT3(cinfo,code,p1,p2,p3) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (cinfo)->err->msg_parm.i[2] = (p3), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT4(cinfo,code,p1,p2,p3,p4) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (cinfo)->err->msg_parm.i[2] = (p3), \ + (cinfo)->err->msg_parm.i[3] = (p4), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXITS(cinfo,code,str) \ + ((cinfo)->err->msg_code = (code), \ + strncpy((cinfo)->err->msg_parm.s, (str), JMSG_STR_PARM_MAX), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) + +#define MAKESTMT(stuff) do { stuff } while (0) + +/* Nonfatal errors (we can keep going, but the data is probably corrupt) */ +#define WARNMS(cinfo,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) +#define WARNMS1(cinfo,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) +#define WARNMS2(cinfo,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) + +/* Informational/debugging messages */ +#define TRACEMS(cinfo,lvl,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS1(cinfo,lvl,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS2(cinfo,lvl,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS3(cinfo,lvl,code,p1,p2,p3) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMS4(cinfo,lvl,code,p1,p2,p3,p4) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMS8(cinfo,lvl,code,p1,p2,p3,p4,p5,p6,p7,p8) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + _mp[4] = (p5); _mp[5] = (p6); _mp[6] = (p7); _mp[7] = (p8); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMSS(cinfo,lvl,code,str) \ + ((cinfo)->err->msg_code = (code), \ + strncpy((cinfo)->err->msg_parm.s, (str), JMSG_STR_PARM_MAX), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) + +#endif /* JERROR_H */ diff --git a/tools/urt/libs/jpeg6/jfdctflt.cpp b/tools/urt/libs/jpeg6/jfdctflt.cpp new file mode 100644 index 00000000..f41bff82 --- /dev/null +++ b/tools/urt/libs/jpeg6/jfdctflt.cpp @@ -0,0 +1,336 @@ +/* + + * jfdctflt.c + + * + + * Copyright (C) 1994, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains a floating-point implementation of the + + * forward DCT (Discrete Cosine Transform). + + * + + * This implementation should be more accurate than either of the integer + + * DCT implementations. However, it may not give the same results on all + + * machines because of differences in roundoff behavior. Speed will depend + + * on the hardware's floating point capacity. + + * + + * A 2-D DCT can be done by 1-D DCT on each row followed by 1-D DCT + + * on each column. Direct algorithms are also available, but they are + + * much more complex and seem not to be any faster when reduced to code. + + * + + * This implementation is based on Arai, Agui, and Nakajima's algorithm for + + * scaled DCT. Their original paper (Trans. IEICE E-71(11):1095) is in + + * Japanese, but the algorithm is described in the Pennebaker & Mitchell + + * JPEG textbook (see REFERENCES section in file README). The following code + + * is based directly on figure 4-8 in P&M. + + * While an 8-point DCT cannot be done in less than 11 multiplies, it is + + * possible to arrange the computation so that many of the multiplies are + + * simple scalings of the final outputs. These multiplies can then be + + * folded into the multiplications or divisions by the JPEG quantization + + * table entries. The AA&N method leaves only 5 multiplies and 29 adds + + * to be done in the DCT itself. + + * The primary disadvantage of this method is that with a fixed-point + + * implementation, accuracy is lost due to imprecise representation of the + + * scaled quantization values. However, that problem does not arise if + + * we use floating point arithmetic. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "radiant_jpeglib.h" + +#include "jdct.h" /* Private declarations for DCT subsystem */ + + + +#ifdef DCT_FLOAT_SUPPORTED + + + + + +/* + + * This module is specialized to the case DCTSIZE = 8. + + */ + + + +#if DCTSIZE != 8 + + Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */ + +#endif + + + + + +/* + + * Perform the forward DCT on one block of samples. + + */ + + + +GLOBAL void + +jpeg_fdct_float (FAST_FLOAT * data) + +{ + + FAST_FLOAT tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + + FAST_FLOAT tmp10, tmp11, tmp12, tmp13; + + FAST_FLOAT z1, z2, z3, z4, z5, z11, z13; + + FAST_FLOAT *dataptr; + + int ctr; + + + + /* Pass 1: process rows. */ + + + + dataptr = data; + + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + + tmp0 = dataptr[0] + dataptr[7]; + + tmp7 = dataptr[0] - dataptr[7]; + + tmp1 = dataptr[1] + dataptr[6]; + + tmp6 = dataptr[1] - dataptr[6]; + + tmp2 = dataptr[2] + dataptr[5]; + + tmp5 = dataptr[2] - dataptr[5]; + + tmp3 = dataptr[3] + dataptr[4]; + + tmp4 = dataptr[3] - dataptr[4]; + + + + /* Even part */ + + + + tmp10 = tmp0 + tmp3; /* phase 2 */ + + tmp13 = tmp0 - tmp3; + + tmp11 = tmp1 + tmp2; + + tmp12 = tmp1 - tmp2; + + + + dataptr[0] = tmp10 + tmp11; /* phase 3 */ + + dataptr[4] = tmp10 - tmp11; + + + + z1 = (tmp12 + tmp13) * ((FAST_FLOAT) 0.707106781); /* c4 */ + + dataptr[2] = tmp13 + z1; /* phase 5 */ + + dataptr[6] = tmp13 - z1; + + + + /* Odd part */ + + + + tmp10 = tmp4 + tmp5; /* phase 2 */ + + tmp11 = tmp5 + tmp6; + + tmp12 = tmp6 + tmp7; + + + + /* The rotator is modified from fig 4-8 to avoid extra negations. */ + + z5 = (tmp10 - tmp12) * ((FAST_FLOAT) 0.382683433); /* c6 */ + + z2 = ((FAST_FLOAT) 0.541196100) * tmp10 + z5; /* c2-c6 */ + + z4 = ((FAST_FLOAT) 1.306562965) * tmp12 + z5; /* c2+c6 */ + + z3 = tmp11 * ((FAST_FLOAT) 0.707106781); /* c4 */ + + + + z11 = tmp7 + z3; /* phase 5 */ + + z13 = tmp7 - z3; + + + + dataptr[5] = z13 + z2; /* phase 6 */ + + dataptr[3] = z13 - z2; + + dataptr[1] = z11 + z4; + + dataptr[7] = z11 - z4; + + + + dataptr += DCTSIZE; /* advance pointer to next row */ + + } + + + + /* Pass 2: process columns. */ + + + + dataptr = data; + + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + + tmp0 = dataptr[DCTSIZE*0] + dataptr[DCTSIZE*7]; + + tmp7 = dataptr[DCTSIZE*0] - dataptr[DCTSIZE*7]; + + tmp1 = dataptr[DCTSIZE*1] + dataptr[DCTSIZE*6]; + + tmp6 = dataptr[DCTSIZE*1] - dataptr[DCTSIZE*6]; + + tmp2 = dataptr[DCTSIZE*2] + dataptr[DCTSIZE*5]; + + tmp5 = dataptr[DCTSIZE*2] - dataptr[DCTSIZE*5]; + + tmp3 = dataptr[DCTSIZE*3] + dataptr[DCTSIZE*4]; + + tmp4 = dataptr[DCTSIZE*3] - dataptr[DCTSIZE*4]; + + + + /* Even part */ + + + + tmp10 = tmp0 + tmp3; /* phase 2 */ + + tmp13 = tmp0 - tmp3; + + tmp11 = tmp1 + tmp2; + + tmp12 = tmp1 - tmp2; + + + + dataptr[DCTSIZE*0] = tmp10 + tmp11; /* phase 3 */ + + dataptr[DCTSIZE*4] = tmp10 - tmp11; + + + + z1 = (tmp12 + tmp13) * ((FAST_FLOAT) 0.707106781); /* c4 */ + + dataptr[DCTSIZE*2] = tmp13 + z1; /* phase 5 */ + + dataptr[DCTSIZE*6] = tmp13 - z1; + + + + /* Odd part */ + + + + tmp10 = tmp4 + tmp5; /* phase 2 */ + + tmp11 = tmp5 + tmp6; + + tmp12 = tmp6 + tmp7; + + + + /* The rotator is modified from fig 4-8 to avoid extra negations. */ + + z5 = (tmp10 - tmp12) * ((FAST_FLOAT) 0.382683433); /* c6 */ + + z2 = ((FAST_FLOAT) 0.541196100) * tmp10 + z5; /* c2-c6 */ + + z4 = ((FAST_FLOAT) 1.306562965) * tmp12 + z5; /* c2+c6 */ + + z3 = tmp11 * ((FAST_FLOAT) 0.707106781); /* c4 */ + + + + z11 = tmp7 + z3; /* phase 5 */ + + z13 = tmp7 - z3; + + + + dataptr[DCTSIZE*5] = z13 + z2; /* phase 6 */ + + dataptr[DCTSIZE*3] = z13 - z2; + + dataptr[DCTSIZE*1] = z11 + z4; + + dataptr[DCTSIZE*7] = z11 - z4; + + + + dataptr++; /* advance pointer to next column */ + + } + +} + + + +#endif /* DCT_FLOAT_SUPPORTED */ + diff --git a/tools/urt/libs/jpeg6/jidctflt.cpp b/tools/urt/libs/jpeg6/jidctflt.cpp new file mode 100644 index 00000000..9e966daf --- /dev/null +++ b/tools/urt/libs/jpeg6/jidctflt.cpp @@ -0,0 +1,482 @@ +/* + + * jidctflt.c + + * + + * Copyright (C) 1994, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains a floating-point implementation of the + + * inverse DCT (Discrete Cosine Transform). In the IJG code, this routine + + * must also perform dequantization of the input coefficients. + + * + + * This implementation should be more accurate than either of the integer + + * IDCT implementations. However, it may not give the same results on all + + * machines because of differences in roundoff behavior. Speed will depend + + * on the hardware's floating point capacity. + + * + + * A 2-D IDCT can be done by 1-D IDCT on each column followed by 1-D IDCT + + * on each row (or vice versa, but it's more convenient to emit a row at + + * a time). Direct algorithms are also available, but they are much more + + * complex and seem not to be any faster when reduced to code. + + * + + * This implementation is based on Arai, Agui, and Nakajima's algorithm for + + * scaled DCT. Their original paper (Trans. IEICE E-71(11):1095) is in + + * Japanese, but the algorithm is described in the Pennebaker & Mitchell + + * JPEG textbook (see REFERENCES section in file README). The following code + + * is based directly on figure 4-8 in P&M. + + * While an 8-point DCT cannot be done in less than 11 multiplies, it is + + * possible to arrange the computation so that many of the multiplies are + + * simple scalings of the final outputs. These multiplies can then be + + * folded into the multiplications or divisions by the JPEG quantization + + * table entries. The AA&N method leaves only 5 multiplies and 29 adds + + * to be done in the DCT itself. + + * The primary disadvantage of this method is that with a fixed-point + + * implementation, accuracy is lost due to imprecise representation of the + + * scaled quantization values. However, that problem does not arise if + + * we use floating point arithmetic. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "radiant_jpeglib.h" + +#include "jdct.h" /* Private declarations for DCT subsystem */ + + + +#ifdef DCT_FLOAT_SUPPORTED + + + + + +/* + + * This module is specialized to the case DCTSIZE = 8. + + */ + + + +#if DCTSIZE != 8 + + Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */ + +#endif + + + + + +/* Dequantize a coefficient by multiplying it by the multiplier-table + + * entry; produce a float result. + + */ + + + +#define DEQUANTIZE(coef,quantval) (((FAST_FLOAT) (coef)) * (quantval)) + + + + + +/* + + * Perform dequantization and inverse DCT on one block of coefficients. + + */ + + + +GLOBAL void + +jpeg_idct_float (j_decompress_ptr cinfo, jpeg_component_info * compptr, + + JCOEFPTR coef_block, + + JSAMPARRAY output_buf, JDIMENSION output_col) + +{ + + FAST_FLOAT tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + + FAST_FLOAT tmp10, tmp11, tmp12, tmp13; + + FAST_FLOAT z5, z10, z11, z12, z13; + + JCOEFPTR inptr; + + FLOAT_MULT_TYPE * quantptr; + + FAST_FLOAT * wsptr; + + JSAMPROW outptr; + + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + + int ctr; + + FAST_FLOAT workspace[DCTSIZE2]; /* buffers data between passes */ + + SHIFT_TEMPS + + + + /* Pass 1: process columns from input, store into work array. */ + + + + inptr = coef_block; + + quantptr = (FLOAT_MULT_TYPE *) compptr->dct_table; + + wsptr = workspace; + + for (ctr = DCTSIZE; ctr > 0; ctr--) { + + /* Due to quantization, we will usually find that many of the input + + * coefficients are zero, especially the AC terms. We can exploit this + + * by short-circuiting the IDCT calculation for any column in which all + + * the AC terms are zero. In that case each output is equal to the + + * DC coefficient (with scale factor as needed). + + * With typical images and quantization tables, half or more of the + + * column DCT calculations can be simplified this way. + + */ + + + + if ((inptr[DCTSIZE*1] | inptr[DCTSIZE*2] | inptr[DCTSIZE*3] | + + inptr[DCTSIZE*4] | inptr[DCTSIZE*5] | inptr[DCTSIZE*6] | + + inptr[DCTSIZE*7]) == 0) { + + /* AC terms all zero */ + + FAST_FLOAT dcval = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + + + + wsptr[DCTSIZE*0] = dcval; + + wsptr[DCTSIZE*1] = dcval; + + wsptr[DCTSIZE*2] = dcval; + + wsptr[DCTSIZE*3] = dcval; + + wsptr[DCTSIZE*4] = dcval; + + wsptr[DCTSIZE*5] = dcval; + + wsptr[DCTSIZE*6] = dcval; + + wsptr[DCTSIZE*7] = dcval; + + + + inptr++; /* advance pointers to next column */ + + quantptr++; + + wsptr++; + + continue; + + } + + + + /* Even part */ + + + + tmp0 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + + tmp1 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + + tmp2 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); + + tmp3 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); + + + + tmp10 = tmp0 + tmp2; /* phase 3 */ + + tmp11 = tmp0 - tmp2; + + + + tmp13 = tmp1 + tmp3; /* phases 5-3 */ + + tmp12 = (tmp1 - tmp3) * ((FAST_FLOAT) 1.414213562) - tmp13; /* 2*c4 */ + + + + tmp0 = tmp10 + tmp13; /* phase 2 */ + + tmp3 = tmp10 - tmp13; + + tmp1 = tmp11 + tmp12; + + tmp2 = tmp11 - tmp12; + + + + /* Odd part */ + + + + tmp4 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + + tmp5 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + + tmp6 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + + tmp7 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); + + + + z13 = tmp6 + tmp5; /* phase 6 */ + + z10 = tmp6 - tmp5; + + z11 = tmp4 + tmp7; + + z12 = tmp4 - tmp7; + + + + tmp7 = z11 + z13; /* phase 5 */ + + tmp11 = (z11 - z13) * ((FAST_FLOAT) 1.414213562); /* 2*c4 */ + + + + z5 = (z10 + z12) * ((FAST_FLOAT) 1.847759065); /* 2*c2 */ + + tmp10 = ((FAST_FLOAT) 1.082392200) * z12 - z5; /* 2*(c2-c6) */ + + tmp12 = ((FAST_FLOAT) -2.613125930) * z10 + z5; /* -2*(c2+c6) */ + + + + tmp6 = tmp12 - tmp7; /* phase 2 */ + + tmp5 = tmp11 - tmp6; + + tmp4 = tmp10 + tmp5; + + + + wsptr[DCTSIZE*0] = tmp0 + tmp7; + + wsptr[DCTSIZE*7] = tmp0 - tmp7; + + wsptr[DCTSIZE*1] = tmp1 + tmp6; + + wsptr[DCTSIZE*6] = tmp1 - tmp6; + + wsptr[DCTSIZE*2] = tmp2 + tmp5; + + wsptr[DCTSIZE*5] = tmp2 - tmp5; + + wsptr[DCTSIZE*4] = tmp3 + tmp4; + + wsptr[DCTSIZE*3] = tmp3 - tmp4; + + + + inptr++; /* advance pointers to next column */ + + quantptr++; + + wsptr++; + + } + + + + /* Pass 2: process rows from work array, store into output array. */ + + /* Note that we must descale the results by a factor of 8 == 2**3. */ + + + + wsptr = workspace; + + for (ctr = 0; ctr < DCTSIZE; ctr++) { + + outptr = output_buf[ctr] + output_col; + + /* Rows of zeroes can be exploited in the same way as we did with columns. + + * However, the column calculation has created many nonzero AC terms, so + + * the simplification applies less often (typically 5% to 10% of the time). + + * And testing floats for zero is relatively expensive, so we don't bother. + + */ + + + + /* Even part */ + + + + tmp10 = wsptr[0] + wsptr[4]; + + tmp11 = wsptr[0] - wsptr[4]; + + + + tmp13 = wsptr[2] + wsptr[6]; + + tmp12 = (wsptr[2] - wsptr[6]) * ((FAST_FLOAT) 1.414213562) - tmp13; + + + + tmp0 = tmp10 + tmp13; + + tmp3 = tmp10 - tmp13; + + tmp1 = tmp11 + tmp12; + + tmp2 = tmp11 - tmp12; + + + + /* Odd part */ + + + + z13 = wsptr[5] + wsptr[3]; + + z10 = wsptr[5] - wsptr[3]; + + z11 = wsptr[1] + wsptr[7]; + + z12 = wsptr[1] - wsptr[7]; + + + + tmp7 = z11 + z13; + + tmp11 = (z11 - z13) * ((FAST_FLOAT) 1.414213562); + + + + z5 = (z10 + z12) * ((FAST_FLOAT) 1.847759065); /* 2*c2 */ + + tmp10 = ((FAST_FLOAT) 1.082392200) * z12 - z5; /* 2*(c2-c6) */ + + tmp12 = ((FAST_FLOAT) -2.613125930) * z10 + z5; /* -2*(c2+c6) */ + + + + tmp6 = tmp12 - tmp7; + + tmp5 = tmp11 - tmp6; + + tmp4 = tmp10 + tmp5; + + + + /* Final output stage: scale down by a factor of 8 and range-limit */ + + + + outptr[0] = range_limit[(int) DESCALE((INT32) (tmp0 + tmp7), 3) + + & RANGE_MASK]; + + outptr[7] = range_limit[(int) DESCALE((INT32) (tmp0 - tmp7), 3) + + & RANGE_MASK]; + + outptr[1] = range_limit[(int) DESCALE((INT32) (tmp1 + tmp6), 3) + + & RANGE_MASK]; + + outptr[6] = range_limit[(int) DESCALE((INT32) (tmp1 - tmp6), 3) + + & RANGE_MASK]; + + outptr[2] = range_limit[(int) DESCALE((INT32) (tmp2 + tmp5), 3) + + & RANGE_MASK]; + + outptr[5] = range_limit[(int) DESCALE((INT32) (tmp2 - tmp5), 3) + + & RANGE_MASK]; + + outptr[4] = range_limit[(int) DESCALE((INT32) (tmp3 + tmp4), 3) + + & RANGE_MASK]; + + outptr[3] = range_limit[(int) DESCALE((INT32) (tmp3 - tmp4), 3) + + & RANGE_MASK]; + + + + wsptr += DCTSIZE; /* advance pointer to next row */ + + } + +} + + + +#endif /* DCT_FLOAT_SUPPORTED */ + diff --git a/tools/urt/libs/jpeg6/jinclude.h b/tools/urt/libs/jpeg6/jinclude.h new file mode 100644 index 00000000..5ff60fed --- /dev/null +++ b/tools/urt/libs/jpeg6/jinclude.h @@ -0,0 +1,91 @@ +/* + * jinclude.h + * + * Copyright (C) 1991-1994, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file exists to provide a single place to fix any problems with + * including the wrong system include files. (Common problems are taken + * care of by the standard jconfig symbols, but on really weird systems + * you may have to edit this file.) + * + * NOTE: this file is NOT intended to be included by applications using the + * JPEG library. Most applications need only include jpeglib.h. + */ + + +/* Include auto-config file to find out which system include files we need. */ + +#include "jconfig.h" /* auto configuration options */ +#define JCONFIG_INCLUDED /* so that jpeglib.h doesn't do it again */ + +/* + * We need the NULL macro and size_t typedef. + * On an ANSI-conforming system it is sufficient to include . + * Otherwise, we get them from or ; we may have to + * pull in as well. + * Note that the core JPEG library does not require ; + * only the default error handler and data source/destination modules do. + * But we must pull it in because of the references to FILE in jpeglib.h. + * You can remove those references if you want to compile without . + */ + +#ifdef HAVE_STDDEF_H +#include +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif + +#ifdef NEED_SYS_TYPES_H +#include +#endif + +#include + +/* + * We need memory copying and zeroing functions, plus strncpy(). + * ANSI and System V implementations declare these in . + * BSD doesn't have the mem() functions, but it does have bcopy()/bzero(). + * Some systems may declare memset and memcpy in . + * + * NOTE: we assume the size parameters to these functions are of type size_t. + * Change the casts in these macros if not! + */ + +#ifdef NEED_BSD_STRINGS + +#include +#define MEMZERO(target,size) bzero((void *)(target), (size_t)(size)) +#define MEMCOPY(dest,src,size) bcopy((const void *)(src), (void *)(dest), (size_t)(size)) + +#else /* not BSD, assume ANSI/SysV string lib */ + +#include +#define MEMZERO(target,size) memset((void *)(target), 0, (size_t)(size)) +#define MEMCOPY(dest,src,size) memcpy((void *)(dest), (const void *)(src), (size_t)(size)) + +#endif + +/* + * In ANSI C, and indeed any rational implementation, size_t is also the + * type returned by sizeof(). However, it seems there are some irrational + * implementations out there, in which sizeof() returns an int even though + * size_t is defined as long or unsigned long. To ensure consistent results + * we always use this SIZEOF() macro in place of using sizeof() directly. + */ + +#define SIZEOF(object) ((size_t) sizeof(object)) + +/* + * The modules that use fread() and fwrite() always invoke them through + * these macros. On some systems you may need to twiddle the argument casts. + * CAUTION: argument order is different from underlying functions! + */ + +#define JFREAD(file,buf,sizeofbuf) \ + ((size_t) fread((void *) (buf), (size_t) 1, (size_t) (sizeofbuf), (file))) +#define JFWRITE(file,buf,sizeofbuf) \ + ((size_t) fwrite((const void *) (buf), (size_t) 1, (size_t) (sizeofbuf), (file))) diff --git a/tools/urt/libs/jpeg6/jmemmgr.cpp b/tools/urt/libs/jpeg6/jmemmgr.cpp new file mode 100644 index 00000000..9204b324 --- /dev/null +++ b/tools/urt/libs/jpeg6/jmemmgr.cpp @@ -0,0 +1,2230 @@ +/* + + * jmemmgr.c + + * + + * Copyright (C) 1991-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains the JPEG system-independent memory management + + * routines. This code is usable across a wide variety of machines; most + + * of the system dependencies have been isolated in a separate file. + + * The major functions provided here are: + + * * pool-based allocation and freeing of memory; + + * * policy decisions about how to divide available memory among the + + * virtual arrays; + + * * control logic for swapping virtual arrays between main memory and + + * backing storage. + + * The separate system-dependent file provides the actual backing-storage + + * access code, and it contains the policy decision about how much total + + * main memory to use. + + * This file is system-dependent in the sense that some of its functions + + * are unnecessary in some systems. For example, if there is enough virtual + + * memory so that backing storage will never be used, much of the virtual + + * array control logic could be removed. (Of course, if you have that much + + * memory then you shouldn't care about a little bit of unused code...) + + */ + + + +#define JPEG_INTERNALS + +#define AM_MEMORY_MANAGER /* we define jvirt_Xarray_control structs */ + +#include "jinclude.h" + +#include "radiant_jpeglib.h" + +#include "jmemsys.h" /* import the system-dependent declarations */ + + + +#ifndef NO_GETENV + +#ifndef HAVE_STDLIB_H /* should declare getenv() */ + +extern char * getenv JPP((const char * name)); + +#endif + +#endif + + + + + +/* + + * Some important notes: + + * The allocation routines provided here must never return NULL. + + * They should exit to error_exit if unsuccessful. + + * + + * It's not a good idea to try to merge the sarray and barray routines, + + * even though they are textually almost the same, because samples are + + * usually stored as bytes while coefficients are shorts or ints. Thus, + + * in machines where byte pointers have a different representation from + + * word pointers, the resulting machine code could not be the same. + + */ + + + + + +/* + + * Many machines require storage alignment: longs must start on 4-byte + + * boundaries, doubles on 8-byte boundaries, etc. On such machines, malloc() + + * always returns pointers that are multiples of the worst-case alignment + + * requirement, and we had better do so too. + + * There isn't any really portable way to determine the worst-case alignment + + * requirement. This module assumes that the alignment requirement is + + * multiples of sizeof(ALIGN_TYPE). + + * By default, we define ALIGN_TYPE as double. This is necessary on some + + * workstations (where doubles really do need 8-byte alignment) and will work + + * fine on nearly everything. If your machine has lesser alignment needs, + + * you can save a few bytes by making ALIGN_TYPE smaller. + + * The only place I know of where this will NOT work is certain Macintosh + + * 680x0 compilers that define double as a 10-byte IEEE extended float. + + * Doing 10-byte alignment is counterproductive because longwords won't be + + * aligned well. Put "#define ALIGN_TYPE long" in jconfig.h if you have + + * such a compiler. + + */ + + + +#ifndef ALIGN_TYPE /* so can override from jconfig.h */ + +#define ALIGN_TYPE double + +#endif + + + + + +/* + + * We allocate objects from "pools", where each pool is gotten with a single + + * request to jpeg_get_small() or jpeg_get_large(). There is no per-object + + * overhead within a pool, except for alignment padding. Each pool has a + + * header with a link to the next pool of the same class. + + * Small and large pool headers are identical except that the latter's + + * link pointer must be FAR on 80x86 machines. + + * Notice that the "real" header fields are union'ed with a dummy ALIGN_TYPE + + * field. This forces the compiler to make SIZEOF(small_pool_hdr) a multiple + + * of the alignment requirement of ALIGN_TYPE. + + */ + + + +typedef union small_pool_struct * small_pool_ptr; + + + +typedef union small_pool_struct { + + struct { + + small_pool_ptr next; /* next in list of pools */ + + size_t bytes_used; /* how many bytes already used within pool */ + + size_t bytes_left; /* bytes still available in this pool */ + + } hdr; + + ALIGN_TYPE dummy; /* included in union to ensure alignment */ + +} small_pool_hdr; + + + +typedef union large_pool_struct FAR * large_pool_ptr; + + + +typedef union large_pool_struct { + + struct { + + large_pool_ptr next; /* next in list of pools */ + + size_t bytes_used; /* how many bytes already used within pool */ + + size_t bytes_left; /* bytes still available in this pool */ + + } hdr; + + ALIGN_TYPE dummy; /* included in union to ensure alignment */ + +} large_pool_hdr; + + + + + +/* + + * Here is the full definition of a memory manager object. + + */ + + + +typedef struct { + + struct jpeg_memory_mgr pub; /* public fields */ + + + + /* Each pool identifier (lifetime class) names a linked list of pools. */ + + small_pool_ptr small_list[JPOOL_NUMPOOLS]; + + large_pool_ptr large_list[JPOOL_NUMPOOLS]; + + + + /* Since we only have one lifetime class of virtual arrays, only one + + * linked list is necessary (for each datatype). Note that the virtual + + * array control blocks being linked together are actually stored somewhere + + * in the small-pool list. + + */ + + jvirt_sarray_ptr virt_sarray_list; + + jvirt_barray_ptr virt_barray_list; + + + + /* This counts total space obtained from jpeg_get_small/large */ + + long total_space_allocated; + + + + /* alloc_sarray and alloc_barray set this value for use by virtual + + * array routines. + + */ + + JDIMENSION last_rowsperchunk; /* from most recent alloc_sarray/barray */ + +} my_memory_mgr; + + + +typedef my_memory_mgr * my_mem_ptr; + + + + + +/* + + * The control blocks for virtual arrays. + + * Note that these blocks are allocated in the "small" pool area. + + * System-dependent info for the associated backing store (if any) is hidden + + * inside the backing_store_info struct. + + */ + + + +struct jvirt_sarray_control { + + JSAMPARRAY mem_buffer; /* => the in-memory buffer */ + + JDIMENSION rows_in_array; /* total virtual array height */ + + JDIMENSION samplesperrow; /* width of array (and of memory buffer) */ + + JDIMENSION maxaccess; /* max rows accessed by access_virt_sarray */ + + JDIMENSION rows_in_mem; /* height of memory buffer */ + + JDIMENSION rowsperchunk; /* allocation chunk size in mem_buffer */ + + JDIMENSION cur_start_row; /* first logical row # in the buffer */ + + JDIMENSION first_undef_row; /* row # of first uninitialized row */ + + boolean pre_zero; /* pre-zero mode requested? */ + + boolean dirty; /* do current buffer contents need written? */ + + boolean b_s_open; /* is backing-store data valid? */ + + jvirt_sarray_ptr next; /* link to next virtual sarray control block */ + + backing_store_info b_s_info; /* System-dependent control info */ + +}; + + + +struct jvirt_barray_control { + + JBLOCKARRAY mem_buffer; /* => the in-memory buffer */ + + JDIMENSION rows_in_array; /* total virtual array height */ + + JDIMENSION blocksperrow; /* width of array (and of memory buffer) */ + + JDIMENSION maxaccess; /* max rows accessed by access_virt_barray */ + + JDIMENSION rows_in_mem; /* height of memory buffer */ + + JDIMENSION rowsperchunk; /* allocation chunk size in mem_buffer */ + + JDIMENSION cur_start_row; /* first logical row # in the buffer */ + + JDIMENSION first_undef_row; /* row # of first uninitialized row */ + + boolean pre_zero; /* pre-zero mode requested? */ + + boolean dirty; /* do current buffer contents need written? */ + + boolean b_s_open; /* is backing-store data valid? */ + + jvirt_barray_ptr next; /* link to next virtual barray control block */ + + backing_store_info b_s_info; /* System-dependent control info */ + +}; + + + + + +#ifdef MEM_STATS /* optional extra stuff for statistics */ + + + +LOCAL void + +print_mem_stats (j_common_ptr cinfo, int pool_id) + +{ + + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + + small_pool_ptr shdr_ptr; + + large_pool_ptr lhdr_ptr; + + + + /* Since this is only a debugging stub, we can cheat a little by using + + * fprintf directly rather than going through the trace message code. + + * This is helpful because message parm array can't handle longs. + + */ + + fprintf(stderr, "Freeing pool %d, total space = %ld\n", + + pool_id, mem->total_space_allocated); + + + + for (lhdr_ptr = mem->large_list[pool_id]; lhdr_ptr != NULL; + + lhdr_ptr = lhdr_ptr->hdr.next) { + + fprintf(stderr, " Large chunk used %ld\n", + + (long) lhdr_ptr->hdr.bytes_used); + + } + + + + for (shdr_ptr = mem->small_list[pool_id]; shdr_ptr != NULL; + + shdr_ptr = shdr_ptr->hdr.next) { + + fprintf(stderr, " Small chunk used %ld free %ld\n", + + (long) shdr_ptr->hdr.bytes_used, + + (long) shdr_ptr->hdr.bytes_left); + + } + +} + + + +#endif /* MEM_STATS */ + + + + + +LOCAL void + +out_of_memory (j_common_ptr cinfo, int which) + +/* Report an out-of-memory error and stop execution */ + +/* If we compiled MEM_STATS support, report alloc requests before dying */ + +{ + +#ifdef MEM_STATS + + cinfo->err->trace_level = 2; /* force self_destruct to report stats */ + +#endif + + ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, which); + +} + + + + + +/* + + * Allocation of "small" objects. + + * + + * For these, we use pooled storage. When a new pool must be created, + + * we try to get enough space for the current request plus a "slop" factor, + + * where the slop will be the amount of leftover space in the new pool. + + * The speed vs. space tradeoff is largely determined by the slop values. + + * A different slop value is provided for each pool class (lifetime), + + * and we also distinguish the first pool of a class from later ones. + + * NOTE: the values given work fairly well on both 16- and 32-bit-int + + * machines, but may be too small if longs are 64 bits or more. + + */ + + + +static const size_t first_pool_slop[JPOOL_NUMPOOLS] = + +{ + + 1600, /* first PERMANENT pool */ + + 16000 /* first IMAGE pool */ + +}; + + + +static const size_t extra_pool_slop[JPOOL_NUMPOOLS] = + +{ + + 0, /* additional PERMANENT pools */ + + 5000 /* additional IMAGE pools */ + +}; + + + +#define MIN_SLOP 50 /* greater than 0 to avoid futile looping */ + + + + + +METHODDEF void * + +alloc_small (j_common_ptr cinfo, int pool_id, size_t sizeofobject) + +/* Allocate a "small" object */ + +{ + + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + + small_pool_ptr hdr_ptr, prev_hdr_ptr; + + char * data_ptr; + + size_t odd_bytes, min_request, slop; + + + + /* Check for unsatisfiable request (do now to ensure no overflow below) */ + + if (sizeofobject > (size_t) (MAX_ALLOC_CHUNK-SIZEOF(small_pool_hdr))) + + out_of_memory(cinfo, 1); /* request exceeds malloc's ability */ + + + + /* Round up the requested size to a multiple of SIZEOF(ALIGN_TYPE) */ + + odd_bytes = sizeofobject % SIZEOF(ALIGN_TYPE); + + if (odd_bytes > 0) + + sizeofobject += SIZEOF(ALIGN_TYPE) - odd_bytes; + + + + /* See if space is available in any existing pool */ + + if (pool_id < 0 || pool_id >= JPOOL_NUMPOOLS) + + ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); /* safety check */ + + prev_hdr_ptr = NULL; + + hdr_ptr = mem->small_list[pool_id]; + + while (hdr_ptr != NULL) { + + if (hdr_ptr->hdr.bytes_left >= sizeofobject) + + break; /* found pool with enough space */ + + prev_hdr_ptr = hdr_ptr; + + hdr_ptr = hdr_ptr->hdr.next; + + } + + + + /* Time to make a new pool? */ + + if (hdr_ptr == NULL) { + + /* min_request is what we need now, slop is what will be leftover */ + + min_request = sizeofobject + SIZEOF(small_pool_hdr); + + if (prev_hdr_ptr == NULL) /* first pool in class? */ + + slop = first_pool_slop[pool_id]; + + else + + slop = extra_pool_slop[pool_id]; + + /* Don't ask for more than MAX_ALLOC_CHUNK */ + + if (slop > (size_t) (MAX_ALLOC_CHUNK-min_request)) + + slop = (size_t) (MAX_ALLOC_CHUNK-min_request); + + /* Try to get space, if fail reduce slop and try again */ + + for (;;) { + + hdr_ptr = (small_pool_ptr) jpeg_get_small(cinfo, min_request + slop); + + if (hdr_ptr != NULL) + + break; + + slop /= 2; + + if (slop < MIN_SLOP) /* give up when it gets real small */ + + out_of_memory(cinfo, 2); /* jpeg_get_small failed */ + + } + + mem->total_space_allocated += min_request + slop; + + /* Success, initialize the new pool header and add to end of list */ + + hdr_ptr->hdr.next = NULL; + + hdr_ptr->hdr.bytes_used = 0; + + hdr_ptr->hdr.bytes_left = sizeofobject + slop; + + if (prev_hdr_ptr == NULL) /* first pool in class? */ + + mem->small_list[pool_id] = hdr_ptr; + + else + + prev_hdr_ptr->hdr.next = hdr_ptr; + + } + + + + /* OK, allocate the object from the current pool */ + + data_ptr = (char *) (hdr_ptr + 1); /* point to first data byte in pool */ + + data_ptr += hdr_ptr->hdr.bytes_used; /* point to place for object */ + + hdr_ptr->hdr.bytes_used += sizeofobject; + + hdr_ptr->hdr.bytes_left -= sizeofobject; + + + + return (void *) data_ptr; + +} + + + + + +/* + + * Allocation of "large" objects. + + * + + * The external semantics of these are the same as "small" objects, + + * except that FAR pointers are used on 80x86. However the pool + + * management heuristics are quite different. We assume that each + + * request is large enough that it may as well be passed directly to + + * jpeg_get_large; the pool management just links everything together + + * so that we can free it all on demand. + + * Note: the major use of "large" objects is in JSAMPARRAY and JBLOCKARRAY + + * structures. The routines that create these structures (see below) + + * deliberately bunch rows together to ensure a large request size. + + */ + + + +METHODDEF void FAR * + +alloc_large (j_common_ptr cinfo, int pool_id, size_t sizeofobject) + +/* Allocate a "large" object */ + +{ + + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + + large_pool_ptr hdr_ptr; + + size_t odd_bytes; + + + + /* Check for unsatisfiable request (do now to ensure no overflow below) */ + + if (sizeofobject > (size_t) (MAX_ALLOC_CHUNK-SIZEOF(large_pool_hdr))) + + out_of_memory(cinfo, 3); /* request exceeds malloc's ability */ + + + + /* Round up the requested size to a multiple of SIZEOF(ALIGN_TYPE) */ + + odd_bytes = sizeofobject % SIZEOF(ALIGN_TYPE); + + if (odd_bytes > 0) + + sizeofobject += SIZEOF(ALIGN_TYPE) - odd_bytes; + + + + /* Always make a new pool */ + + if (pool_id < 0 || pool_id >= JPOOL_NUMPOOLS) + + ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); /* safety check */ + + + + hdr_ptr = (large_pool_ptr) jpeg_get_large(cinfo, sizeofobject + + + SIZEOF(large_pool_hdr)); + + if (hdr_ptr == NULL) + + out_of_memory(cinfo, 4); /* jpeg_get_large failed */ + + mem->total_space_allocated += sizeofobject + SIZEOF(large_pool_hdr); + + + + /* Success, initialize the new pool header and add to list */ + + hdr_ptr->hdr.next = mem->large_list[pool_id]; + + /* We maintain space counts in each pool header for statistical purposes, + + * even though they are not needed for allocation. + + */ + + hdr_ptr->hdr.bytes_used = sizeofobject; + + hdr_ptr->hdr.bytes_left = 0; + + mem->large_list[pool_id] = hdr_ptr; + + + + return (void FAR *) (hdr_ptr + 1); /* point to first data byte in pool */ + +} + + + + + +/* + + * Creation of 2-D sample arrays. + + * The pointers are in near heap, the samples themselves in FAR heap. + + * + + * To minimize allocation overhead and to allow I/O of large contiguous + + * blocks, we allocate the sample rows in groups of as many rows as possible + + * without exceeding MAX_ALLOC_CHUNK total bytes per allocation request. + + * NB: the virtual array control routines, later in this file, know about + + * this chunking of rows. The rowsperchunk value is left in the mem manager + + * object so that it can be saved away if this sarray is the workspace for + + * a virtual array. + + */ + + + +METHODDEF JSAMPARRAY + +alloc_sarray (j_common_ptr cinfo, int pool_id, + + JDIMENSION samplesperrow, JDIMENSION numrows) + +/* Allocate a 2-D sample array */ + +{ + + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + + JSAMPARRAY result; + + JSAMPROW workspace; + + JDIMENSION rowsperchunk, currow, i; + + long ltemp; + + + + /* Calculate max # of rows allowed in one allocation chunk */ + + ltemp = (MAX_ALLOC_CHUNK-SIZEOF(large_pool_hdr)) / + + ((long) samplesperrow * SIZEOF(JSAMPLE)); + + if (ltemp <= 0) + + ERREXIT(cinfo, JERR_WIDTH_OVERFLOW); + + if (ltemp < (long) numrows) + + rowsperchunk = (JDIMENSION) ltemp; + + else + + rowsperchunk = numrows; + + mem->last_rowsperchunk = rowsperchunk; + + + + /* Get space for row pointers (small object) */ + + result = (JSAMPARRAY) alloc_small(cinfo, pool_id, + + (size_t) (numrows * SIZEOF(JSAMPROW))); + + + + /* Get the rows themselves (large objects) */ + + currow = 0; + + while (currow < numrows) { + + rowsperchunk = MIN(rowsperchunk, numrows - currow); + + workspace = (JSAMPROW) alloc_large(cinfo, pool_id, + + (size_t) ((size_t) rowsperchunk * (size_t) samplesperrow + + * SIZEOF(JSAMPLE))); + + for (i = rowsperchunk; i > 0; i--) { + + result[currow++] = workspace; + + workspace += samplesperrow; + + } + + } + + + + return result; + +} + + + + + +/* + + * Creation of 2-D coefficient-block arrays. + + * This is essentially the same as the code for sample arrays, above. + + */ + + + +METHODDEF JBLOCKARRAY + +alloc_barray (j_common_ptr cinfo, int pool_id, + + JDIMENSION blocksperrow, JDIMENSION numrows) + +/* Allocate a 2-D coefficient-block array */ + +{ + + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + + JBLOCKARRAY result; + + JBLOCKROW workspace; + + JDIMENSION rowsperchunk, currow, i; + + long ltemp; + + + + /* Calculate max # of rows allowed in one allocation chunk */ + + ltemp = (MAX_ALLOC_CHUNK-SIZEOF(large_pool_hdr)) / + + ((long) blocksperrow * SIZEOF(JBLOCK)); + + if (ltemp <= 0) + + ERREXIT(cinfo, JERR_WIDTH_OVERFLOW); + + if (ltemp < (long) numrows) + + rowsperchunk = (JDIMENSION) ltemp; + + else + + rowsperchunk = numrows; + + mem->last_rowsperchunk = rowsperchunk; + + + + /* Get space for row pointers (small object) */ + + result = (JBLOCKARRAY) alloc_small(cinfo, pool_id, + + (size_t) (numrows * SIZEOF(JBLOCKROW))); + + + + /* Get the rows themselves (large objects) */ + + currow = 0; + + while (currow < numrows) { + + rowsperchunk = MIN(rowsperchunk, numrows - currow); + + workspace = (JBLOCKROW) alloc_large(cinfo, pool_id, + + (size_t) ((size_t) rowsperchunk * (size_t) blocksperrow + + * SIZEOF(JBLOCK))); + + for (i = rowsperchunk; i > 0; i--) { + + result[currow++] = workspace; + + workspace += blocksperrow; + + } + + } + + + + return result; + +} + + + + + +/* + + * About virtual array management: + + * + + * The above "normal" array routines are only used to allocate strip buffers + + * (as wide as the image, but just a few rows high). Full-image-sized buffers + + * are handled as "virtual" arrays. The array is still accessed a strip at a + + * time, but the memory manager must save the whole array for repeated + + * accesses. The intended implementation is that there is a strip buffer in + + * memory (as high as is possible given the desired memory limit), plus a + + * backing file that holds the rest of the array. + + * + + * The request_virt_array routines are told the total size of the image and + + * the maximum number of rows that will be accessed at once. The in-memory + + * buffer must be at least as large as the maxaccess value. + + * + + * The request routines create control blocks but not the in-memory buffers. + + * That is postponed until realize_virt_arrays is called. At that time the + + * total amount of space needed is known (approximately, anyway), so free + + * memory can be divided up fairly. + + * + + * The access_virt_array routines are responsible for making a specific strip + + * area accessible (after reading or writing the backing file, if necessary). + + * Note that the access routines are told whether the caller intends to modify + + * the accessed strip; during a read-only pass this saves having to rewrite + + * data to disk. The access routines are also responsible for pre-zeroing + + * any newly accessed rows, if pre-zeroing was requested. + + * + + * In current usage, the access requests are usually for nonoverlapping + + * strips; that is, successive access start_row numbers differ by exactly + + * num_rows = maxaccess. This means we can get good performance with simple + + * buffer dump/reload logic, by making the in-memory buffer be a multiple + + * of the access height; then there will never be accesses across bufferload + + * boundaries. The code will still work with overlapping access requests, + + * but it doesn't handle bufferload overlaps very efficiently. + + */ + + + + + +METHODDEF jvirt_sarray_ptr + +request_virt_sarray (j_common_ptr cinfo, int pool_id, boolean pre_zero, + + JDIMENSION samplesperrow, JDIMENSION numrows, + + JDIMENSION maxaccess) + +/* Request a virtual 2-D sample array */ + +{ + + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + + jvirt_sarray_ptr result; + + + + /* Only IMAGE-lifetime virtual arrays are currently supported */ + + if (pool_id != JPOOL_IMAGE) + + ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); /* safety check */ + + + + /* get control block */ + + result = (jvirt_sarray_ptr) alloc_small(cinfo, pool_id, + + SIZEOF(struct jvirt_sarray_control)); + + + + result->mem_buffer = NULL; /* marks array not yet realized */ + + result->rows_in_array = numrows; + + result->samplesperrow = samplesperrow; + + result->maxaccess = maxaccess; + + result->pre_zero = pre_zero; + + result->b_s_open = FALSE; /* no associated backing-store object */ + + result->next = mem->virt_sarray_list; /* add to list of virtual arrays */ + + mem->virt_sarray_list = result; + + + + return result; + +} + + + + + +METHODDEF jvirt_barray_ptr + +request_virt_barray (j_common_ptr cinfo, int pool_id, boolean pre_zero, + + JDIMENSION blocksperrow, JDIMENSION numrows, + + JDIMENSION maxaccess) + +/* Request a virtual 2-D coefficient-block array */ + +{ + + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + + jvirt_barray_ptr result; + + + + /* Only IMAGE-lifetime virtual arrays are currently supported */ + + if (pool_id != JPOOL_IMAGE) + + ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); /* safety check */ + + + + /* get control block */ + + result = (jvirt_barray_ptr) alloc_small(cinfo, pool_id, + + SIZEOF(struct jvirt_barray_control)); + + + + result->mem_buffer = NULL; /* marks array not yet realized */ + + result->rows_in_array = numrows; + + result->blocksperrow = blocksperrow; + + result->maxaccess = maxaccess; + + result->pre_zero = pre_zero; + + result->b_s_open = FALSE; /* no associated backing-store object */ + + result->next = mem->virt_barray_list; /* add to list of virtual arrays */ + + mem->virt_barray_list = result; + + + + return result; + +} + + + + + +METHODDEF void + +realize_virt_arrays (j_common_ptr cinfo) + +/* Allocate the in-memory buffers for any unrealized virtual arrays */ + +{ + + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + + long space_per_minheight, maximum_space, avail_mem; + + long minheights, max_minheights; + + jvirt_sarray_ptr sptr; + + jvirt_barray_ptr bptr; + + + + /* Compute the minimum space needed (maxaccess rows in each buffer) + + * and the maximum space needed (full image height in each buffer). + + * These may be of use to the system-dependent jpeg_mem_available routine. + + */ + + space_per_minheight = 0; + + maximum_space = 0; + + for (sptr = mem->virt_sarray_list; sptr != NULL; sptr = sptr->next) { + + if (sptr->mem_buffer == NULL) { /* if not realized yet */ + + space_per_minheight += (long) sptr->maxaccess * + + (long) sptr->samplesperrow * SIZEOF(JSAMPLE); + + maximum_space += (long) sptr->rows_in_array * + + (long) sptr->samplesperrow * SIZEOF(JSAMPLE); + + } + + } + + for (bptr = mem->virt_barray_list; bptr != NULL; bptr = bptr->next) { + + if (bptr->mem_buffer == NULL) { /* if not realized yet */ + + space_per_minheight += (long) bptr->maxaccess * + + (long) bptr->blocksperrow * SIZEOF(JBLOCK); + + maximum_space += (long) bptr->rows_in_array * + + (long) bptr->blocksperrow * SIZEOF(JBLOCK); + + } + + } + + + + if (space_per_minheight <= 0) + + return; /* no unrealized arrays, no work */ + + + + /* Determine amount of memory to actually use; this is system-dependent. */ + + avail_mem = jpeg_mem_available(cinfo, space_per_minheight, maximum_space, + + mem->total_space_allocated); + + + + /* If the maximum space needed is available, make all the buffers full + + * height; otherwise parcel it out with the same number of minheights + + * in each buffer. + + */ + + if (avail_mem >= maximum_space) + + max_minheights = 1000000000L; + + else { + + max_minheights = avail_mem / space_per_minheight; + + /* If there doesn't seem to be enough space, try to get the minimum + + * anyway. This allows a "stub" implementation of jpeg_mem_available(). + + */ + + if (max_minheights <= 0) + + max_minheights = 1; + + } + + + + /* Allocate the in-memory buffers and initialize backing store as needed. */ + + + + for (sptr = mem->virt_sarray_list; sptr != NULL; sptr = sptr->next) { + + if (sptr->mem_buffer == NULL) { /* if not realized yet */ + + minheights = ((long) sptr->rows_in_array - 1L) / sptr->maxaccess + 1L; + + if (minheights <= max_minheights) { + + /* This buffer fits in memory */ + + sptr->rows_in_mem = sptr->rows_in_array; + + } else { + + /* It doesn't fit in memory, create backing store. */ + + sptr->rows_in_mem = (JDIMENSION) (max_minheights * sptr->maxaccess); + + jpeg_open_backing_store(cinfo, & sptr->b_s_info, + + (long) sptr->rows_in_array * + + (long) sptr->samplesperrow * + + (long) SIZEOF(JSAMPLE)); + + sptr->b_s_open = TRUE; + + } + + sptr->mem_buffer = alloc_sarray(cinfo, JPOOL_IMAGE, + + sptr->samplesperrow, sptr->rows_in_mem); + + sptr->rowsperchunk = mem->last_rowsperchunk; + + sptr->cur_start_row = 0; + + sptr->first_undef_row = 0; + + sptr->dirty = FALSE; + + } + + } + + + + for (bptr = mem->virt_barray_list; bptr != NULL; bptr = bptr->next) { + + if (bptr->mem_buffer == NULL) { /* if not realized yet */ + + minheights = ((long) bptr->rows_in_array - 1L) / bptr->maxaccess + 1L; + + if (minheights <= max_minheights) { + + /* This buffer fits in memory */ + + bptr->rows_in_mem = bptr->rows_in_array; + + } else { + + /* It doesn't fit in memory, create backing store. */ + + bptr->rows_in_mem = (JDIMENSION) (max_minheights * bptr->maxaccess); + + jpeg_open_backing_store(cinfo, & bptr->b_s_info, + + (long) bptr->rows_in_array * + + (long) bptr->blocksperrow * + + (long) SIZEOF(JBLOCK)); + + bptr->b_s_open = TRUE; + + } + + bptr->mem_buffer = alloc_barray(cinfo, JPOOL_IMAGE, + + bptr->blocksperrow, bptr->rows_in_mem); + + bptr->rowsperchunk = mem->last_rowsperchunk; + + bptr->cur_start_row = 0; + + bptr->first_undef_row = 0; + + bptr->dirty = FALSE; + + } + + } + +} + + + + + +LOCAL void + +do_sarray_io (j_common_ptr cinfo, jvirt_sarray_ptr ptr, boolean writing) + +/* Do backing store read or write of a virtual sample array */ + +{ + + long bytesperrow, file_offset, byte_count, rows, thisrow, i; + + + + bytesperrow = (long) ptr->samplesperrow * SIZEOF(JSAMPLE); + + file_offset = ptr->cur_start_row * bytesperrow; + + /* Loop to read or write each allocation chunk in mem_buffer */ + + for (i = 0; i < (long) ptr->rows_in_mem; i += ptr->rowsperchunk) { + + /* One chunk, but check for short chunk at end of buffer */ + + rows = MIN((long) ptr->rowsperchunk, (long) ptr->rows_in_mem - i); + + /* Transfer no more than is currently defined */ + + thisrow = (long) ptr->cur_start_row + i; + + rows = MIN(rows, (long) ptr->first_undef_row - thisrow); + + /* Transfer no more than fits in file */ + + rows = MIN(rows, (long) ptr->rows_in_array - thisrow); + + if (rows <= 0) /* this chunk might be past end of file! */ + + break; + + byte_count = rows * bytesperrow; + + if (writing) + + (*ptr->b_s_info.write_backing_store) (cinfo, & ptr->b_s_info, + + (void FAR *) ptr->mem_buffer[i], + + file_offset, byte_count); + + else + + (*ptr->b_s_info.read_backing_store) (cinfo, & ptr->b_s_info, + + (void FAR *) ptr->mem_buffer[i], + + file_offset, byte_count); + + file_offset += byte_count; + + } + +} + + + + + +LOCAL void + +do_barray_io (j_common_ptr cinfo, jvirt_barray_ptr ptr, boolean writing) + +/* Do backing store read or write of a virtual coefficient-block array */ + +{ + + long bytesperrow, file_offset, byte_count, rows, thisrow, i; + + + + bytesperrow = (long) ptr->blocksperrow * SIZEOF(JBLOCK); + + file_offset = ptr->cur_start_row * bytesperrow; + + /* Loop to read or write each allocation chunk in mem_buffer */ + + for (i = 0; i < (long) ptr->rows_in_mem; i += ptr->rowsperchunk) { + + /* One chunk, but check for short chunk at end of buffer */ + + rows = MIN((long) ptr->rowsperchunk, (long) ptr->rows_in_mem - i); + + /* Transfer no more than is currently defined */ + + thisrow = (long) ptr->cur_start_row + i; + + rows = MIN(rows, (long) ptr->first_undef_row - thisrow); + + /* Transfer no more than fits in file */ + + rows = MIN(rows, (long) ptr->rows_in_array - thisrow); + + if (rows <= 0) /* this chunk might be past end of file! */ + + break; + + byte_count = rows * bytesperrow; + + if (writing) + + (*ptr->b_s_info.write_backing_store) (cinfo, & ptr->b_s_info, + + (void FAR *) ptr->mem_buffer[i], + + file_offset, byte_count); + + else + + (*ptr->b_s_info.read_backing_store) (cinfo, & ptr->b_s_info, + + (void FAR *) ptr->mem_buffer[i], + + file_offset, byte_count); + + file_offset += byte_count; + + } + +} + + + + + +METHODDEF JSAMPARRAY + +access_virt_sarray (j_common_ptr cinfo, jvirt_sarray_ptr ptr, + + JDIMENSION start_row, JDIMENSION num_rows, + + boolean writable) + +/* Access the part of a virtual sample array starting at start_row */ + +/* and extending for num_rows rows. writable is true if */ + +/* caller intends to modify the accessed area. */ + +{ + + JDIMENSION end_row = start_row + num_rows; + + JDIMENSION undef_row; + + + + /* debugging check */ + + if (end_row > ptr->rows_in_array || num_rows > ptr->maxaccess || + + ptr->mem_buffer == NULL) + + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + + + + /* Make the desired part of the virtual array accessible */ + + if (start_row < ptr->cur_start_row || + + end_row > ptr->cur_start_row+ptr->rows_in_mem) { + + if (! ptr->b_s_open) + + ERREXIT(cinfo, JERR_VIRTUAL_BUG); + + /* Flush old buffer contents if necessary */ + + if (ptr->dirty) { + + do_sarray_io(cinfo, ptr, TRUE); + + ptr->dirty = FALSE; + + } + + /* Decide what part of virtual array to access. + + * Algorithm: if target address > current window, assume forward scan, + + * load starting at target address. If target address < current window, + + * assume backward scan, load so that target area is top of window. + + * Note that when switching from forward write to forward read, will have + + * start_row = 0, so the limiting case applies and we load from 0 anyway. + + */ + + if (start_row > ptr->cur_start_row) { + + ptr->cur_start_row = start_row; + + } else { + + /* use long arithmetic here to avoid overflow & unsigned problems */ + + long ltemp; + + + + ltemp = (long) end_row - (long) ptr->rows_in_mem; + + if (ltemp < 0) + + ltemp = 0; /* don't fall off front end of file */ + + ptr->cur_start_row = (JDIMENSION) ltemp; + + } + + /* Read in the selected part of the array. + + * During the initial write pass, we will do no actual read + + * because the selected part is all undefined. + + */ + + do_sarray_io(cinfo, ptr, FALSE); + + } + + /* Ensure the accessed part of the array is defined; prezero if needed. + + * To improve locality of access, we only prezero the part of the array + + * that the caller is about to access, not the entire in-memory array. + + */ + + if (ptr->first_undef_row < end_row) { + + if (ptr->first_undef_row < start_row) { + + if (writable) /* writer skipped over a section of array */ + + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + + undef_row = start_row; /* but reader is allowed to read ahead */ + + } else { + + undef_row = ptr->first_undef_row; + + } + + if (writable) + + ptr->first_undef_row = end_row; + + if (ptr->pre_zero) { + + size_t bytesperrow = (size_t) ptr->samplesperrow * SIZEOF(JSAMPLE); + + undef_row -= ptr->cur_start_row; /* make indexes relative to buffer */ + + end_row -= ptr->cur_start_row; + + while (undef_row < end_row) { + + jzero_far((void FAR *) ptr->mem_buffer[undef_row], bytesperrow); + + undef_row++; + + } + + } else { + + if (! writable) /* reader looking at undefined data */ + + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + + } + + } + + /* Flag the buffer dirty if caller will write in it */ + + if (writable) + + ptr->dirty = TRUE; + + /* Return address of proper part of the buffer */ + + return ptr->mem_buffer + (start_row - ptr->cur_start_row); + +} + + + + + +METHODDEF JBLOCKARRAY + +access_virt_barray (j_common_ptr cinfo, jvirt_barray_ptr ptr, + + JDIMENSION start_row, JDIMENSION num_rows, + + boolean writable) + +/* Access the part of a virtual block array starting at start_row */ + +/* and extending for num_rows rows. writable is true if */ + +/* caller intends to modify the accessed area. */ + +{ + + JDIMENSION end_row = start_row + num_rows; + + JDIMENSION undef_row; + + + + /* debugging check */ + + if (end_row > ptr->rows_in_array || num_rows > ptr->maxaccess || + + ptr->mem_buffer == NULL) + + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + + + + /* Make the desired part of the virtual array accessible */ + + if (start_row < ptr->cur_start_row || + + end_row > ptr->cur_start_row+ptr->rows_in_mem) { + + if (! ptr->b_s_open) + + ERREXIT(cinfo, JERR_VIRTUAL_BUG); + + /* Flush old buffer contents if necessary */ + + if (ptr->dirty) { + + do_barray_io(cinfo, ptr, TRUE); + + ptr->dirty = FALSE; + + } + + /* Decide what part of virtual array to access. + + * Algorithm: if target address > current window, assume forward scan, + + * load starting at target address. If target address < current window, + + * assume backward scan, load so that target area is top of window. + + * Note that when switching from forward write to forward read, will have + + * start_row = 0, so the limiting case applies and we load from 0 anyway. + + */ + + if (start_row > ptr->cur_start_row) { + + ptr->cur_start_row = start_row; + + } else { + + /* use long arithmetic here to avoid overflow & unsigned problems */ + + long ltemp; + + + + ltemp = (long) end_row - (long) ptr->rows_in_mem; + + if (ltemp < 0) + + ltemp = 0; /* don't fall off front end of file */ + + ptr->cur_start_row = (JDIMENSION) ltemp; + + } + + /* Read in the selected part of the array. + + * During the initial write pass, we will do no actual read + + * because the selected part is all undefined. + + */ + + do_barray_io(cinfo, ptr, FALSE); + + } + + /* Ensure the accessed part of the array is defined; prezero if needed. + + * To improve locality of access, we only prezero the part of the array + + * that the caller is about to access, not the entire in-memory array. + + */ + + if (ptr->first_undef_row < end_row) { + + if (ptr->first_undef_row < start_row) { + + if (writable) /* writer skipped over a section of array */ + + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + + undef_row = start_row; /* but reader is allowed to read ahead */ + + } else { + + undef_row = ptr->first_undef_row; + + } + + if (writable) + + ptr->first_undef_row = end_row; + + if (ptr->pre_zero) { + + size_t bytesperrow = (size_t) ptr->blocksperrow * SIZEOF(JBLOCK); + + undef_row -= ptr->cur_start_row; /* make indexes relative to buffer */ + + end_row -= ptr->cur_start_row; + + while (undef_row < end_row) { + + jzero_far((void FAR *) ptr->mem_buffer[undef_row], bytesperrow); + + undef_row++; + + } + + } else { + + if (! writable) /* reader looking at undefined data */ + + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + + } + + } + + /* Flag the buffer dirty if caller will write in it */ + + if (writable) + + ptr->dirty = TRUE; + + /* Return address of proper part of the buffer */ + + return ptr->mem_buffer + (start_row - ptr->cur_start_row); + +} + + + + + +/* + + * Release all objects belonging to a specified pool. + + */ + + + +METHODDEF void + +free_pool (j_common_ptr cinfo, int pool_id) + +{ + + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + + small_pool_ptr shdr_ptr; + + large_pool_ptr lhdr_ptr; + + size_t space_freed; + + + + if (pool_id < 0 || pool_id >= JPOOL_NUMPOOLS) + + ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); /* safety check */ + + + +#ifdef MEM_STATS + + if (cinfo->err->trace_level > 1) + + print_mem_stats(cinfo, pool_id); /* print pool's memory usage statistics */ + +#endif + + + + /* If freeing IMAGE pool, close any virtual arrays first */ + + if (pool_id == JPOOL_IMAGE) { + + jvirt_sarray_ptr sptr; + + jvirt_barray_ptr bptr; + + + + for (sptr = mem->virt_sarray_list; sptr != NULL; sptr = sptr->next) { + + if (sptr->b_s_open) { /* there may be no backing store */ + + sptr->b_s_open = FALSE; /* prevent recursive close if error */ + + (*sptr->b_s_info.close_backing_store) (cinfo, & sptr->b_s_info); + + } + + } + + mem->virt_sarray_list = NULL; + + for (bptr = mem->virt_barray_list; bptr != NULL; bptr = bptr->next) { + + if (bptr->b_s_open) { /* there may be no backing store */ + + bptr->b_s_open = FALSE; /* prevent recursive close if error */ + + (*bptr->b_s_info.close_backing_store) (cinfo, & bptr->b_s_info); + + } + + } + + mem->virt_barray_list = NULL; + + } + + + + /* Release large objects */ + + lhdr_ptr = mem->large_list[pool_id]; + + mem->large_list[pool_id] = NULL; + + + + while (lhdr_ptr != NULL) { + + large_pool_ptr next_lhdr_ptr = lhdr_ptr->hdr.next; + + space_freed = lhdr_ptr->hdr.bytes_used + + + lhdr_ptr->hdr.bytes_left + + + SIZEOF(large_pool_hdr); + + jpeg_free_large(cinfo, (void FAR *) lhdr_ptr, space_freed); + + mem->total_space_allocated -= space_freed; + + lhdr_ptr = next_lhdr_ptr; + + } + + + + /* Release small objects */ + + shdr_ptr = mem->small_list[pool_id]; + + mem->small_list[pool_id] = NULL; + + + + while (shdr_ptr != NULL) { + + small_pool_ptr next_shdr_ptr = shdr_ptr->hdr.next; + + space_freed = shdr_ptr->hdr.bytes_used + + + shdr_ptr->hdr.bytes_left + + + SIZEOF(small_pool_hdr); + + jpeg_free_small(cinfo, (void *) shdr_ptr, space_freed); + + mem->total_space_allocated -= space_freed; + + shdr_ptr = next_shdr_ptr; + + } + +} + + + + + +/* + + * Close up shop entirely. + + * Note that this cannot be called unless cinfo->mem is non-NULL. + + */ + + + +METHODDEF void + +self_destruct (j_common_ptr cinfo) + +{ + + int pool; + + + + /* Close all backing store, release all memory. + + * Releasing pools in reverse order might help avoid fragmentation + + * with some (brain-damaged) malloc libraries. + + */ + + for (pool = JPOOL_NUMPOOLS-1; pool >= JPOOL_PERMANENT; pool--) { + + free_pool(cinfo, pool); + + } + + + + /* Release the memory manager control block too. */ + + jpeg_free_small(cinfo, (void *) cinfo->mem, SIZEOF(my_memory_mgr)); + + cinfo->mem = NULL; /* ensures I will be called only once */ + + + + jpeg_mem_term(cinfo); /* system-dependent cleanup */ + +} + + + + + +/* + + * Memory manager initialization. + + * When this is called, only the error manager pointer is valid in cinfo! + + */ + + + +GLOBAL void + +jinit_memory_mgr (j_common_ptr cinfo) + +{ + + my_mem_ptr mem; + + long max_to_use; + + int pool; + + size_t test_mac; + + + + cinfo->mem = NULL; /* for safety if init fails */ + + + + /* Check for configuration errors. + + * SIZEOF(ALIGN_TYPE) should be a power of 2; otherwise, it probably + + * doesn't reflect any real hardware alignment requirement. + + * The test is a little tricky: for X>0, X and X-1 have no one-bits + + * in common if and only if X is a power of 2, ie has only one one-bit. + + * Some compilers may give an "unreachable code" warning here; ignore it. + + */ + + if ((SIZEOF(ALIGN_TYPE) & (SIZEOF(ALIGN_TYPE)-1)) != 0) + + ERREXIT(cinfo, JERR_BAD_ALIGN_TYPE); + + /* MAX_ALLOC_CHUNK must be representable as type size_t, and must be + + * a multiple of SIZEOF(ALIGN_TYPE). + + * Again, an "unreachable code" warning may be ignored here. + + * But a "constant too large" warning means you need to fix MAX_ALLOC_CHUNK. + + */ + + test_mac = (size_t) MAX_ALLOC_CHUNK; + + if ((long) test_mac != MAX_ALLOC_CHUNK || + + (MAX_ALLOC_CHUNK % SIZEOF(ALIGN_TYPE)) != 0) + + ERREXIT(cinfo, JERR_BAD_ALLOC_CHUNK); + + + + max_to_use = jpeg_mem_init(cinfo); /* system-dependent initialization */ + + + + /* Attempt to allocate memory manager's control block */ + + mem = (my_mem_ptr) jpeg_get_small(cinfo, SIZEOF(my_memory_mgr)); + + + + if (mem == NULL) { + + jpeg_mem_term(cinfo); /* system-dependent cleanup */ + + ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, 0); + + } + + + + /* OK, fill in the method pointers */ + + mem->pub.alloc_small = alloc_small; + + mem->pub.alloc_large = alloc_large; + + mem->pub.alloc_sarray = alloc_sarray; + + mem->pub.alloc_barray = alloc_barray; + + mem->pub.request_virt_sarray = request_virt_sarray; + + mem->pub.request_virt_barray = request_virt_barray; + + mem->pub.realize_virt_arrays = realize_virt_arrays; + + mem->pub.access_virt_sarray = access_virt_sarray; + + mem->pub.access_virt_barray = access_virt_barray; + + mem->pub.free_pool = free_pool; + + mem->pub.self_destruct = self_destruct; + + + + /* Initialize working state */ + + mem->pub.max_memory_to_use = max_to_use; + + + + for (pool = JPOOL_NUMPOOLS-1; pool >= JPOOL_PERMANENT; pool--) { + + mem->small_list[pool] = NULL; + + mem->large_list[pool] = NULL; + + } + + mem->virt_sarray_list = NULL; + + mem->virt_barray_list = NULL; + + + + mem->total_space_allocated = SIZEOF(my_memory_mgr); + + + + /* Declare ourselves open for business */ + + cinfo->mem = & mem->pub; + + + + /* Check for an environment variable JPEGMEM; if found, override the + + * default max_memory setting from jpeg_mem_init. Note that the + + * surrounding application may again override this value. + + * If your system doesn't support getenv(), define NO_GETENV to disable + + * this feature. + + */ + +#ifndef NO_GETENV + + { char * memenv; + + + + if ((memenv = getenv("JPEGMEM")) != NULL) { + + char ch = 'x'; + + + + if (sscanf(memenv, "%ld%c", &max_to_use, &ch) > 0) { + + if (ch == 'm' || ch == 'M') + + max_to_use *= 1000L; + + mem->pub.max_memory_to_use = max_to_use * 1000L; + + } + + } + + } + +#endif + + + +} + diff --git a/tools/urt/libs/jpeg6/jmemnobs.cpp b/tools/urt/libs/jpeg6/jmemnobs.cpp new file mode 100644 index 00000000..bd236e54 --- /dev/null +++ b/tools/urt/libs/jpeg6/jmemnobs.cpp @@ -0,0 +1,206 @@ +/* + + * jmemnobs.c + + * + + * Copyright (C) 1992-1994, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file provides a really simple implementation of the system- + + * dependent portion of the JPEG memory manager. This implementation + + * assumes that no backing-store files are needed: all required space + + * can be obtained from ri.Malloc(). + + * This is very portable in the sense that it'll compile on almost anything, + + * but you'd better have lots of main memory (or virtual memory) if you want + + * to process big images. + + * Note that the max_memory_to_use option is ignored by this implementation. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "radiant_jpeglib.h" + +#include "jmemsys.h" /* import the system-dependent declarations */ + + + +/* + + * Memory allocation and ri.Freeing are controlled by the regular library + + * routines ri.Malloc() and ri.Free(). + + */ + + + +GLOBAL void * + +jpeg_get_small (j_common_ptr cinfo, size_t sizeofobject) + +{ + + return (void *) malloc(sizeofobject); + +} + + + +GLOBAL void + +jpeg_free_small (j_common_ptr cinfo, void * object, size_t sizeofobject) + +{ + + free(object); + +} + + + + + +/* + + * "Large" objects are treated the same as "small" ones. + + * NB: although we include FAR keywords in the routine declarations, + + * this file won't actually work in 80x86 small/medium model; at least, + + * you probably won't be able to process useful-size images in only 64KB. + + */ + + + +GLOBAL void FAR * + +jpeg_get_large (j_common_ptr cinfo, size_t sizeofobject) + +{ + + return (void FAR *) malloc(sizeofobject); + +} + + + +GLOBAL void + +jpeg_free_large (j_common_ptr cinfo, void FAR * object, size_t sizeofobject) + +{ + + free(object); + +} + + + + + +/* + + * This routine computes the total memory space available for allocation. + + * Here we always say, "we got all you want bud!" + + */ + + + +GLOBAL long + +jpeg_mem_available (j_common_ptr cinfo, long min_bytes_needed, + + long max_bytes_needed, long already_allocated) + +{ + + return max_bytes_needed; + +} + + + + + +/* + + * Backing store (temporary file) management. + + * Since jpeg_mem_available always promised the moon, + + * this should never be called and we can just error out. + + */ + + + +GLOBAL void + +jpeg_open_backing_store (j_common_ptr cinfo, backing_store_ptr info, + + long total_bytes_needed) + +{ + + ERREXIT(cinfo, JERR_NO_BACKING_STORE); + +} + + + + + +/* + + * These routines take care of any system-dependent initialization and + + * cleanup required. Here, there isn't any. + + */ + + + +GLOBAL long + +jpeg_mem_init (j_common_ptr cinfo) + +{ + + return 0; /* just set max_memory_to_use to 0 */ + +} + + + +GLOBAL void + +jpeg_mem_term (j_common_ptr cinfo) + +{ + + /* no work */ + +} + diff --git a/tools/urt/libs/jpeg6/jmemsys.h b/tools/urt/libs/jpeg6/jmemsys.h new file mode 100644 index 00000000..9c8028bc --- /dev/null +++ b/tools/urt/libs/jpeg6/jmemsys.h @@ -0,0 +1,364 @@ +/* + + * jmemsys.h + + * + + * Copyright (C) 1992-1994, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This include file defines the interface between the system-independent + + * and system-dependent portions of the JPEG memory manager. No other + + * modules need include it. (The system-independent portion is jmemmgr.c; + + * there are several different versions of the system-dependent portion.) + + * + + * This file works as-is for the system-dependent memory managers supplied + + * in the IJG distribution. You may need to modify it if you write a + + * custom memory manager. If system-dependent changes are needed in + + * this file, the best method is to #ifdef them based on a configuration + + * symbol supplied in jconfig.h, as we have done with USE_MSDOS_MEMMGR. + + */ + + + + + +/* Short forms of external names for systems with brain-damaged linkers. */ + + + +#ifdef NEED_SHORT_EXTERNAL_NAMES + +#define jpeg_get_small jGetSmall + +#define jpeg_free_small jFreeSmall + +#define jpeg_get_large jGetLarge + +#define jpeg_free_large jFreeLarge + +#define jpeg_mem_available jMemAvail + +#define jpeg_open_backing_store jOpenBackStore + +#define jpeg_mem_init jMemInit + +#define jpeg_mem_term jMemTerm + +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + + + + +/* + + * These two functions are used to allocate and release small chunks of + + * memory. (Typically the total amount requested through jpeg_get_small is + + * no more than 20K or so; this will be requested in chunks of a few K each.) + + * Behavior should be the same as for the standard library functions malloc + + * and free; in particular, jpeg_get_small must return NULL on failure. + + * On most systems, these ARE malloc and free. jpeg_free_small is passed the + + * size of the object being freed, just in case it's needed. + + * On an 80x86 machine using small-data memory model, these manage near heap. + + */ + + + +EXTERN void * jpeg_get_small JPP((j_common_ptr cinfo, size_t sizeofobject)); + +EXTERN void jpeg_free_small JPP((j_common_ptr cinfo, void * object, + + size_t sizeofobject)); + + + +/* + + * These two functions are used to allocate and release large chunks of + + * memory (up to the total free space designated by jpeg_mem_available). + + * The interface is the same as above, except that on an 80x86 machine, + + * far pointers are used. On most other machines these are identical to + + * the jpeg_get/free_small routines; but we keep them separate anyway, + + * in case a different allocation strategy is desirable for large chunks. + + */ + + + +EXTERN void FAR * jpeg_get_large JPP((j_common_ptr cinfo,size_t sizeofobject)); + +EXTERN void jpeg_free_large JPP((j_common_ptr cinfo, void FAR * object, + + size_t sizeofobject)); + + + +/* + + * The macro MAX_ALLOC_CHUNK designates the maximum number of bytes that may + + * be requested in a single call to jpeg_get_large (and jpeg_get_small for that + + * matter, but that case should never come into play). This macro is needed + + * to model the 64Kb-segment-size limit of far addressing on 80x86 machines. + + * On those machines, we expect that jconfig.h will provide a proper value. + + * On machines with 32-bit flat address spaces, any large constant may be used. + + * + + * NB: jmemmgr.c expects that MAX_ALLOC_CHUNK will be representable as type + + * size_t and will be a multiple of sizeof(align_type). + + */ + + + +#ifndef MAX_ALLOC_CHUNK /* may be overridden in jconfig.h */ + +#define MAX_ALLOC_CHUNK 1000000000L + +#endif + + + +/* + + * This routine computes the total space still available for allocation by + + * jpeg_get_large. If more space than this is needed, backing store will be + + * used. NOTE: any memory already allocated must not be counted. + + * + + * There is a minimum space requirement, corresponding to the minimum + + * feasible buffer sizes; jmemmgr.c will request that much space even if + + * jpeg_mem_available returns zero. The maximum space needed, enough to hold + + * all working storage in memory, is also passed in case it is useful. + + * Finally, the total space already allocated is passed. If no better + + * method is available, cinfo->mem->max_memory_to_use - already_allocated + + * is often a suitable calculation. + + * + + * It is OK for jpeg_mem_available to underestimate the space available + + * (that'll just lead to more backing-store access than is really necessary). + + * However, an overestimate will lead to failure. Hence it's wise to subtract + + * a slop factor from the true available space. 5% should be enough. + + * + + * On machines with lots of virtual memory, any large constant may be returned. + + * Conversely, zero may be returned to always use the minimum amount of memory. + + */ + + + +EXTERN long jpeg_mem_available JPP((j_common_ptr cinfo, + + long min_bytes_needed, + + long max_bytes_needed, + + long already_allocated)); + + + + + +/* + + * This structure holds whatever state is needed to access a single + + * backing-store object. The read/write/close method pointers are called + + * by jmemmgr.c to manipulate the backing-store object; all other fields + + * are private to the system-dependent backing store routines. + + */ + + + +#define TEMP_NAME_LENGTH 64 /* max length of a temporary file's name */ + + + +#ifdef USE_MSDOS_MEMMGR /* DOS-specific junk */ + + + +typedef unsigned short XMSH; /* type of extended-memory handles */ + +typedef unsigned short EMSH; /* type of expanded-memory handles */ + + + +typedef union { + + short file_handle; /* DOS file handle if it's a temp file */ + + XMSH xms_handle; /* handle if it's a chunk of XMS */ + + EMSH ems_handle; /* handle if it's a chunk of EMS */ + +} handle_union; + + + +#endif /* USE_MSDOS_MEMMGR */ + + + +typedef struct backing_store_struct * backing_store_ptr; + + + +typedef struct backing_store_struct { + + /* Methods for reading/writing/closing this backing-store object */ + + JMETHOD(void, read_backing_store, (j_common_ptr cinfo, + + backing_store_ptr info, + + void FAR * buffer_address, + + long file_offset, long byte_count)); + + JMETHOD(void, write_backing_store, (j_common_ptr cinfo, + + backing_store_ptr info, + + void FAR * buffer_address, + + long file_offset, long byte_count)); + + JMETHOD(void, close_backing_store, (j_common_ptr cinfo, + + backing_store_ptr info)); + + + + /* Private fields for system-dependent backing-store management */ + +#ifdef USE_MSDOS_MEMMGR + + /* For the MS-DOS manager (jmemdos.c), we need: */ + + handle_union handle; /* reference to backing-store storage object */ + + char temp_name[TEMP_NAME_LENGTH]; /* name if it's a file */ + +#else + + /* For a typical implementation with temp files, we need: */ + + FILE * temp_file; /* stdio reference to temp file */ + + char temp_name[TEMP_NAME_LENGTH]; /* name of temp file */ + +#endif + +} backing_store_info; + + + +/* + + * Initial opening of a backing-store object. This must fill in the + + * read/write/close pointers in the object. The read/write routines + + * may take an error exit if the specified maximum file size is exceeded. + + * (If jpeg_mem_available always returns a large value, this routine can + + * just take an error exit.) + + */ + + + +EXTERN void jpeg_open_backing_store JPP((j_common_ptr cinfo, + + backing_store_ptr info, + + long total_bytes_needed)); + + + + + +/* + + * These routines take care of any system-dependent initialization and + + * cleanup required. jpeg_mem_init will be called before anything is + + * allocated (and, therefore, nothing in cinfo is of use except the error + + * manager pointer). It should return a suitable default value for + + * max_memory_to_use; this may subsequently be overridden by the surrounding + + * application. (Note that max_memory_to_use is only important if + + * jpeg_mem_available chooses to consult it ... no one else will.) + + * jpeg_mem_term may assume that all requested memory has been freed and that + + * all opened backing-store objects have been closed. + + */ + + + +EXTERN long jpeg_mem_init JPP((j_common_ptr cinfo)); + +EXTERN void jpeg_mem_term JPP((j_common_ptr cinfo)); + diff --git a/tools/urt/libs/jpeg6/jmorecfg.h b/tools/urt/libs/jpeg6/jmorecfg.h new file mode 100644 index 00000000..afb6e26c --- /dev/null +++ b/tools/urt/libs/jpeg6/jmorecfg.h @@ -0,0 +1,693 @@ +/* + + * jmorecfg.h + + * + + * Copyright (C) 1991-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains additional configuration options that customize the + + * JPEG software for special applications or support machine-dependent + + * optimizations. Most users will not need to touch this file. + + */ + + + + + +/* + + * Define BITS_IN_JSAMPLE as either + + * 8 for 8-bit sample values (the usual setting) + + * 12 for 12-bit sample values + + * Only 8 and 12 are legal data precisions for lossy JPEG according to the + + * JPEG standard, and the IJG code does not support anything else! + + * We do not support run-time selection of data precision, sorry. + + */ + + + +#define BITS_IN_JSAMPLE 8 /* use 8 or 12 */ + + + + + +/* + + * Maximum number of components (color channels) allowed in JPEG image. + + * To meet the letter of the JPEG spec, set this to 255. However, darn + + * few applications need more than 4 channels (maybe 5 for CMYK + alpha + + * mask). We recommend 10 as a reasonable compromise; use 4 if you are + + * really short on memory. (Each allowed component costs a hundred or so + + * bytes of storage, whether actually used in an image or not.) + + */ + + + +#define MAX_COMPONENTS 10 /* maximum number of image components */ + + + + + +/* + + * Basic data types. + + * You may need to change these if you have a machine with unusual data + + * type sizes; for example, "char" not 8 bits, "short" not 16 bits, + + * or "long" not 32 bits. We don't care whether "int" is 16 or 32 bits, + + * but it had better be at least 16. + + */ + + + +/* Representation of a single sample (pixel element value). + + * We frequently allocate large arrays of these, so it's important to keep + + * them small. But if you have memory to burn and access to char or short + + * arrays is very slow on your hardware, you might want to change these. + + */ + + + +#if BITS_IN_JSAMPLE == 8 + +/* JSAMPLE should be the smallest type that will hold the values 0..255. + + * You can use a signed char by having GETJSAMPLE mask it with 0xFF. + + */ + + + +#ifdef HAVE_UNSIGNED_CHAR + + + +typedef unsigned char JSAMPLE; + +#define GETJSAMPLE(value) ((int) (value)) + + + +#else /* not HAVE_UNSIGNED_CHAR */ + + + +typedef char JSAMPLE; + +#ifdef CHAR_IS_UNSIGNED + +#define GETJSAMPLE(value) ((int) (value)) + +#else + +#define GETJSAMPLE(value) ((int) (value) & 0xFF) + +#endif /* CHAR_IS_UNSIGNED */ + + + +#endif /* HAVE_UNSIGNED_CHAR */ + + + +#define MAXJSAMPLE 255 + +#define CENTERJSAMPLE 128 + + + +#endif /* BITS_IN_JSAMPLE == 8 */ + + + + + +#if BITS_IN_JSAMPLE == 12 + +/* JSAMPLE should be the smallest type that will hold the values 0..4095. + + * On nearly all machines "short" will do nicely. + + */ + + + +typedef short JSAMPLE; + +#define GETJSAMPLE(value) ((int) (value)) + + + +#define MAXJSAMPLE 4095 + +#define CENTERJSAMPLE 2048 + + + +#endif /* BITS_IN_JSAMPLE == 12 */ + + + + + +/* Representation of a DCT frequency coefficient. + + * This should be a signed value of at least 16 bits; "short" is usually OK. + + * Again, we allocate large arrays of these, but you can change to int + + * if you have memory to burn and "short" is really slow. + + */ + + + +typedef short JCOEF; + + + + + +/* Compressed datastreams are represented as arrays of JOCTET. + + * These must be EXACTLY 8 bits wide, at least once they are written to + + * external storage. Note that when using the stdio data source/destination + + * managers, this is also the data type passed to fread/fwrite. + + */ + + + +#ifdef HAVE_UNSIGNED_CHAR + + + +typedef unsigned char JOCTET; + +#define GETJOCTET(value) (value) + + + +#else /* not HAVE_UNSIGNED_CHAR */ + + + +typedef char JOCTET; + +#ifdef CHAR_IS_UNSIGNED + +#define GETJOCTET(value) (value) + +#else + +#define GETJOCTET(value) ((value) & 0xFF) + +#endif /* CHAR_IS_UNSIGNED */ + + + +#endif /* HAVE_UNSIGNED_CHAR */ + + + + + +/* These typedefs are used for various table entries and so forth. + + * They must be at least as wide as specified; but making them too big + + * won't cost a huge amount of memory, so we don't provide special + + * extraction code like we did for JSAMPLE. (In other words, these + + * typedefs live at a different point on the speed/space tradeoff curve.) + + */ + + + +/* UINT8 must hold at least the values 0..255. */ + + + +#ifdef HAVE_UNSIGNED_CHAR + +typedef unsigned char UINT8; + +#else /* not HAVE_UNSIGNED_CHAR */ + +#ifdef CHAR_IS_UNSIGNED + +typedef char UINT8; + +#else /* not CHAR_IS_UNSIGNED */ + +typedef short UINT8; + +#endif /* CHAR_IS_UNSIGNED */ + +#endif /* HAVE_UNSIGNED_CHAR */ + + + +/* UINT16 must hold at least the values 0..65535. */ + + + +#ifdef HAVE_UNSIGNED_SHORT + +typedef unsigned short UINT16; + +#else /* not HAVE_UNSIGNED_SHORT */ + +typedef unsigned int UINT16; + +#endif /* HAVE_UNSIGNED_SHORT */ + + + +/* INT16 must hold at least the values -32768..32767. */ + + + +#ifndef XMD_H /* X11/xmd.h correctly defines INT16 */ + +typedef short INT16; + +#endif + + + +/* INT32 must hold at least signed 32-bit values. */ + + + +//#ifndef XMD_H /* X11/xmd.h correctly defines INT32 */ + +//typedef long INT32; + +//#endif + + + +/* Datatype used for image dimensions. The JPEG standard only supports + + * images up to 64K*64K due to 16-bit fields in SOF markers. Therefore + + * "unsigned int" is sufficient on all machines. However, if you need to + + * handle larger images and you don't mind deviating from the spec, you + + * can change this datatype. + + */ + + + +typedef unsigned int JDIMENSION; + + + +#define JPEG_MAX_DIMENSION 65500L /* a tad under 64K to prevent overflows */ + + + + + +/* These defines are used in all function definitions and extern declarations. + + * You could modify them if you need to change function linkage conventions. + + * Another application is to make all functions global for use with debuggers + + * or code profilers that require it. + + */ + + + +#define METHODDEF static /* a function called through method pointers */ + +#define LOCAL static /* a function used only in its module */ + +#define GLOBAL /* a function referenced thru EXTERNs */ + +#define EXTERN extern /* a reference to a GLOBAL function */ + + + + + +/* Here is the pseudo-keyword for declaring pointers that must be "far" + + * on 80x86 machines. Most of the specialized coding for 80x86 is handled + + * by just saying "FAR *" where such a pointer is needed. In a few places + + * explicit coding is needed; see uses of the NEED_FAR_POINTERS symbol. + + */ + + + +#ifdef NEED_FAR_POINTERS + +#undef FAR + +#define FAR far + +#else + +#undef FAR + +#define FAR + +#endif + + + + + +/* + + * On a few systems, type boolean and/or its values FALSE, TRUE may appear + + * in standard header files. Or you may have conflicts with application- + + * specific header files that you want to include together with these files. + + * Defining HAVE_BOOLEAN before including jpeglib.h should make it work. + + */ + + + +//#ifndef HAVE_BOOLEAN + +//typedef int boolean; + +//#endif + +#ifndef FALSE /* in case these macros already exist */ + +#define FALSE 0 /* values of boolean */ + +#endif + +#ifndef TRUE + +#define TRUE 1 + +#endif + + + + + +/* + + * The remaining options affect code selection within the JPEG library, + + * but they don't need to be visible to most applications using the library. + + * To minimize application namespace pollution, the symbols won't be + + * defined unless JPEG_INTERNALS or JPEG_INTERNAL_OPTIONS has been defined. + + */ + + + +#ifdef JPEG_INTERNALS + +#define JPEG_INTERNAL_OPTIONS + +#endif + + + +#ifdef JPEG_INTERNAL_OPTIONS + + + + + +/* + + * These defines indicate whether to include various optional functions. + + * Undefining some of these symbols will produce a smaller but less capable + + * library. Note that you can leave certain source files out of the + + * compilation/linking process if you've #undef'd the corresponding symbols. + + * (You may HAVE to do that if your compiler doesn't like null source files.) + + */ + + + +/* Arithmetic coding is unsupported for legal reasons. Complaints to IBM. */ + + + +/* Capability options common to encoder and decoder: */ + + + +#undef DCT_ISLOW_SUPPORTED /* slow but accurate integer algorithm */ + +#undef DCT_IFAST_SUPPORTED /* faster, less accurate integer method */ + +#define DCT_FLOAT_SUPPORTED /* floating-point: accurate, fast on fast HW */ + + + +/* Encoder capability options: */ + + + +#undef C_ARITH_CODING_SUPPORTED /* Arithmetic coding back end? */ + +#define C_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ + +#define C_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ + +#define ENTROPY_OPT_SUPPORTED /* Optimization of entropy coding parms? */ + +/* Note: if you selected 12-bit data precision, it is dangerous to turn off + + * ENTROPY_OPT_SUPPORTED. The standard Huffman tables are only good for 8-bit + + * precision, so jchuff.c normally uses entropy optimization to compute + + * usable tables for higher precision. If you don't want to do optimization, + + * you'll have to supply different default Huffman tables. + + * The exact same statements apply for progressive JPEG: the default tables + + * don't work for progressive mode. (This may get fixed, however.) + + */ + +#define INPUT_SMOOTHING_SUPPORTED /* Input image smoothing option? */ + + + +/* Decoder capability options: */ + + + +#undef D_ARITH_CODING_SUPPORTED /* Arithmetic coding back end? */ + +#undef D_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ + +#undef D_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ + +#undef BLOCK_SMOOTHING_SUPPORTED /* Block smoothing? (Progressive only) */ + +#undef IDCT_SCALING_SUPPORTED /* Output rescaling via IDCT? */ + +#undef UPSAMPLE_SCALING_SUPPORTED /* Output rescaling at upsample stage? */ + +#undef UPSAMPLE_MERGING_SUPPORTED /* Fast path for sloppy upsampling? */ + +#undef QUANT_1PASS_SUPPORTED /* 1-pass color quantization? */ + +#undef QUANT_2PASS_SUPPORTED /* 2-pass color quantization? */ + + + +/* more capability options later, no doubt */ + + + + + +/* + + * Ordering of RGB data in scanlines passed to or from the application. + + * If your application wants to deal with data in the order B,G,R, just + + * change these macros. You can also deal with formats such as R,G,B,X + + * (one extra byte per pixel) by changing RGB_PIXELSIZE. Note that changing + + * the offsets will also change the order in which colormap data is organized. + + * RESTRICTIONS: + + * 1. The sample applications cjpeg,djpeg do NOT support modified RGB formats. + + * 2. These macros only affect RGB<=>YCbCr color conversion, so they are not + + * useful if you are using JPEG color spaces other than YCbCr or grayscale. + + * 3. The color quantizer modules will not behave desirably if RGB_PIXELSIZE + + * is not 3 (they don't understand about dummy color components!). So you + + * can't use color quantization if you change that value. + + */ + + + +#define RGB_RED 0 /* Offset of Red in an RGB scanline element */ + +#define RGB_GREEN 1 /* Offset of Green */ + +#define RGB_BLUE 2 /* Offset of Blue */ + +// http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=900 +// ydnar: setting this fucks jpeg loading in q3map2, disabling "fix" (3) +#define RGB_PIXELSIZE 4 /* JSAMPLEs per RGB scanline element */ + + + + + +/* Definitions for speed-related optimizations. */ + + + + + +/* If your compiler supports inline functions, define INLINE + + * as the inline keyword; otherwise define it as empty. + + */ + + + +#ifndef INLINE + +#ifdef __GNUC__ /* for instance, GNU C knows about inline */ + +#define INLINE __inline__ + +#endif + +#ifndef INLINE + +#define INLINE /* default is to define it as empty */ + +#endif + +#endif + + + + + +/* On some machines (notably 68000 series) "int" is 32 bits, but multiplying + + * two 16-bit shorts is faster than multiplying two ints. Define MULTIPLIER + + * as short on such a machine. MULTIPLIER must be at least 16 bits wide. + + */ + + + +#ifndef MULTIPLIER + +#define MULTIPLIER int /* type for fastest integer multiply */ + +#endif + + + + + +/* FAST_FLOAT should be either float or double, whichever is done faster + + * by your compiler. (Note that this type is only used in the floating point + + * DCT routines, so it only matters if you've defined DCT_FLOAT_SUPPORTED.) + + * Typically, float is faster in ANSI C compilers, while double is faster in + + * pre-ANSI compilers (because they insist on converting to double anyway). + + * The code below therefore chooses float if we have ANSI-style prototypes. + + */ + + + +#ifndef FAST_FLOAT + +#ifdef HAVE_PROTOTYPES + +#define FAST_FLOAT float + +#else + +#define FAST_FLOAT double + +#endif + +#endif + + + +#endif /* JPEG_INTERNAL_OPTIONS */ diff --git a/tools/urt/libs/jpeg6/jpeg6.dsp b/tools/urt/libs/jpeg6/jpeg6.dsp new file mode 100644 index 00000000..99b49748 --- /dev/null +++ b/tools/urt/libs/jpeg6/jpeg6.dsp @@ -0,0 +1,218 @@ +# Microsoft Developer Studio Project File - Name="jpeg6" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=jpeg6 - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "jpeg6.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "jpeg6.mak" CFG="jpeg6 - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "jpeg6 - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "jpeg6 - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "jpeg6" +# PROP Scc_LocalPath "." +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "jpeg6 - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +MTL=midl.exe +F90=df.exe +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /O2 /I "../" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /FR /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 +# ADD RSC /l 0x409 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "jpeg6 - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +MTL=midl.exe +F90=df.exe +# ADD BASE CPP /nologo /W3 /GX /Z7 /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /MDd /W3 /Z7 /Od /I "../" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 +# ADD RSC /l 0x409 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "jpeg6 - Win32 Release" +# Name "jpeg6 - Win32 Debug" +# Begin Source File + +SOURCE=.\Jchuff.h +# End Source File +# Begin Source File + +SOURCE=.\JCOMAPI.cpp +# End Source File +# Begin Source File + +SOURCE=.\Jconfig.h +# End Source File +# Begin Source File + +SOURCE=.\JDAPIMIN.cpp +# End Source File +# Begin Source File + +SOURCE=.\JDAPISTD.cpp +# End Source File +# Begin Source File + +SOURCE=.\JDATASRC.cpp +# End Source File +# Begin Source File + +SOURCE=.\JDCOEFCT.cpp +# End Source File +# Begin Source File + +SOURCE=.\JDCOLOR.cpp +# End Source File +# Begin Source File + +SOURCE=.\Jdct.h +# End Source File +# Begin Source File + +SOURCE=.\JDDCTMGR.cpp +# End Source File +# Begin Source File + +SOURCE=.\JDHUFF.cpp +# End Source File +# Begin Source File + +SOURCE=.\Jdhuff.h +# End Source File +# Begin Source File + +SOURCE=.\JDINPUT.cpp +# End Source File +# Begin Source File + +SOURCE=.\JDMAINCT.cpp +# End Source File +# Begin Source File + +SOURCE=.\JDMARKER.cpp +# End Source File +# Begin Source File + +SOURCE=.\JDMASTER.cpp +# End Source File +# Begin Source File + +SOURCE=.\JDPOSTCT.cpp +# End Source File +# Begin Source File + +SOURCE=.\JDSAMPLE.cpp +# End Source File +# Begin Source File + +SOURCE=.\JDTRANS.cpp +# End Source File +# Begin Source File + +SOURCE=.\JERROR.cpp +# End Source File +# Begin Source File + +SOURCE=.\Jerror.h +# End Source File +# Begin Source File + +SOURCE=.\JFDCTFLT.cpp +# End Source File +# Begin Source File + +SOURCE=.\JIDCTFLT.cpp +# End Source File +# Begin Source File + +SOURCE=.\Jinclude.h +# End Source File +# Begin Source File + +SOURCE=.\JMEMMGR.cpp +# End Source File +# Begin Source File + +SOURCE=.\JMEMNOBS.cpp +# End Source File +# Begin Source File + +SOURCE=.\Jmemsys.h +# End Source File +# Begin Source File + +SOURCE=.\Jmorecfg.h +# End Source File +# Begin Source File + +SOURCE=.\Jpegint.h +# End Source File +# Begin Source File + +SOURCE=.\JPGLOAD.cpp +# End Source File +# Begin Source File + +SOURCE=.\JUTILS.cpp +# End Source File +# Begin Source File + +SOURCE=.\Jversion.h +# End Source File +# End Target +# End Project diff --git a/tools/urt/libs/jpeg6/jpeg6.vcproj b/tools/urt/libs/jpeg6/jpeg6.vcproj new file mode 100644 index 00000000..a0d973f7 --- /dev/null +++ b/tools/urt/libs/jpeg6/jpeg6.vcproj @@ -0,0 +1,692 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/urt/libs/jpeg6/jpegint.h b/tools/urt/libs/jpeg6/jpegint.h new file mode 100644 index 00000000..9b02525d --- /dev/null +++ b/tools/urt/libs/jpeg6/jpegint.h @@ -0,0 +1,776 @@ +/* + + * jpegint.h + + * + + * Copyright (C) 1991-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file provides common declarations for the various JPEG modules. + + * These declarations are considered internal to the JPEG library; most + + * applications using the library shouldn't need to include this file. + + */ + + + + + +/* Declarations for both compression & decompression */ + + + +typedef enum { /* Operating modes for buffer controllers */ + + JBUF_PASS_THRU, /* Plain stripwise operation */ + + /* Remaining modes require a full-image buffer to have been created */ + + JBUF_SAVE_SOURCE, /* Run source subobject only, save output */ + + JBUF_CRANK_DEST, /* Run dest subobject only, using saved data */ + + JBUF_SAVE_AND_PASS /* Run both subobjects, save output */ + +} J_BUF_MODE; + + + +/* Values of global_state field (jdapi.c has some dependencies on ordering!) */ + +#define CSTATE_START 100 /* after create_compress */ + +#define CSTATE_SCANNING 101 /* start_compress done, write_scanlines OK */ + +#define CSTATE_RAW_OK 102 /* start_compress done, write_raw_data OK */ + +#define CSTATE_WRCOEFS 103 /* jpeg_write_coefficients done */ + +#define DSTATE_START 200 /* after create_decompress */ + +#define DSTATE_INHEADER 201 /* reading header markers, no SOS yet */ + +#define DSTATE_READY 202 /* found SOS, ready for start_decompress */ + +#define DSTATE_PRELOAD 203 /* reading multiscan file in start_decompress*/ + +#define DSTATE_PRESCAN 204 /* performing dummy pass for 2-pass quant */ + +#define DSTATE_SCANNING 205 /* start_decompress done, read_scanlines OK */ + +#define DSTATE_RAW_OK 206 /* start_decompress done, read_raw_data OK */ + +#define DSTATE_BUFIMAGE 207 /* expecting jpeg_start_output */ + +#define DSTATE_BUFPOST 208 /* looking for SOS/EOI in jpeg_finish_output */ + +#define DSTATE_RDCOEFS 209 /* reading file in jpeg_read_coefficients */ + +#define DSTATE_STOPPING 210 /* looking for EOI in jpeg_finish_decompress */ + + + + + +/* Declarations for compression modules */ + + + +/* Master control module */ + +struct jpeg_comp_master { + + JMETHOD(void, prepare_for_pass, (j_compress_ptr cinfo)); + + JMETHOD(void, pass_startup, (j_compress_ptr cinfo)); + + JMETHOD(void, finish_pass, (j_compress_ptr cinfo)); + + + + /* State variables made visible to other modules */ + + boolean call_pass_startup; /* True if pass_startup must be called */ + + boolean is_last_pass; /* True during last pass */ + +}; + + + +/* Main buffer control (downsampled-data buffer) */ + +struct jpeg_c_main_controller { + + JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode)); + + JMETHOD(void, process_data, (j_compress_ptr cinfo, + + JSAMPARRAY input_buf, JDIMENSION *in_row_ctr, + + JDIMENSION in_rows_avail)); + +}; + + + +/* Compression preprocessing (downsampling input buffer control) */ + +struct jpeg_c_prep_controller { + + JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode)); + + JMETHOD(void, pre_process_data, (j_compress_ptr cinfo, + + JSAMPARRAY input_buf, + + JDIMENSION *in_row_ctr, + + JDIMENSION in_rows_avail, + + JSAMPIMAGE output_buf, + + JDIMENSION *out_row_group_ctr, + + JDIMENSION out_row_groups_avail)); + +}; + + + +/* Coefficient buffer control */ + +struct jpeg_c_coef_controller { + + JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode)); + + JMETHOD(boolean, compress_data, (j_compress_ptr cinfo, + + JSAMPIMAGE input_buf)); + +}; + + + +/* Colorspace conversion */ + +struct jpeg_color_converter { + + JMETHOD(void, start_pass, (j_compress_ptr cinfo)); + + JMETHOD(void, color_convert, (j_compress_ptr cinfo, + + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + + JDIMENSION output_row, int num_rows)); + +}; + + + +/* Downsampling */ + +struct jpeg_downsampler { + + JMETHOD(void, start_pass, (j_compress_ptr cinfo)); + + JMETHOD(void, downsample, (j_compress_ptr cinfo, + + JSAMPIMAGE input_buf, JDIMENSION in_row_index, + + JSAMPIMAGE output_buf, + + JDIMENSION out_row_group_index)); + + + + boolean need_context_rows; /* TRUE if need rows above & below */ + +}; + + + +/* Forward DCT (also controls coefficient quantization) */ + +struct jpeg_forward_dct { + + JMETHOD(void, start_pass, (j_compress_ptr cinfo)); + + /* perhaps this should be an array??? */ + + JMETHOD(void, forward_DCT, (j_compress_ptr cinfo, + + jpeg_component_info * compptr, + + JSAMPARRAY sample_data, JBLOCKROW coef_blocks, + + JDIMENSION start_row, JDIMENSION start_col, + + JDIMENSION num_blocks)); + +}; + + + +/* Entropy encoding */ + +struct jpeg_entropy_encoder { + + JMETHOD(void, start_pass, (j_compress_ptr cinfo, boolean gather_statistics)); + + JMETHOD(boolean, encode_mcu, (j_compress_ptr cinfo, JBLOCKROW *MCU_data)); + + JMETHOD(void, finish_pass, (j_compress_ptr cinfo)); + +}; + + + +/* Marker writing */ + +struct jpeg_marker_writer { + + /* write_any_marker is exported for use by applications */ + + /* Probably only COM and APPn markers should be written */ + + JMETHOD(void, write_any_marker, (j_compress_ptr cinfo, int marker, + + const JOCTET *dataptr, unsigned int datalen)); + + JMETHOD(void, write_file_header, (j_compress_ptr cinfo)); + + JMETHOD(void, write_frame_header, (j_compress_ptr cinfo)); + + JMETHOD(void, write_scan_header, (j_compress_ptr cinfo)); + + JMETHOD(void, write_file_trailer, (j_compress_ptr cinfo)); + + JMETHOD(void, write_tables_only, (j_compress_ptr cinfo)); + +}; + + + + + +/* Declarations for decompression modules */ + + + +/* Master control module */ + +struct jpeg_decomp_master { + + JMETHOD(void, prepare_for_output_pass, (j_decompress_ptr cinfo)); + + JMETHOD(void, finish_output_pass, (j_decompress_ptr cinfo)); + + + + /* State variables made visible to other modules */ + + boolean is_dummy_pass; /* True during 1st pass for 2-pass quant */ + +}; + + + +/* Input control module */ + +struct jpeg_input_controller { + + JMETHOD(int, consume_input, (j_decompress_ptr cinfo)); + + JMETHOD(void, reset_input_controller, (j_decompress_ptr cinfo)); + + JMETHOD(void, start_input_pass, (j_decompress_ptr cinfo)); + + JMETHOD(void, finish_input_pass, (j_decompress_ptr cinfo)); + + + + /* State variables made visible to other modules */ + + boolean has_multiple_scans; /* True if file has multiple scans */ + + boolean eoi_reached; /* True when EOI has been consumed */ + +}; + + + +/* Main buffer control (downsampled-data buffer) */ + +struct jpeg_d_main_controller { + + JMETHOD(void, start_pass, (j_decompress_ptr cinfo, J_BUF_MODE pass_mode)); + + JMETHOD(void, process_data, (j_decompress_ptr cinfo, + + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + + JDIMENSION out_rows_avail)); + +}; + + + +/* Coefficient buffer control */ + +struct jpeg_d_coef_controller { + + JMETHOD(void, start_input_pass, (j_decompress_ptr cinfo)); + + JMETHOD(int, consume_data, (j_decompress_ptr cinfo)); + + JMETHOD(void, start_output_pass, (j_decompress_ptr cinfo)); + + JMETHOD(int, decompress_data, (j_decompress_ptr cinfo, + + JSAMPIMAGE output_buf)); + + /* Pointer to array of coefficient virtual arrays, or NULL if none */ + + jvirt_barray_ptr *coef_arrays; + +}; + + + +/* Decompression postprocessing (color quantization buffer control) */ + +struct jpeg_d_post_controller { + + JMETHOD(void, start_pass, (j_decompress_ptr cinfo, J_BUF_MODE pass_mode)); + + JMETHOD(void, post_process_data, (j_decompress_ptr cinfo, + + JSAMPIMAGE input_buf, + + JDIMENSION *in_row_group_ctr, + + JDIMENSION in_row_groups_avail, + + JSAMPARRAY output_buf, + + JDIMENSION *out_row_ctr, + + JDIMENSION out_rows_avail)); + +}; + + + +/* Marker reading & parsing */ + +struct jpeg_marker_reader { + + JMETHOD(void, reset_marker_reader, (j_decompress_ptr cinfo)); + + /* Read markers until SOS or EOI. + + * Returns same codes as are defined for jpeg_consume_input: + + * JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI. + + */ + + JMETHOD(int, read_markers, (j_decompress_ptr cinfo)); + + /* Read a restart marker --- exported for use by entropy decoder only */ + + jpeg_marker_parser_method read_restart_marker; + + /* Application-overridable marker processing methods */ + + jpeg_marker_parser_method process_COM; + + jpeg_marker_parser_method process_APPn[16]; + + + + /* State of marker reader --- nominally internal, but applications + + * supplying COM or APPn handlers might like to know the state. + + */ + + boolean saw_SOI; /* found SOI? */ + + boolean saw_SOF; /* found SOF? */ + + int next_restart_num; /* next restart number expected (0-7) */ + + unsigned int discarded_bytes; /* # of bytes skipped looking for a marker */ + +}; + + + +/* Entropy decoding */ + +struct jpeg_entropy_decoder { + + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + + JMETHOD(boolean, decode_mcu, (j_decompress_ptr cinfo, + + JBLOCKROW *MCU_data)); + +}; + + + +/* Inverse DCT (also performs dequantization) */ + +typedef JMETHOD(void, inverse_DCT_method_ptr, + + (j_decompress_ptr cinfo, jpeg_component_info * compptr, + + JCOEFPTR coef_block, + + JSAMPARRAY output_buf, JDIMENSION output_col)); + + + +struct jpeg_inverse_dct { + + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + + /* It is useful to allow each component to have a separate IDCT method. */ + + inverse_DCT_method_ptr inverse_DCT[MAX_COMPONENTS]; + +}; + + + +/* Upsampling (note that upsampler must also call color converter) */ + +struct jpeg_upsampler { + + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + + JMETHOD(void, upsample, (j_decompress_ptr cinfo, + + JSAMPIMAGE input_buf, + + JDIMENSION *in_row_group_ctr, + + JDIMENSION in_row_groups_avail, + + JSAMPARRAY output_buf, + + JDIMENSION *out_row_ctr, + + JDIMENSION out_rows_avail)); + + + + boolean need_context_rows; /* TRUE if need rows above & below */ + +}; + + + +/* Colorspace conversion */ + +struct jpeg_color_deconverter { + + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + + JMETHOD(void, color_convert, (j_decompress_ptr cinfo, + + JSAMPIMAGE input_buf, JDIMENSION input_row, + + JSAMPARRAY output_buf, int num_rows)); + +}; + + + +/* Color quantization or color precision reduction */ + +struct jpeg_color_quantizer { + + JMETHOD(void, start_pass, (j_decompress_ptr cinfo, boolean is_pre_scan)); + + JMETHOD(void, color_quantize, (j_decompress_ptr cinfo, + + JSAMPARRAY input_buf, JSAMPARRAY output_buf, + + int num_rows)); + + JMETHOD(void, finish_pass, (j_decompress_ptr cinfo)); + + JMETHOD(void, new_color_map, (j_decompress_ptr cinfo)); + +}; + + + + + +/* Miscellaneous useful macros */ + + + +#undef MAX + +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +#undef MIN + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) + + + + + +/* We assume that right shift corresponds to signed division by 2 with + + * rounding towards minus infinity. This is correct for typical "arithmetic + + * shift" instructions that shift in copies of the sign bit. But some + + * C compilers implement >> with an unsigned shift. For these machines you + + * must define RIGHT_SHIFT_IS_UNSIGNED. + + * RIGHT_SHIFT provides a proper signed right shift of an INT32 quantity. + + * It is only applied with constant shift counts. SHIFT_TEMPS must be + + * included in the variables of any routine using RIGHT_SHIFT. + + */ + + + +#ifdef RIGHT_SHIFT_IS_UNSIGNED + +#define SHIFT_TEMPS INT32 shift_temp; + +#define RIGHT_SHIFT(x,shft) \ + + ((shift_temp = (x)) < 0 ? \ + + (shift_temp >> (shft)) | ((~((INT32) 0)) << (32-(shft))) : \ + + (shift_temp >> (shft))) + +#else + +#define SHIFT_TEMPS + +#define RIGHT_SHIFT(x,shft) ((x) >> (shft)) + +#endif + + + + + +/* Short forms of external names for systems with brain-damaged linkers. */ + + + +#ifdef NEED_SHORT_EXTERNAL_NAMES + +#define jinit_compress_master jICompress + +#define jinit_c_master_control jICMaster + +#define jinit_c_main_controller jICMainC + +#define jinit_c_prep_controller jICPrepC + +#define jinit_c_coef_controller jICCoefC + +#define jinit_color_converter jICColor + +#define jinit_downsampler jIDownsampler + +#define jinit_forward_dct jIFDCT + +#define jinit_huff_encoder jIHEncoder + +#define jinit_phuff_encoder jIPHEncoder + +#define jinit_marker_writer jIMWriter + +#define jinit_master_decompress jIDMaster + +#define jinit_d_main_controller jIDMainC + +#define jinit_d_coef_controller jIDCoefC + +#define jinit_d_post_controller jIDPostC + +#define jinit_input_controller jIInCtlr + +#define jinit_marker_reader jIMReader + +#define jinit_huff_decoder jIHDecoder + +#define jinit_phuff_decoder jIPHDecoder + +#define jinit_inverse_dct jIIDCT + +#define jinit_upsampler jIUpsampler + +#define jinit_color_deconverter jIDColor + +#define jinit_1pass_quantizer jI1Quant + +#define jinit_2pass_quantizer jI2Quant + +#define jinit_merged_upsampler jIMUpsampler + +#define jinit_memory_mgr jIMemMgr + +#define jdiv_round_up jDivRound + +#define jround_up jRound + +#define jcopy_sample_rows jCopySamples + +#define jcopy_block_row jCopyBlocks + +#define jzero_far jZeroFar + +#define jpeg_zigzag_order jZIGTable + +#define jpeg_natural_order jZAGTable + +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + + + + +/* Compression module initialization routines */ + +EXTERN void jinit_compress_master JPP((j_compress_ptr cinfo)); + +EXTERN void jinit_c_master_control JPP((j_compress_ptr cinfo, + + boolean transcode_only)); + +EXTERN void jinit_c_main_controller JPP((j_compress_ptr cinfo, + + boolean need_full_buffer)); + +EXTERN void jinit_c_prep_controller JPP((j_compress_ptr cinfo, + + boolean need_full_buffer)); + +EXTERN void jinit_c_coef_controller JPP((j_compress_ptr cinfo, + + boolean need_full_buffer)); + +EXTERN void jinit_color_converter JPP((j_compress_ptr cinfo)); + +EXTERN void jinit_downsampler JPP((j_compress_ptr cinfo)); + +EXTERN void jinit_forward_dct JPP((j_compress_ptr cinfo)); + +EXTERN void jinit_huff_encoder JPP((j_compress_ptr cinfo)); + +EXTERN void jinit_phuff_encoder JPP((j_compress_ptr cinfo)); + +EXTERN void jinit_marker_writer JPP((j_compress_ptr cinfo)); + +/* Decompression module initialization routines */ + +EXTERN void jinit_master_decompress JPP((j_decompress_ptr cinfo)); + +EXTERN void jinit_d_main_controller JPP((j_decompress_ptr cinfo, + + boolean need_full_buffer)); + +EXTERN void jinit_d_coef_controller JPP((j_decompress_ptr cinfo, + + boolean need_full_buffer)); + +EXTERN void jinit_d_post_controller JPP((j_decompress_ptr cinfo, + + boolean need_full_buffer)); + +EXTERN void jinit_input_controller JPP((j_decompress_ptr cinfo)); + +EXTERN void jinit_marker_reader JPP((j_decompress_ptr cinfo)); + +EXTERN void jinit_huff_decoder JPP((j_decompress_ptr cinfo)); + +EXTERN void jinit_phuff_decoder JPP((j_decompress_ptr cinfo)); + +EXTERN void jinit_inverse_dct JPP((j_decompress_ptr cinfo)); + +EXTERN void jinit_upsampler JPP((j_decompress_ptr cinfo)); + +EXTERN void jinit_color_deconverter JPP((j_decompress_ptr cinfo)); + +EXTERN void jinit_1pass_quantizer JPP((j_decompress_ptr cinfo)); + +EXTERN void jinit_2pass_quantizer JPP((j_decompress_ptr cinfo)); + +EXTERN void jinit_merged_upsampler JPP((j_decompress_ptr cinfo)); + +/* Memory manager initialization */ + +EXTERN void jinit_memory_mgr JPP((j_common_ptr cinfo)); + + + +/* Utility routines in jutils.c */ + +EXTERN long jdiv_round_up JPP((long a, long b)); + +EXTERN long jround_up JPP((long a, long b)); + +EXTERN void jcopy_sample_rows JPP((JSAMPARRAY input_array, int source_row, + + JSAMPARRAY output_array, int dest_row, + + int num_rows, JDIMENSION num_cols)); + +EXTERN void jcopy_block_row JPP((JBLOCKROW input_row, JBLOCKROW output_row, + + JDIMENSION num_blocks)); + +EXTERN void jzero_far JPP((void FAR * target, size_t bytestozero)); + +/* Constant tables in jutils.c */ + +extern const int jpeg_zigzag_order[]; /* natural coef order to zigzag order */ + +extern const int jpeg_natural_order[]; /* zigzag coef order to natural order */ + + + +/* Suppress undefined-structure complaints if necessary. */ + + + +#ifdef INCOMPLETE_TYPES_BROKEN + +#ifndef AM_MEMORY_MANAGER /* only jmemmgr.c defines these */ + +struct jvirt_sarray_control { long dummy; }; + +struct jvirt_barray_control { long dummy; }; + +#endif + +#endif /* INCOMPLETE_TYPES_BROKEN */ + diff --git a/tools/urt/libs/jpeg6/jpgload.cpp b/tools/urt/libs/jpeg6/jpgload.cpp new file mode 100644 index 00000000..686799fa --- /dev/null +++ b/tools/urt/libs/jpeg6/jpgload.cpp @@ -0,0 +1,166 @@ + + +#include "radiant_jpeglib.h" +#include "jerror.h" +#include + +GLOBAL int LoadJPGBuff(unsigned char *fbuffer, int bufsize, unsigned char **pic, int *width, int *height ) +{ + + /* This struct contains the JPEG decompression parameters and pointers to + * working space (which is allocated as needed by the JPEG library). + */ + struct jpeg_decompress_struct cinfo; + /* We use our private extension JPEG error handler. + * Note that this struct must live as long as the main JPEG parameter + * struct, to avoid dangling-pointer problems. + */ + /* This struct represents a JPEG error handler. It is declared separately + * because applications often want to supply a specialized error handler + * (see the second half of this file for an example). But here we just + * take the easy way out and use the standard error handler, which will + * print a message on stderr and call exit() if compression fails. + * Note that this struct must live as long as the main JPEG parameter + * struct, to avoid dangling-pointer problems. + */ + + struct jpeg_error_mgr jerr; + /* More stuff */ + JSAMPARRAY buffer; /* Output row buffer */ + int row_stride; /* physical row width in output buffer */ + unsigned char *out, *bbuf; + int nSize; + int jmpret; + + // Rad additions: initialize the longjmp buffer + jmpret = setjmp( rad_loadfailed ); + if (jmpret != 0) + { + *pic = (unsigned char *)rad_errormsg; + return -1; + } + + /* Step 1: allocate and initialize JPEG decompression object */ + + /* We have to set up the error handler first, in case the initialization + * step fails. (Unlikely, but it could happen if you are out of memory.) + * This routine fills in the contents of struct jerr, and returns jerr's + * address which we place into the link field in cinfo. + */ + cinfo.err = jpeg_std_error(&jerr); + + /* Now we can initialize the JPEG decompression object. */ + jpeg_create_decompress(&cinfo); + + /* Step 2: specify data source (eg, a file) */ + + jpeg_stdio_src(&cinfo, fbuffer, bufsize); + + /* Step 3: read file parameters with jpeg_read_header() */ + + (void) jpeg_read_header(&cinfo, TRUE); + /* We can ignore the return value from jpeg_read_header since + * (a) suspension is not possible with the stdio data source, and + * (b) we passed TRUE to reject a tables-only JPEG file as an error. + * See libjpeg.doc for more info. + */ + + /* Step 4: set parameters for decompression */ + + /* In this example, we don't need to change any of the defaults set by + * jpeg_read_header(), so we do nothing here. + */ + + /* Step 5: Start decompressor */ + + (void) jpeg_start_decompress(&cinfo); + /* We can ignore the return value since suspension is not possible + * with the stdio data source. + */ + + /* ydnar: radiant only handles RGB, non-progressive format jpegs */ + if( cinfo.output_components != 4 ) + { + *pic = const_cast(reinterpret_cast("Non-RGB JPEG encountered (unsupported)")); + return -1; + } + if( cinfo.progressive_mode ) + { + *pic = const_cast(reinterpret_cast("Progressive JPEG encountered (unsupported)")); + return -1; + } + + /* We may need to do some setup of our own at this point before reading + * the data. After jpeg_start_decompress() we have the correct scaled + * output image dimensions available, as well as the output colormap + * if we asked for color quantization. + * In this example, we need to make an output work buffer of the right size. + */ + + /* JSAMPLEs per row in output buffer */ + row_stride = cinfo.output_width * cinfo.output_components; + nSize = cinfo.output_width*cinfo.output_height*cinfo.output_components; + + out = reinterpret_cast( malloc( nSize+ 1 ) ); + memset( out, 255, nSize + 1 ); + + *pic = out; + *width = cinfo.output_width; + *height = cinfo.output_height; + + /* Step 6: while (scan lines remain to be read) */ + /* jpeg_read_scanlines(...); */ + + /* Here we use the library's state variable cinfo.output_scanline as the + * loop counter, so that we don't have to keep track ourselves. + */ + while (cinfo.output_scanline < cinfo.output_height) + { + /* jpeg_read_scanlines expects an array of pointers to scanlines. + * Here the array is only one element long, but you could ask for + * more than one scanline at a time if that's more convenient. + */ + bbuf = out + row_stride * cinfo.output_scanline; + buffer = &bbuf; + (void) jpeg_read_scanlines( &cinfo, buffer, 1 ); + } + + // clear all the alphas to 255 + { + int i, j; + unsigned char *buf; + + buf = *pic; + + j = cinfo.output_width * cinfo.output_height * 4; + for ( i = 3 ; i < j ; i+=4 ) { + buf[i] = 255; + } + } + + /* Step 7: Finish decompression */ + + (void) jpeg_finish_decompress(&cinfo); + /* We can ignore the return value since suspension is not possible + * with the stdio data source. + */ + + /* Step 8: Release JPEG decompression object */ + + /* This is an important step since it will release a good deal of memory. */ + jpeg_destroy_decompress(&cinfo); + + /* After finish_decompress, we can close the input file. + * Here we postpone it until after no more JPEG errors are possible, + * so as to simplify the setjmp error logic above. (Actually, I don't + * think that jpeg_destroy can do an error exit, but why assume anything...) + */ + //free (fbuffer); + + /* At this point you may want to check to see whether any corrupt-data + * warnings occurred (test whether jerr.pub.num_warnings is nonzero). + */ + + /* And we're done! */ + return 0; +} diff --git a/tools/urt/libs/jpeg6/jutils.cpp b/tools/urt/libs/jpeg6/jutils.cpp new file mode 100644 index 00000000..f5454a6e --- /dev/null +++ b/tools/urt/libs/jpeg6/jutils.cpp @@ -0,0 +1,350 @@ +/* + + * jutils.c + + * + + * Copyright (C) 1991-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains tables and miscellaneous utility routines needed + + * for both compression and decompression. + + * Note we prefix all global names with "j" to minimize conflicts with + + * a surrounding application. + + */ + + + +#define JPEG_INTERNALS + +#include "jinclude.h" + +#include "radiant_jpeglib.h" + + + + + +/* + + * jpeg_zigzag_order[i] is the zigzag-order position of the i'th element + + * of a DCT block read in natural order (left to right, top to bottom). + + */ + + + +const int jpeg_zigzag_order[DCTSIZE2] = { + + 0, 1, 5, 6, 14, 15, 27, 28, + + 2, 4, 7, 13, 16, 26, 29, 42, + + 3, 8, 12, 17, 25, 30, 41, 43, + + 9, 11, 18, 24, 31, 40, 44, 53, + + 10, 19, 23, 32, 39, 45, 52, 54, + + 20, 22, 33, 38, 46, 51, 55, 60, + + 21, 34, 37, 47, 50, 56, 59, 61, + + 35, 36, 48, 49, 57, 58, 62, 63 + +}; + + + +/* + + * jpeg_natural_order[i] is the natural-order position of the i'th element + + * of zigzag order. + + * + + * When reading corrupted data, the Huffman decoders could attempt + + * to reference an entry beyond the end of this array (if the decoded + + * zero run length reaches past the end of the block). To prevent + + * wild stores without adding an inner-loop test, we put some extra + + * "63"s after the real entries. This will cause the extra coefficient + + * to be stored in location 63 of the block, not somewhere random. + + * The worst case would be a run-length of 15, which means we need 16 + + * fake entries. + + */ + + + +const int jpeg_natural_order[DCTSIZE2+16] = { + + 0, 1, 8, 16, 9, 2, 3, 10, + + 17, 24, 32, 25, 18, 11, 4, 5, + + 12, 19, 26, 33, 40, 48, 41, 34, + + 27, 20, 13, 6, 7, 14, 21, 28, + + 35, 42, 49, 56, 57, 50, 43, 36, + + 29, 22, 15, 23, 30, 37, 44, 51, + + 58, 59, 52, 45, 38, 31, 39, 46, + + 53, 60, 61, 54, 47, 55, 62, 63, + + 63, 63, 63, 63, 63, 63, 63, 63, /* extra entries for safety in decoder */ + + 63, 63, 63, 63, 63, 63, 63, 63 + +}; + + + + + +/* + + * Arithmetic utilities + + */ + + + +GLOBAL long + +jdiv_round_up (long a, long b) + +/* Compute a/b rounded up to next integer, ie, ceil(a/b) */ + +/* Assumes a >= 0, b > 0 */ + +{ + + return (a + b - 1L) / b; + +} + + + + + +GLOBAL long + +jround_up (long a, long b) + +/* Compute a rounded up to next multiple of b, ie, ceil(a/b)*b */ + +/* Assumes a >= 0, b > 0 */ + +{ + + a += b - 1L; + + return a - (a % b); + +} + + + + + +/* On normal machines we can apply MEMCOPY() and MEMZERO() to sample arrays + + * and coefficient-block arrays. This won't work on 80x86 because the arrays + + * are FAR and we're assuming a small-pointer memory model. However, some + + * DOS compilers provide far-pointer versions of memcpy() and memset() even + + * in the small-model libraries. These will be used if USE_FMEM is defined. + + * Otherwise, the routines below do it the hard way. (The performance cost + + * is not all that great, because these routines aren't very heavily used.) + + */ + + + +#ifndef NEED_FAR_POINTERS /* normal case, same as regular macros */ + +#define FMEMCOPY(dest,src,size) MEMCOPY(dest,src,size) + +#define FMEMZERO(target,size) MEMZERO(target,size) + +#else /* 80x86 case, define if we can */ + +#ifdef USE_FMEM + +#define FMEMCOPY(dest,src,size) _fmemcpy((void FAR *)(dest), (const void FAR *)(src), (size_t)(size)) + +#define FMEMZERO(target,size) _fmemset((void FAR *)(target), 0, (size_t)(size)) + +#endif + +#endif + + + + + +GLOBAL void + +jcopy_sample_rows (JSAMPARRAY input_array, int source_row, + + JSAMPARRAY output_array, int dest_row, + + int num_rows, JDIMENSION num_cols) + +/* Copy some rows of samples from one place to another. + + * num_rows rows are copied from input_array[source_row++] + + * to output_array[dest_row++]; these areas may overlap for duplication. + + * The source and destination arrays must be at least as wide as num_cols. + + */ + +{ + + register JSAMPROW inptr, outptr; + +#ifdef FMEMCOPY + + register size_t count = (size_t) (num_cols * SIZEOF(JSAMPLE)); + +#else + + register JDIMENSION count; + +#endif + + register int row; + + + + input_array += source_row; + + output_array += dest_row; + + + + for (row = num_rows; row > 0; row--) { + + inptr = *input_array++; + + outptr = *output_array++; + +#ifdef FMEMCOPY + + FMEMCOPY(outptr, inptr, count); + +#else + + for (count = num_cols; count > 0; count--) + + *outptr++ = *inptr++; /* needn't bother with GETJSAMPLE() here */ + +#endif + + } + +} + + + + + +GLOBAL void + +jcopy_block_row (JBLOCKROW input_row, JBLOCKROW output_row, + + JDIMENSION num_blocks) + +/* Copy a row of coefficient blocks from one place to another. */ + +{ + +#ifdef FMEMCOPY + + FMEMCOPY(output_row, input_row, num_blocks * (DCTSIZE2 * SIZEOF(JCOEF))); + +#else + + register JCOEFPTR inptr, outptr; + + register long count; + + + + inptr = (JCOEFPTR) input_row; + + outptr = (JCOEFPTR) output_row; + + for (count = (long) num_blocks * DCTSIZE2; count > 0; count--) { + + *outptr++ = *inptr++; + + } + +#endif + +} + + + + + +GLOBAL void + +jzero_far (void FAR * target, size_t bytestozero) + +/* Zero out a chunk of FAR memory. */ + +/* This might be sample-array data, block-array data, or alloc_large data. */ + +{ + +#ifdef FMEMZERO + + FMEMZERO(target, bytestozero); + +#else + + register char FAR * ptr = (char FAR *) target; + + register size_t count; + + + + for (count = bytestozero; count > 0; count--) { + + *ptr++ = 0; + + } + +#endif + +} + diff --git a/tools/urt/libs/jpeg6/jversion.h b/tools/urt/libs/jpeg6/jversion.h new file mode 100644 index 00000000..784a088c --- /dev/null +++ b/tools/urt/libs/jpeg6/jversion.h @@ -0,0 +1,28 @@ +/* + + * jversion.h + + * + + * Copyright (C) 1991-1995, Thomas G. Lane. + + * This file is part of the Independent JPEG Group's software. + + * For conditions of distribution and use, see the accompanying README file. + + * + + * This file contains software version identification. + + */ + + + + + +#define JVERSION "6 2-Aug-95" + + + +#define JCOPYRIGHT "Copyright (C) 1995, Thomas G. Lane" + diff --git a/tools/urt/libs/jpeglib.h b/tools/urt/libs/jpeglib.h new file mode 100644 index 00000000..fda176bb --- /dev/null +++ b/tools/urt/libs/jpeglib.h @@ -0,0 +1,1126 @@ +/* +This code is based on source provided under the terms of the Id Software +LIMITED USE SOFTWARE LICENSE AGREEMENT, a copy of which is included with the +GtkRadiant sources (see LICENSE_ID). If you did not receive a copy of +LICENSE_ID, please contact Id Software immediately at info@idsoftware.com. + +All changes and additions to the original source which have been developed by +other contributors (see CONTRIBUTORS) are provided under the terms of the +license the contributors choose (see LICENSE), to the extent permitted by the +LICENSE_ID. If you did not receive a copy of the contributor license, +please contact the GtkRadiant maintainers at info@gtkradiant.com immediately. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * jpeglib.h + * + * Copyright (C) 1991-1995, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file defines the application interface for the JPEG library. + * Most applications using the library need only include this file, + * and perhaps jerror.h if they want to know the exact error codes. + */ + +#ifndef JPEGLIB_H +#define JPEGLIB_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +// LZ: linux stuff +#if defined (__linux__) || defined (__APPLE__) + +#include +#include + +#ifndef boolean +#ifdef __cplusplus +#define boolean bool +#else +typedef int boolean; +#endif +#endif + +#endif + +#ifdef __MACOS__ + +// JDC: stuff to make mac version compile +#define boolean qboolean +#define register +#define INT32 int + +#endif + +// rad additions +// 11.29.99 + +//#include "cmdlib.h" +#ifdef _WIN32 +#include "windows.h" +#include "stdio.h" +#endif + +#ifndef INT32 +#define INT32 int +#endif + +// TTimo: if LoadJPGBuff returns -1, *pic is the error message +extern int LoadJPGBuff(unsigned char *fbuffer, int bufsize, unsigned char **pic, int *width, int *height ); +// rad end + + +/* + * First we include the configuration files that record how this + * installation of the JPEG library is set up. jconfig.h can be + * generated automatically for many systems. jmorecfg.h contains + * manual configuration options that most people need not worry about. + */ + +#ifndef JCONFIG_INCLUDED /* in case jinclude.h already did */ +#include "jpeg6/jconfig.h" /* widely used configuration options */ +#endif +#include "jpeg6/jmorecfg.h" /* seldom changed options */ + + +/* Version ID for the JPEG library. + * Might be useful for tests like "#if JPEG_LIB_VERSION >= 60". + */ + +#define JPEG_LIB_VERSION 60 /* Version 6 */ + + +/* Various constants determining the sizes of things. + * All of these are specified by the JPEG standard, so don't change them + * if you want to be compatible. + */ + +#define DCTSIZE 8 /* The basic DCT block is 8x8 samples */ +#define DCTSIZE2 64 /* DCTSIZE squared; # of elements in a block */ +#define NUM_QUANT_TBLS 4 /* Quantization tables are numbered 0..3 */ +#define NUM_HUFF_TBLS 4 /* Huffman tables are numbered 0..3 */ +#define NUM_ARITH_TBLS 16 /* Arith-coding tables are numbered 0..15 */ +#define MAX_COMPS_IN_SCAN 4 /* JPEG limit on # of components in one scan */ +#define MAX_SAMP_FACTOR 4 /* JPEG limit on sampling factors */ +/* Unfortunately, some bozo at Adobe saw no reason to be bound by the standard; + * the PostScript DCT filter can emit files with many more than 10 blocks/MCU. + * If you happen to run across such a file, you can up D_MAX_BLOCKS_IN_MCU + * to handle it. We even let you do this from the jconfig.h file. However, + * we strongly discourage changing C_MAX_BLOCKS_IN_MCU; just because Adobe + * sometimes emits noncompliant files doesn't mean you should too. + */ +#define C_MAX_BLOCKS_IN_MCU 10 /* compressor's limit on blocks per MCU */ +#ifndef D_MAX_BLOCKS_IN_MCU +#define D_MAX_BLOCKS_IN_MCU 10 /* decompressor's limit on blocks per MCU */ +#endif + + +/* This macro is used to declare a "method", that is, a function pointer. + * We want to supply prototype parameters if the compiler can cope. + * Note that the arglist parameter must be parenthesized! + */ + +#ifdef HAVE_PROTOTYPES +#define JMETHOD(type,methodname,arglist) type (*methodname) arglist +#else +#define JMETHOD(type,methodname,arglist) type (*methodname) () +#endif + + +/* Data structures for images (arrays of samples and of DCT coefficients). + * On 80x86 machines, the image arrays are too big for near pointers, + * but the pointer arrays can fit in near memory. + */ + +typedef JSAMPLE FAR *JSAMPROW; /* ptr to one image row of pixel samples. */ +typedef JSAMPROW *JSAMPARRAY; /* ptr to some rows (a 2-D sample array) */ +typedef JSAMPARRAY *JSAMPIMAGE; /* a 3-D sample array: top index is color */ + +typedef JCOEF JBLOCK[DCTSIZE2]; /* one block of coefficients */ +typedef JBLOCK FAR *JBLOCKROW; /* pointer to one row of coefficient blocks */ +typedef JBLOCKROW *JBLOCKARRAY; /* a 2-D array of coefficient blocks */ +typedef JBLOCKARRAY *JBLOCKIMAGE; /* a 3-D array of coefficient blocks */ + +typedef JCOEF FAR *JCOEFPTR; /* useful in a couple of places */ + + +/* Types for JPEG compression parameters and working tables. */ + + +/* DCT coefficient quantization tables. */ + +typedef struct { + /* This field directly represents the contents of a JPEG DQT marker. + * Note: the values are always given in zigzag order. + */ + UINT16 quantval[DCTSIZE2]; /* quantization step for each coefficient */ + /* This field is used only during compression. It's initialized FALSE when + * the table is created, and set TRUE when it's been output to the file. + * You could suppress output of a table by setting this to TRUE. + * (See jpeg_suppress_tables for an example.) + */ + boolean sent_table; /* TRUE when table has been output */ +} JQUANT_TBL; + + +/* Huffman coding tables. */ + +typedef struct { + /* These two fields directly represent the contents of a JPEG DHT marker */ + UINT8 bits[17]; /* bits[k] = # of symbols with codes of */ + /* length k bits; bits[0] is unused */ + UINT8 huffval[256]; /* The symbols, in order of incr code length */ + /* This field is used only during compression. It's initialized FALSE when + * the table is created, and set TRUE when it's been output to the file. + * You could suppress output of a table by setting this to TRUE. + * (See jpeg_suppress_tables for an example.) + */ + boolean sent_table; /* TRUE when table has been output */ +} JHUFF_TBL; + + +/* Basic info about one component (color channel). */ + +typedef struct { + /* These values are fixed over the whole image. */ + /* For compression, they must be supplied by parameter setup; */ + /* for decompression, they are read from the SOF marker. */ + int component_id; /* identifier for this component (0..255) */ + int component_index; /* its index in SOF or cinfo->comp_info[] */ + int h_samp_factor; /* horizontal sampling factor (1..4) */ + int v_samp_factor; /* vertical sampling factor (1..4) */ + int quant_tbl_no; /* quantization table selector (0..3) */ + /* These values may vary between scans. */ + /* For compression, they must be supplied by parameter setup; */ + /* for decompression, they are read from the SOS marker. */ + /* The decompressor output side may not use these variables. */ + int dc_tbl_no; /* DC entropy table selector (0..3) */ + int ac_tbl_no; /* AC entropy table selector (0..3) */ + + /* Remaining fields should be treated as private by applications. */ + + /* These values are computed during compression or decompression startup: */ + /* Component's size in DCT blocks. + * Any dummy blocks added to complete an MCU are not counted; therefore + * these values do not depend on whether a scan is interleaved or not. + */ + JDIMENSION width_in_blocks; + JDIMENSION height_in_blocks; + /* Size of a DCT block in samples. Always DCTSIZE for compression. + * For decompression this is the size of the output from one DCT block, + * reflecting any scaling we choose to apply during the IDCT step. + * Values of 1,2,4,8 are likely to be supported. Note that different + * components may receive different IDCT scalings. + */ + int DCT_scaled_size; + /* The downsampled dimensions are the component's actual, unpadded number + * of samples at the main buffer (preprocessing/compression interface), thus + * downsampled_width = ceil(image_width * Hi/Hmax) + * and similarly for height. For decompression, IDCT scaling is included, so + * downsampled_width = ceil(image_width * Hi/Hmax * DCT_scaled_size/DCTSIZE) + */ + JDIMENSION downsampled_width; /* actual width in samples */ + JDIMENSION downsampled_height; /* actual height in samples */ + /* This flag is used only for decompression. In cases where some of the + * components will be ignored (eg grayscale output from YCbCr image), + * we can skip most computations for the unused components. + */ + boolean component_needed; /* do we need the value of this component? */ + + /* These values are computed before starting a scan of the component. */ + /* The decompressor output side may not use these variables. */ + int MCU_width; /* number of blocks per MCU, horizontally */ + int MCU_height; /* number of blocks per MCU, vertically */ + int MCU_blocks; /* MCU_width * MCU_height */ + int MCU_sample_width; /* MCU width in samples, MCU_width*DCT_scaled_size */ + int last_col_width; /* # of non-dummy blocks across in last MCU */ + int last_row_height; /* # of non-dummy blocks down in last MCU */ + + /* Saved quantization table for component; NULL if none yet saved. + * See jdinput.c comments about the need for this information. + * This field is not currently used by the compressor. + */ + JQUANT_TBL * quant_table; + + /* Private per-component storage for DCT or IDCT subsystem. */ + void * dct_table; +} jpeg_component_info; + + +/* The script for encoding a multiple-scan file is an array of these: */ + +typedef struct { + int comps_in_scan; /* number of components encoded in this scan */ + int component_index[MAX_COMPS_IN_SCAN]; /* their SOF/comp_info[] indexes */ + int Ss, Se; /* progressive JPEG spectral selection parms */ + int Ah, Al; /* progressive JPEG successive approx. parms */ +} jpeg_scan_info; + + +/* Known color spaces. */ + +typedef enum { + JCS_UNKNOWN, /* error/unspecified */ + JCS_GRAYSCALE, /* monochrome */ + JCS_RGB, /* red/green/blue */ + JCS_YCbCr, /* Y/Cb/Cr (also known as YUV) */ + JCS_CMYK, /* C/M/Y/K */ + JCS_YCCK /* Y/Cb/Cr/K */ +} J_COLOR_SPACE; + +/* DCT/IDCT algorithm options. */ + +typedef enum { + JDCT_ISLOW, /* slow but accurate integer algorithm */ + JDCT_IFAST, /* faster, less accurate integer method */ + JDCT_FLOAT /* floating-point: accurate, fast on fast HW */ +} J_DCT_METHOD; + +#ifndef JDCT_DEFAULT /* may be overridden in jconfig.h */ +#define JDCT_DEFAULT JDCT_ISLOW +#endif +#ifndef JDCT_FASTEST /* may be overridden in jconfig.h */ +#define JDCT_FASTEST JDCT_IFAST +#endif + +/* Dithering options for decompression. */ + +typedef enum { + JDITHER_NONE, /* no dithering */ + JDITHER_ORDERED, /* simple ordered dither */ + JDITHER_FS /* Floyd-Steinberg error diffusion dither */ +} J_DITHER_MODE; + + +/* Common fields between JPEG compression and decompression master structs. */ + +#define jpeg_common_fields \ + struct jpeg_error_mgr * err; /* Error handler module */\ + struct jpeg_memory_mgr * mem; /* Memory manager module */\ + struct jpeg_progress_mgr * progress; /* Progress monitor, or NULL if none */\ + boolean is_decompressor; /* so common code can tell which is which */\ + int global_state /* for checking call sequence validity */ + +/* Routines that are to be used by both halves of the library are declared + * to receive a pointer to this structure. There are no actual instances of + * jpeg_common_struct, only of jpeg_compress_struct and jpeg_decompress_struct. + */ +struct jpeg_common_struct { + jpeg_common_fields; /* Fields common to both master struct types */ + /* Additional fields follow in an actual jpeg_compress_struct or + * jpeg_decompress_struct. All three structs must agree on these + * initial fields! (This would be a lot cleaner in C++.) + */ +}; + +typedef struct jpeg_common_struct * j_common_ptr; +typedef struct jpeg_compress_struct * j_compress_ptr; +typedef struct jpeg_decompress_struct * j_decompress_ptr; + + +/* Master record for a compression instance */ + +struct jpeg_compress_struct { + jpeg_common_fields; /* Fields shared with jpeg_decompress_struct */ + + /* Destination for compressed data */ + struct jpeg_destination_mgr * dest; + + /* Description of source image --- these fields must be filled in by + * outer application before starting compression. in_color_space must + * be correct before you can even call jpeg_set_defaults(). + */ + + JDIMENSION image_width; /* input image width */ + JDIMENSION image_height; /* input image height */ + int input_components; /* # of color components in input image */ + J_COLOR_SPACE in_color_space; /* colorspace of input image */ + + double input_gamma; /* image gamma of input image */ + + /* Compression parameters --- these fields must be set before calling + * jpeg_start_compress(). We recommend calling jpeg_set_defaults() to + * initialize everything to reasonable defaults, then changing anything + * the application specifically wants to change. That way you won't get + * burnt when new parameters are added. Also note that there are several + * helper routines to simplify changing parameters. + */ + + int data_precision; /* bits of precision in image data */ + + int num_components; /* # of color components in JPEG image */ + J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */ + + jpeg_component_info * comp_info; + /* comp_info[i] describes component that appears i'th in SOF */ + + JQUANT_TBL * quant_tbl_ptrs[NUM_QUANT_TBLS]; + /* ptrs to coefficient quantization tables, or NULL if not defined */ + + JHUFF_TBL * dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; + JHUFF_TBL * ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; + /* ptrs to Huffman coding tables, or NULL if not defined */ + + UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ + UINT8 arith_dc_U[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */ + UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */ + + int num_scans; /* # of entries in scan_info array */ + const jpeg_scan_info * scan_info; /* script for multi-scan file, or NULL */ + /* The default value of scan_info is NULL, which causes a single-scan + * sequential JPEG file to be emitted. To create a multi-scan file, + * set num_scans and scan_info to point to an array of scan definitions. + */ + + boolean raw_data_in; /* TRUE=caller supplies downsampled data */ + boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ + boolean optimize_coding; /* TRUE=optimize entropy encoding parms */ + boolean CCIR601_sampling; /* TRUE=first samples are cosited */ + int smoothing_factor; /* 1..100, or 0 for no input smoothing */ + J_DCT_METHOD dct_method; /* DCT algorithm selector */ + + /* The restart interval can be specified in absolute MCUs by setting + * restart_interval, or in MCU rows by setting restart_in_rows + * (in which case the correct restart_interval will be figured + * for each scan). + */ + unsigned int restart_interval; /* MCUs per restart, or 0 for no restart */ + int restart_in_rows; /* if > 0, MCU rows per restart interval */ + + /* Parameters controlling emission of special markers. */ + + boolean write_JFIF_header; /* should a JFIF marker be written? */ + /* These three values are not used by the JPEG code, merely copied */ + /* into the JFIF APP0 marker. density_unit can be 0 for unknown, */ + /* 1 for dots/inch, or 2 for dots/cm. Note that the pixel aspect */ + /* ratio is defined by X_density/Y_density even when density_unit=0. */ + UINT8 density_unit; /* JFIF code for pixel size units */ + UINT16 X_density; /* Horizontal pixel density */ + UINT16 Y_density; /* Vertical pixel density */ + boolean write_Adobe_marker; /* should an Adobe marker be written? */ + + /* State variable: index of next scanline to be written to + * jpeg_write_scanlines(). Application may use this to control its + * processing loop, e.g., "while (next_scanline < image_height)". + */ + + JDIMENSION next_scanline; /* 0 .. image_height-1 */ + + /* Remaining fields are known throughout compressor, but generally + * should not be touched by a surrounding application. + */ + + /* + * These fields are computed during compression startup + */ + boolean progressive_mode; /* TRUE if scan script uses progressive mode */ + int max_h_samp_factor; /* largest h_samp_factor */ + int max_v_samp_factor; /* largest v_samp_factor */ + + JDIMENSION total_iMCU_rows; /* # of iMCU rows to be input to coef ctlr */ + /* The coefficient controller receives data in units of MCU rows as defined + * for fully interleaved scans (whether the JPEG file is interleaved or not). + * There are v_samp_factor * DCTSIZE sample rows of each component in an + * "iMCU" (interleaved MCU) row. + */ + + /* + * These fields are valid during any one scan. + * They describe the components and MCUs actually appearing in the scan. + */ + int comps_in_scan; /* # of JPEG components in this scan */ + jpeg_component_info * cur_comp_info[MAX_COMPS_IN_SCAN]; + /* *cur_comp_info[i] describes component that appears i'th in SOS */ + + JDIMENSION MCUs_per_row; /* # of MCUs across the image */ + JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ + + int blocks_in_MCU; /* # of DCT blocks per MCU */ + int MCU_membership[C_MAX_BLOCKS_IN_MCU]; + /* MCU_membership[i] is index in cur_comp_info of component owning */ + /* i'th block in an MCU */ + + int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + + /* + * Links to compression subobjects (methods and private variables of modules) + */ + struct jpeg_comp_master * master; + struct jpeg_c_main_controller * main; + struct jpeg_c_prep_controller * prep; + struct jpeg_c_coef_controller * coef; + struct jpeg_marker_writer * marker; + struct jpeg_color_converter * cconvert; + struct jpeg_downsampler * downsample; + struct jpeg_forward_dct * fdct; + struct jpeg_entropy_encoder * entropy; +}; + + +/* Master record for a decompression instance */ + +struct jpeg_decompress_struct { + jpeg_common_fields; /* Fields shared with jpeg_compress_struct */ + + /* Source of compressed data */ + struct jpeg_source_mgr * src; + + /* Basic description of image --- filled in by jpeg_read_header(). */ + /* Application may inspect these values to decide how to process image. */ + + JDIMENSION image_width; /* nominal image width (from SOF marker) */ + JDIMENSION image_height; /* nominal image height */ + int num_components; /* # of color components in JPEG image */ + J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */ + + /* Decompression processing parameters --- these fields must be set before + * calling jpeg_start_decompress(). Note that jpeg_read_header() initializes + * them to default values. + */ + + J_COLOR_SPACE out_color_space; /* colorspace for output */ + + unsigned int scale_num, scale_denom; /* fraction by which to scale image */ + + double output_gamma; /* image gamma wanted in output */ + + boolean buffered_image; /* TRUE=multiple output passes */ + boolean raw_data_out; /* TRUE=downsampled data wanted */ + + J_DCT_METHOD dct_method; /* IDCT algorithm selector */ + boolean do_fancy_upsampling; /* TRUE=apply fancy upsampling */ + boolean do_block_smoothing; /* TRUE=apply interblock smoothing */ + + boolean quantize_colors; /* TRUE=colormapped output wanted */ + /* the following are ignored if not quantize_colors: */ + J_DITHER_MODE dither_mode; /* type of color dithering to use */ + boolean two_pass_quantize; /* TRUE=use two-pass color quantization */ + int desired_number_of_colors; /* max # colors to use in created colormap */ + /* these are significant only in buffered-image mode: */ + boolean enable_1pass_quant; /* enable future use of 1-pass quantizer */ + boolean enable_external_quant;/* enable future use of external colormap */ + boolean enable_2pass_quant; /* enable future use of 2-pass quantizer */ + + /* Description of actual output image that will be returned to application. + * These fields are computed by jpeg_start_decompress(). + * You can also use jpeg_calc_output_dimensions() to determine these values + * in advance of calling jpeg_start_decompress(). + */ + + JDIMENSION output_width; /* scaled image width */ + JDIMENSION output_height; /* scaled image height */ + int out_color_components; /* # of color components in out_color_space */ + int output_components; /* # of color components returned */ + /* output_components is 1 (a colormap index) when quantizing colors; + * otherwise it equals out_color_components. + */ + int rec_outbuf_height; /* min recommended height of scanline buffer */ + /* If the buffer passed to jpeg_read_scanlines() is less than this many rows + * high, space and time will be wasted due to unnecessary data copying. + * Usually rec_outbuf_height will be 1 or 2, at most 4. + */ + + /* When quantizing colors, the output colormap is described by these fields. + * The application can supply a colormap by setting colormap non-NULL before + * calling jpeg_start_decompress; otherwise a colormap is created during + * jpeg_start_decompress or jpeg_start_output. + * The map has out_color_components rows and actual_number_of_colors columns. + */ + int actual_number_of_colors; /* number of entries in use */ + JSAMPARRAY colormap; /* The color map as a 2-D pixel array */ + + /* State variables: these variables indicate the progress of decompression. + * The application may examine these but must not modify them. + */ + + /* Row index of next scanline to be read from jpeg_read_scanlines(). + * Application may use this to control its processing loop, e.g., + * "while (output_scanline < output_height)". + */ + JDIMENSION output_scanline; /* 0 .. output_height-1 */ + + /* Current input scan number and number of iMCU rows completed in scan. + * These indicate the progress of the decompressor input side. + */ + int input_scan_number; /* Number of SOS markers seen so far */ + JDIMENSION input_iMCU_row; /* Number of iMCU rows completed */ + + /* The "output scan number" is the notional scan being displayed by the + * output side. The decompressor will not allow output scan/row number + * to get ahead of input scan/row, but it can fall arbitrarily far behind. + */ + int output_scan_number; /* Nominal scan number being displayed */ + JDIMENSION output_iMCU_row; /* Number of iMCU rows read */ + + /* Current progression status. coef_bits[c][i] indicates the precision + * with which component c's DCT coefficient i (in zigzag order) is known. + * It is -1 when no data has yet been received, otherwise it is the point + * transform (shift) value for the most recent scan of the coefficient + * (thus, 0 at completion of the progression). + * This pointer is NULL when reading a non-progressive file. + */ + int (*coef_bits)[DCTSIZE2]; /* -1 or current Al value for each coef */ + + /* Internal JPEG parameters --- the application usually need not look at + * these fields. Note that the decompressor output side may not use + * any parameters that can change between scans. + */ + + /* Quantization and Huffman tables are carried forward across input + * datastreams when processing abbreviated JPEG datastreams. + */ + + JQUANT_TBL * quant_tbl_ptrs[NUM_QUANT_TBLS]; + /* ptrs to coefficient quantization tables, or NULL if not defined */ + + JHUFF_TBL * dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; + JHUFF_TBL * ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; + /* ptrs to Huffman coding tables, or NULL if not defined */ + + /* These parameters are never carried across datastreams, since they + * are given in SOF/SOS markers or defined to be reset by SOI. + */ + + int data_precision; /* bits of precision in image data */ + + jpeg_component_info * comp_info; + /* comp_info[i] describes component that appears i'th in SOF */ + + boolean progressive_mode; /* TRUE if SOFn specifies progressive mode */ + boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ + + UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ + UINT8 arith_dc_U[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */ + UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */ + + unsigned int restart_interval; /* MCUs per restart interval, or 0 for no restart */ + + /* These fields record data obtained from optional markers recognized by + * the JPEG library. + */ + boolean saw_JFIF_marker; /* TRUE iff a JFIF APP0 marker was found */ + /* Data copied from JFIF marker: */ + UINT8 density_unit; /* JFIF code for pixel size units */ + UINT16 X_density; /* Horizontal pixel density */ + UINT16 Y_density; /* Vertical pixel density */ + boolean saw_Adobe_marker; /* TRUE iff an Adobe APP14 marker was found */ + UINT8 Adobe_transform; /* Color transform code from Adobe marker */ + + boolean CCIR601_sampling; /* TRUE=first samples are cosited */ + + /* Remaining fields are known throughout decompressor, but generally + * should not be touched by a surrounding application. + */ + + /* + * These fields are computed during decompression startup + */ + int max_h_samp_factor; /* largest h_samp_factor */ + int max_v_samp_factor; /* largest v_samp_factor */ + + int min_DCT_scaled_size; /* smallest DCT_scaled_size of any component */ + + JDIMENSION total_iMCU_rows; /* # of iMCU rows in image */ + /* The coefficient controller's input and output progress is measured in + * units of "iMCU" (interleaved MCU) rows. These are the same as MCU rows + * in fully interleaved JPEG scans, but are used whether the scan is + * interleaved or not. We define an iMCU row as v_samp_factor DCT block + * rows of each component. Therefore, the IDCT output contains + * v_samp_factor*DCT_scaled_size sample rows of a component per iMCU row. + */ + + JSAMPLE * sample_range_limit; /* table for fast range-limiting */ + + /* + * These fields are valid during any one scan. + * They describe the components and MCUs actually appearing in the scan. + * Note that the decompressor output side must not use these fields. + */ + int comps_in_scan; /* # of JPEG components in this scan */ + jpeg_component_info * cur_comp_info[MAX_COMPS_IN_SCAN]; + /* *cur_comp_info[i] describes component that appears i'th in SOS */ + + JDIMENSION MCUs_per_row; /* # of MCUs across the image */ + JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ + + int blocks_in_MCU; /* # of DCT blocks per MCU */ + int MCU_membership[D_MAX_BLOCKS_IN_MCU]; + /* MCU_membership[i] is index in cur_comp_info of component owning */ + /* i'th block in an MCU */ + + int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + + /* This field is shared between entropy decoder and marker parser. + * It is either zero or the code of a JPEG marker that has been + * read from the data source, but has not yet been processed. + */ + int unread_marker; + + /* + * Links to decompression subobjects (methods, private variables of modules) + */ + struct jpeg_decomp_master * master; + struct jpeg_d_main_controller * main; + struct jpeg_d_coef_controller * coef; + struct jpeg_d_post_controller * post; + struct jpeg_input_controller * inputctl; + struct jpeg_marker_reader * marker; + struct jpeg_entropy_decoder * entropy; + struct jpeg_inverse_dct * idct; + struct jpeg_upsampler * upsample; + struct jpeg_color_deconverter * cconvert; + struct jpeg_color_quantizer * cquantize; +}; + + +/* "Object" declarations for JPEG modules that may be supplied or called + * directly by the surrounding application. + * As with all objects in the JPEG library, these structs only define the + * publicly visible methods and state variables of a module. Additional + * private fields may exist after the public ones. + */ + + +/* Error handler object */ + +struct jpeg_error_mgr { + /* Error exit handler: does not return to caller */ + JMETHOD(void, error_exit, (j_common_ptr cinfo)); + /* Conditionally emit a trace or warning message */ + JMETHOD(void, emit_message, (j_common_ptr cinfo, int msg_level)); + /* Routine that actually outputs a trace or error message */ + JMETHOD(void, output_message, (j_common_ptr cinfo)); + /* Format a message string for the most recent JPEG error or message */ + JMETHOD(void, format_message, (j_common_ptr cinfo, char * buffer)); +#define JMSG_LENGTH_MAX 200 /* recommended size of format_message buffer */ + /* Reset error state variables at start of a new image */ + JMETHOD(void, reset_error_mgr, (j_common_ptr cinfo)); + + /* The message ID code and any parameters are saved here. + * A message can have one string parameter or up to 8 int parameters. + */ + int msg_code; +#define JMSG_STR_PARM_MAX 80 + union { + int i[8]; + char s[JMSG_STR_PARM_MAX]; + } msg_parm; + + /* Standard state variables for error facility */ + + int trace_level; /* max msg_level that will be displayed */ + + /* For recoverable corrupt-data errors, we emit a warning message, + * but keep going unless emit_message chooses to abort. emit_message + * should count warnings in num_warnings. The surrounding application + * can check for bad data by seeing if num_warnings is nonzero at the + * end of processing. + */ + long num_warnings; /* number of corrupt-data warnings */ + + /* These fields point to the table(s) of error message strings. + * An application can change the table pointer to switch to a different + * message list (typically, to change the language in which errors are + * reported). Some applications may wish to add additional error codes + * that will be handled by the JPEG library error mechanism; the second + * table pointer is used for this purpose. + * + * First table includes all errors generated by JPEG library itself. + * Error code 0 is reserved for a "no such error string" message. + */ + const char * const * jpeg_message_table; /* Library errors */ + int last_jpeg_message; /* Table contains strings 0..last_jpeg_message */ + /* Second table can be added by application (see cjpeg/djpeg for example). + * It contains strings numbered first_addon_message..last_addon_message. + */ + const char * const * addon_message_table; /* Non-library errors */ + int first_addon_message; /* code for first string in addon table */ + int last_addon_message; /* code for last string in addon table */ +}; + + +/* Progress monitor object */ + +struct jpeg_progress_mgr { + JMETHOD(void, progress_monitor, (j_common_ptr cinfo)); + + long pass_counter; /* work units completed in this pass */ + long pass_limit; /* total number of work units in this pass */ + int completed_passes; /* passes completed so far */ + int total_passes; /* total number of passes expected */ +}; + + +/* Data destination object for compression */ + +struct jpeg_destination_mgr { + JOCTET * next_output_byte; /* => next byte to write in buffer */ + size_t free_in_buffer; /* # of byte spaces remaining in buffer */ + + JMETHOD(void, init_destination, (j_compress_ptr cinfo)); + JMETHOD(boolean, empty_output_buffer, (j_compress_ptr cinfo)); + JMETHOD(void, term_destination, (j_compress_ptr cinfo)); +}; + + +/* Data source object for decompression */ + +struct jpeg_source_mgr { + const JOCTET * next_input_byte; /* => next byte to read from buffer */ + size_t bytes_in_buffer; /* # of bytes remaining in buffer */ + + JMETHOD(void, init_source, (j_decompress_ptr cinfo)); + JMETHOD(boolean, fill_input_buffer, (j_decompress_ptr cinfo)); + JMETHOD(void, skip_input_data, (j_decompress_ptr cinfo, long num_bytes)); + JMETHOD(boolean, resync_to_restart, (j_decompress_ptr cinfo, int desired)); + JMETHOD(void, term_source, (j_decompress_ptr cinfo)); +}; + + +/* Memory manager object. + * Allocates "small" objects (a few K total), "large" objects (tens of K), + * and "really big" objects (virtual arrays with backing store if needed). + * The memory manager does not allow individual objects to be freed; rather, + * each created object is assigned to a pool, and whole pools can be freed + * at once. This is faster and more convenient than remembering exactly what + * to free, especially where malloc()/free() are not too speedy. + * NB: alloc routines never return NULL. They exit to error_exit if not + * successful. + */ + +#define JPOOL_PERMANENT 0 /* lasts until master record is destroyed */ +#define JPOOL_IMAGE 1 /* lasts until done with image/datastream */ +#define JPOOL_NUMPOOLS 2 + +typedef struct jvirt_sarray_control * jvirt_sarray_ptr; +typedef struct jvirt_barray_control * jvirt_barray_ptr; + + +struct jpeg_memory_mgr { + /* Method pointers */ + JMETHOD(void *, alloc_small, (j_common_ptr cinfo, int pool_id, + size_t sizeofobject)); + JMETHOD(void FAR *, alloc_large, (j_common_ptr cinfo, int pool_id, + size_t sizeofobject)); + JMETHOD(JSAMPARRAY, alloc_sarray, (j_common_ptr cinfo, int pool_id, + JDIMENSION samplesperrow, + JDIMENSION numrows)); + JMETHOD(JBLOCKARRAY, alloc_barray, (j_common_ptr cinfo, int pool_id, + JDIMENSION blocksperrow, + JDIMENSION numrows)); + JMETHOD(jvirt_sarray_ptr, request_virt_sarray, (j_common_ptr cinfo, + int pool_id, + boolean pre_zero, + JDIMENSION samplesperrow, + JDIMENSION numrows, + JDIMENSION maxaccess)); + JMETHOD(jvirt_barray_ptr, request_virt_barray, (j_common_ptr cinfo, + int pool_id, + boolean pre_zero, + JDIMENSION blocksperrow, + JDIMENSION numrows, + JDIMENSION maxaccess)); + JMETHOD(void, realize_virt_arrays, (j_common_ptr cinfo)); + JMETHOD(JSAMPARRAY, access_virt_sarray, (j_common_ptr cinfo, + jvirt_sarray_ptr ptr, + JDIMENSION start_row, + JDIMENSION num_rows, + boolean writable)); + JMETHOD(JBLOCKARRAY, access_virt_barray, (j_common_ptr cinfo, + jvirt_barray_ptr ptr, + JDIMENSION start_row, + JDIMENSION num_rows, + boolean writable)); + JMETHOD(void, free_pool, (j_common_ptr cinfo, int pool_id)); + JMETHOD(void, self_destruct, (j_common_ptr cinfo)); + + /* Limit on memory allocation for this JPEG object. (Note that this is + * merely advisory, not a guaranteed maximum; it only affects the space + * used for virtual-array buffers.) May be changed by outer application + * after creating the JPEG object. + */ + long max_memory_to_use; +}; + + +/* Routine signature for application-supplied marker processing methods. + * Need not pass marker code since it is stored in cinfo->unread_marker. + */ +typedef JMETHOD(boolean, jpeg_marker_parser_method, (j_decompress_ptr cinfo)); + + +/* Declarations for routines called by application. + * The JPP macro hides prototype parameters from compilers that can't cope. + * Note JPP requires double parentheses. + */ + +#ifdef HAVE_PROTOTYPES +#define JPP(arglist) arglist +#else +#define JPP(arglist) () +#endif + + +/* Short forms of external names for systems with brain-damaged linkers. + * We shorten external names to be unique in the first six letters, which + * is good enough for all known systems. + * (If your compiler itself needs names to be unique in less than 15 + * characters, you are out of luck. Get a better compiler.) + */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_std_error jStdError +#define jpeg_create_compress jCreaCompress +#define jpeg_create_decompress jCreaDecompress +#define jpeg_destroy_compress jDestCompress +#define jpeg_destroy_decompress jDestDecompress +#define jpeg_stdio_dest jStdDest +#define jpeg_stdio_src jStdSrc +#define jpeg_set_defaults jSetDefaults +#define jpeg_set_colorspace jSetColorspace +#define jpeg_default_colorspace jDefColorspace +#define jpeg_set_quality jSetQuality +#define jpeg_set_linear_quality jSetLQuality +#define jpeg_add_quant_table jAddQuantTable +#define jpeg_quality_scaling jQualityScaling +#define jpeg_simple_progression jSimProgress +#define jpeg_suppress_tables jSuppressTables +#define jpeg_alloc_quant_table jAlcQTable +#define jpeg_alloc_huff_table jAlcHTable +#define jpeg_start_compress jStrtCompress +#define jpeg_write_scanlines jWrtScanlines +#define jpeg_finish_compress jFinCompress +#define jpeg_write_raw_data jWrtRawData +#define jpeg_write_marker jWrtMarker +#define jpeg_write_tables jWrtTables +#define jpeg_read_header jReadHeader +#define jpeg_start_decompress jStrtDecompress +#define jpeg_read_scanlines jReadScanlines +#define jpeg_finish_decompress jFinDecompress +#define jpeg_read_raw_data jReadRawData +#define jpeg_has_multiple_scans jHasMultScn +#define jpeg_start_output jStrtOutput +#define jpeg_finish_output jFinOutput +#define jpeg_input_complete jInComplete +#define jpeg_new_colormap jNewCMap +#define jpeg_consume_input jConsumeInput +#define jpeg_calc_output_dimensions jCalcDimensions +#define jpeg_set_marker_processor jSetMarker +#define jpeg_read_coefficients jReadCoefs +#define jpeg_write_coefficients jWrtCoefs +#define jpeg_copy_critical_parameters jCopyCrit +#define jpeg_abort_compress jAbrtCompress +#define jpeg_abort_decompress jAbrtDecompress +#define jpeg_abort jAbort +#define jpeg_destroy jDestroy +#define jpeg_resync_to_restart jResyncRestart +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + +/* Default error-management setup */ +EXTERN struct jpeg_error_mgr *jpeg_std_error JPP((struct jpeg_error_mgr *err)); + +/* Initialization and destruction of JPEG compression objects */ +/* NB: you must set up the error-manager BEFORE calling jpeg_create_xxx */ +EXTERN void jpeg_create_compress JPP((j_compress_ptr cinfo)); +EXTERN void jpeg_create_decompress JPP((j_decompress_ptr cinfo)); +EXTERN void jpeg_destroy_compress JPP((j_compress_ptr cinfo)); +EXTERN void jpeg_destroy_decompress JPP((j_decompress_ptr cinfo)); + +/* Standard data source and destination managers: stdio streams. */ +/* Caller is responsible for opening the file before and closing after. */ +EXTERN void jpeg_stdio_dest JPP((j_compress_ptr cinfo, FILE * outfile)); +EXTERN void jpeg_stdio_src JPP((j_decompress_ptr cinfo, unsigned char *infile, int bufsize)); + +/* Default parameter setup for compression */ +EXTERN void jpeg_set_defaults JPP((j_compress_ptr cinfo)); +/* Compression parameter setup aids */ +EXTERN void jpeg_set_colorspace JPP((j_compress_ptr cinfo, + J_COLOR_SPACE colorspace)); +EXTERN void jpeg_default_colorspace JPP((j_compress_ptr cinfo)); +EXTERN void jpeg_set_quality JPP((j_compress_ptr cinfo, int quality, + boolean force_baseline)); +EXTERN void jpeg_set_linear_quality JPP((j_compress_ptr cinfo, + int scale_factor, + boolean force_baseline)); +EXTERN void jpeg_add_quant_table JPP((j_compress_ptr cinfo, int which_tbl, + const unsigned int *basic_table, + int scale_factor, + boolean force_baseline)); +EXTERN int jpeg_quality_scaling JPP((int quality)); +EXTERN void jpeg_simple_progression JPP((j_compress_ptr cinfo)); +EXTERN void jpeg_suppress_tables JPP((j_compress_ptr cinfo, + boolean suppress)); +EXTERN JQUANT_TBL * jpeg_alloc_quant_table JPP((j_common_ptr cinfo)); +EXTERN JHUFF_TBL * jpeg_alloc_huff_table JPP((j_common_ptr cinfo)); + +/* Main entry points for compression */ +EXTERN void jpeg_start_compress JPP((j_compress_ptr cinfo, + boolean write_all_tables)); +EXTERN JDIMENSION jpeg_write_scanlines JPP((j_compress_ptr cinfo, + JSAMPARRAY scanlines, + JDIMENSION num_lines)); +EXTERN void jpeg_finish_compress JPP((j_compress_ptr cinfo)); + +/* Replaces jpeg_write_scanlines when writing raw downsampled data. */ +EXTERN JDIMENSION jpeg_write_raw_data JPP((j_compress_ptr cinfo, + JSAMPIMAGE data, + JDIMENSION num_lines)); + +/* Write a special marker. See libjpeg.doc concerning safe usage. */ +EXTERN void jpeg_write_marker JPP((j_compress_ptr cinfo, int marker, + const JOCTET *dataptr, unsigned int datalen)); + +/* Alternate compression function: just write an abbreviated table file */ +EXTERN void jpeg_write_tables JPP((j_compress_ptr cinfo)); + +/* Decompression startup: read start of JPEG datastream to see what's there */ +EXTERN int jpeg_read_header JPP((j_decompress_ptr cinfo, + boolean require_image)); +/* Return value is one of: */ +#define JPEG_SUSPENDED 0 /* Suspended due to lack of input data */ +#define JPEG_HEADER_OK 1 /* Found valid image datastream */ +#define JPEG_HEADER_TABLES_ONLY 2 /* Found valid table-specs-only datastream */ +/* If you pass require_image = TRUE (normal case), you need not check for + * a TABLES_ONLY return code; an abbreviated file will cause an error exit. + * JPEG_SUSPENDED is only possible if you use a data source module that can + * give a suspension return (the stdio source module doesn't). + */ + +/* Main entry points for decompression */ +EXTERN boolean jpeg_start_decompress JPP((j_decompress_ptr cinfo)); +EXTERN JDIMENSION jpeg_read_scanlines JPP((j_decompress_ptr cinfo, + JSAMPARRAY scanlines, + JDIMENSION max_lines)); +EXTERN boolean jpeg_finish_decompress JPP((j_decompress_ptr cinfo)); + +/* Replaces jpeg_read_scanlines when reading raw downsampled data. */ +EXTERN JDIMENSION jpeg_read_raw_data JPP((j_decompress_ptr cinfo, + JSAMPIMAGE data, + JDIMENSION max_lines)); + +/* Additional entry points for buffered-image mode. */ +EXTERN boolean jpeg_has_multiple_scans JPP((j_decompress_ptr cinfo)); +EXTERN boolean jpeg_start_output JPP((j_decompress_ptr cinfo, + int scan_number)); +EXTERN boolean jpeg_finish_output JPP((j_decompress_ptr cinfo)); +EXTERN boolean jpeg_input_complete JPP((j_decompress_ptr cinfo)); +EXTERN void jpeg_new_colormap JPP((j_decompress_ptr cinfo)); +EXTERN int jpeg_consume_input JPP((j_decompress_ptr cinfo)); +/* Return value is one of: */ +/* #define JPEG_SUSPENDED 0 Suspended due to lack of input data */ +#define JPEG_REACHED_SOS 1 /* Reached start of new scan */ +#define JPEG_REACHED_EOI 2 /* Reached end of image */ +#define JPEG_ROW_COMPLETED 3 /* Completed one iMCU row */ +#define JPEG_SCAN_COMPLETED 4 /* Completed last iMCU row of a scan */ + +/* Precalculate output dimensions for current decompression parameters. */ +EXTERN void jpeg_calc_output_dimensions JPP((j_decompress_ptr cinfo)); + +/* Install a special processing method for COM or APPn markers. */ +EXTERN void jpeg_set_marker_processor JPP((j_decompress_ptr cinfo, + int marker_code, + jpeg_marker_parser_method routine)); + +/* Read or write raw DCT coefficients --- useful for lossless transcoding. */ +EXTERN jvirt_barray_ptr * jpeg_read_coefficients JPP((j_decompress_ptr cinfo)); +EXTERN void jpeg_write_coefficients JPP((j_compress_ptr cinfo, + jvirt_barray_ptr * coef_arrays)); +EXTERN void jpeg_copy_critical_parameters JPP((j_decompress_ptr srcinfo, + j_compress_ptr dstinfo)); + +/* If you choose to abort compression or decompression before completing + * jpeg_finish_(de)compress, then you need to clean up to release memory, + * temporary files, etc. You can just call jpeg_destroy_(de)compress + * if you're done with the JPEG object, but if you want to clean it up and + * reuse it, call this: + */ +EXTERN void jpeg_abort_compress JPP((j_compress_ptr cinfo)); +EXTERN void jpeg_abort_decompress JPP((j_decompress_ptr cinfo)); + +/* Generic versions of jpeg_abort and jpeg_destroy that work on either + * flavor of JPEG object. These may be more convenient in some places. + */ +EXTERN void jpeg_abort JPP((j_common_ptr cinfo)); +EXTERN void jpeg_destroy JPP((j_common_ptr cinfo)); + +/* Default restart-marker-resync procedure for use by data source modules */ +EXTERN boolean jpeg_resync_to_restart JPP((j_decompress_ptr cinfo, + int desired)); + + +/* These marker codes are exported since applications and data source modules + * are likely to want to use them. + */ + +#define JPEG_RST0 0xD0 /* RST0 marker code */ +#define JPEG_EOI 0xD9 /* EOI marker code */ +#define JPEG_APP0 0xE0 /* APP0 marker code */ +#define JPEG_COM 0xFE /* COM marker code */ + + +/* If we have a brain-damaged compiler that emits warnings (or worse, errors) + * for structure definitions that are never filled in, keep it quiet by + * supplying dummy definitions for the various substructures. + */ + +#ifdef INCOMPLETE_TYPES_BROKEN +#ifndef JPEG_INTERNALS /* will be defined in jpegint.h */ +struct jvirt_sarray_control { long dummy; }; +struct jvirt_barray_control { long dummy; }; +struct jpeg_comp_master { long dummy; }; +struct jpeg_c_main_controller { long dummy; }; +struct jpeg_c_prep_controller { long dummy; }; +struct jpeg_c_coef_controller { long dummy; }; +struct jpeg_marker_writer { long dummy; }; +struct jpeg_color_converter { long dummy; }; +struct jpeg_downsampler { long dummy; }; +struct jpeg_forward_dct { long dummy; }; +struct jpeg_entropy_encoder { long dummy; }; +struct jpeg_decomp_master { long dummy; }; +struct jpeg_d_main_controller { long dummy; }; +struct jpeg_d_coef_controller { long dummy; }; +struct jpeg_d_post_controller { long dummy; }; +struct jpeg_input_controller { long dummy; }; +struct jpeg_marker_reader { long dummy; }; +struct jpeg_entropy_decoder { long dummy; }; +struct jpeg_inverse_dct { long dummy; }; +struct jpeg_upsampler { long dummy; }; +struct jpeg_color_deconverter { long dummy; }; +struct jpeg_color_quantizer { long dummy; }; +#endif /* JPEG_INTERNALS */ +#endif /* INCOMPLETE_TYPES_BROKEN */ + + +/* + * The JPEG library modules define JPEG_INTERNALS before including this file. + * The internal structure declarations are read only when that is true. + * Applications using the library should not include jpegint.h, but may wish + * to include jerror.h. + */ + +#ifdef JPEG_INTERNALS +#include "jpegint.h" /* fetch private declarations */ +#include "jerror.h" /* fetch error codes too */ +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* JPEGLIB_H */ diff --git a/tools/urt/libs/l_net/.cvsignore b/tools/urt/libs/l_net/.cvsignore new file mode 100644 index 00000000..877bc8c4 --- /dev/null +++ b/tools/urt/libs/l_net/.cvsignore @@ -0,0 +1,5 @@ +Debug +Release +*.plg +*.BAK +.consign diff --git a/tools/urt/libs/l_net/l_net.c b/tools/urt/libs/l_net/l_net.c new file mode 100644 index 00000000..6740f6e5 --- /dev/null +++ b/tools/urt/libs/l_net/l_net.c @@ -0,0 +1,630 @@ +/* +This code is based on source provided under the terms of the Id Software +LIMITED USE SOFTWARE LICENSE AGREEMENT, a copy of which is included with the +GtkRadiant sources (see LICENSE_ID). If you did not receive a copy of +LICENSE_ID, please contact Id Software immediately at info@idsoftware.com. + +All changes and additions to the original source which have been developed by +other contributors (see CONTRIBUTORS) are provided under the terms of the +license the contributors choose (see LICENSE), to the extent permitted by the +LICENSE_ID. If you did not receive a copy of the contributor license, +please contact the GtkRadiant maintainers at info@gtkradiant.com immediately. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +//==================================================================== +// +// Name: l_net.c +// Function: - +// Programmer: MrElusive +// Last update: - +// Tab size: 3 +// Notes: +//==================================================================== + +#include +#include +#include +#include +#include "l_net.h" +#include "l_net_wins.h" + +#define GetMemory malloc +#define FreeMemory free + +#define qtrue 1 +#define qfalse 0 + +#ifdef _DEBUG +void WinPrint(char *str, ...) +{ + va_list argptr; + char text[4096]; + + va_start (argptr,str); + vsprintf (text, str, argptr); + va_end (argptr); + + printf(text); +} +#else +void WinPrint(char *str, ...) +{ +} +#endif + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Net_SetAddressPort(address_t *address, int port) +{ + sockaddr_t addr; + + WINS_StringToAddr(address->ip, &addr); + WINS_SetSocketPort(&addr, port); + strcpy(address->ip, WINS_AddrToString(&addr)); +} //end of the function Net_SetAddressPort +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int Net_AddressCompare(address_t *addr1, address_t *addr2) +{ +#ifdef WIN32 + return stricmp(addr1->ip, addr2->ip); +#endif +#ifdef __linux__ + return strcasecmp(addr1->ip, addr2->ip); +#endif +} //end of the function Net_AddressCompare +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Net_SocketToAddress(socket_t *sock, address_t *address) +{ + strcpy(address->ip, WINS_AddrToString(&sock->addr)); +} //end of the function Net_SocketToAddress +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int Net_Send(socket_t *sock, netmessage_t *msg) +{ + int size; + + size = msg->size; + msg->size = 0; + NMSG_WriteLong(msg, size-4); + msg->size = size; + //WinPrint("Net_Send: message of size %d\n", sendmsg.size); + return WINS_Write(sock->socket, msg->data, msg->size, NULL); +} //end of the function Net_SendSocketReliable +//=========================================================================== +// returns the number of bytes recieved +// -1 on error +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int Net_Receive(socket_t *sock, netmessage_t *msg) +{ + int curread; + + if (sock->remaining > 0) + { + curread = WINS_Read(sock->socket, &sock->msg.data[sock->msg.size], sock->remaining, NULL); + if (curread == -1) + { + WinPrint("Net_Receive: read error\n"); + return -1; + } //end if + sock->remaining -= curread; + sock->msg.size += curread; + if (sock->remaining <= 0) + { + sock->remaining = 0; + memcpy(msg, &sock->msg, sizeof(netmessage_t)); + sock->msg.size = 0; + return msg->size - 4; + } //end if + return 0; + } //end if + sock->msg.size = WINS_Read(sock->socket, sock->msg.data, 4, NULL); + if (sock->msg.size == 0) return 0; + if (sock->msg.size == -1) + { + WinPrint("Net_Receive: size header read error\n"); + return -1; + } //end if + //WinPrint("Net_Receive: message size header %d\n", msg->size); + sock->msg.read = 0; + sock->remaining = NMSG_ReadLong(&sock->msg); + if (sock->remaining == 0) return 0; + if (sock->remaining < 0 || sock->remaining > MAX_NETMESSAGE) + { + WinPrint("Net_Receive: invalid message size %d\n", sock->remaining); + return -1; + } //end if + //try to read the message + curread = WINS_Read(sock->socket, &sock->msg.data[sock->msg.size], sock->remaining, NULL); + if (curread == -1) + { + WinPrint("Net_Receive: read error\n"); + return -1; + } //end if + sock->remaining -= curread; + sock->msg.size += curread; + if (sock->remaining <= 0) + { + sock->remaining = 0; + memcpy(msg, &sock->msg, sizeof(netmessage_t)); + sock->msg.size = 0; + return msg->size - 4; + } //end if + //the message has not been completely read yet +#ifdef _DEBUG + printf("++timo TODO: debug the Net_Receive on big size messages\n"); +#endif + return 0; +} //end of the function Net_Receive +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +socket_t *Net_AllocSocket(void) +{ + socket_t *sock; + + sock = (socket_t *) GetMemory(sizeof(socket_t)); + memset(sock, 0, sizeof(socket_t)); + return sock; +} //end of the function Net_AllocSocket +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Net_FreeSocket(socket_t *sock) +{ + FreeMemory(sock); +} //end of the function Net_FreeSocket +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +socket_t *Net_Connect(address_t *address, int port) +{ + int newsock; + socket_t *sock; + sockaddr_t sendaddr; + + // see if we can resolve the host name + WINS_StringToAddr(address->ip, &sendaddr); + + newsock = WINS_OpenReliableSocket(port); + if (newsock == -1) return NULL; + + sock = Net_AllocSocket(); + if (sock == NULL) + { + WINS_CloseSocket(newsock); + return NULL; + } //end if + sock->socket = newsock; + + //connect to the host + if (WINS_Connect(newsock, &sendaddr) == -1) + { + Net_FreeSocket(sock); + WINS_CloseSocket(newsock); + WinPrint("Net_Connect: error connecting\n"); + return NULL; + } //end if + + memcpy(&sock->addr, &sendaddr, sizeof(sockaddr_t)); + //now we can send messages + // + return sock; +} //end of the function Net_Connect + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +socket_t *Net_ListenSocket(int port) +{ + int newsock; + socket_t *sock; + + newsock = WINS_OpenReliableSocket(port); + if (newsock == -1) return NULL; + + if (WINS_Listen(newsock) == -1) + { + WINS_CloseSocket(newsock); + return NULL; + } //end if + sock = Net_AllocSocket(); + if (sock == NULL) + { + WINS_CloseSocket(newsock); + return NULL; + } //end if + sock->socket = newsock; + WINS_GetSocketAddr(newsock, &sock->addr); + WinPrint("listen socket opened at %s\n", WINS_AddrToString(&sock->addr)); + // + return sock; +} //end of the function Net_ListenSocket +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +socket_t *Net_Accept(socket_t *sock) +{ + int newsocket; + sockaddr_t sendaddr; + socket_t *newsock; + + newsocket = WINS_Accept(sock->socket, &sendaddr); + if (newsocket == -1) return NULL; + + newsock = Net_AllocSocket(); + if (newsock == NULL) + { + WINS_CloseSocket(newsocket); + return NULL; + } //end if + newsock->socket = newsocket; + memcpy(&newsock->addr, &sendaddr, sizeof(sockaddr_t)); + // + return newsock; +} //end of the function Net_Accept +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Net_Disconnect(socket_t *sock) +{ + WINS_CloseSocket(sock->socket); + Net_FreeSocket(sock); +} //end of the function Net_Disconnect +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Net_StringToAddress(char *string, address_t *address) +{ + strcpy(address->ip, string); +} //end of the function Net_StringToAddress +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Net_MyAddress(address_t *address) +{ + strcpy(address->ip, WINS_MyAddress()); +} //end of the function Net_MyAddress +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int Net_Setup(void) +{ + WINS_Init(); + // + WinPrint("my address is %s\n", WINS_MyAddress()); + // + return qtrue; +} //end of the function Net_Setup +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Net_Shutdown(void) +{ + WINS_Shutdown(); +} //end of the function Net_Shutdown +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void NMSG_Clear(netmessage_t *msg) +{ + msg->size = 4; +} //end of the function NMSG_Clear +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void NMSG_WriteChar (netmessage_t *msg, int c) +{ + if (c < -128 || c > 127) + WinPrint("NMSG_WriteChar: range error\n"); + + if (msg->size >= MAX_NETMESSAGE) + { + WinPrint("NMSG_WriteChar: overflow\n"); + return; + } //end if + msg->data[msg->size] = c; + msg->size++; +} //end of the function NMSG_WriteChar +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void NMSG_WriteByte(netmessage_t *msg, int c) +{ + if (c < -128 || c > 127) + WinPrint("NMSG_WriteByte: range error\n"); + + if (msg->size + 1 >= MAX_NETMESSAGE) + { + WinPrint("NMSG_WriteByte: overflow\n"); + return; + } //end if + msg->data[msg->size] = c; + msg->size++; +} //end of the function NMSG_WriteByte +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void NMSG_WriteShort(netmessage_t *msg, int c) +{ + if (c < ((short)0x8000) || c > (short)0x7fff) + WinPrint("NMSG_WriteShort: range error"); + + if (msg->size + 2 >= MAX_NETMESSAGE) + { + WinPrint("NMSG_WriteShort: overflow\n"); + return; + } //end if + msg->data[msg->size] = c&0xff; + msg->data[msg->size+1] = c>>8; + msg->size += 2; +} //end of the function NMSG_WriteShort +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void NMSG_WriteLong(netmessage_t *msg, int c) +{ + if (msg->size + 4 >= MAX_NETMESSAGE) + { + WinPrint("NMSG_WriteLong: overflow\n"); + return; + } //end if + msg->data[msg->size] = c&0xff; + msg->data[msg->size+1] = (c>>8)&0xff; + msg->data[msg->size+2] = (c>>16)&0xff; + msg->data[msg->size+3] = c>>24; + msg->size += 4; +} //end of the function NMSG_WriteLong +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void NMSG_WriteFloat(netmessage_t *msg, float c) +{ + if (msg->size + 4 >= MAX_NETMESSAGE) + { + WinPrint("NMSG_WriteLong: overflow\n"); + return; + } //end if + msg->data[msg->size] = *((int *)&c)&0xff; + msg->data[msg->size+1] = (*((int *)&c)>>8)&0xff; + msg->data[msg->size+2] = (*((int *)&c)>>16)&0xff; + msg->data[msg->size+3] = *((int *)&c)>>24; + msg->size += 4; +} //end of the function NMSG_WriteFloat +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void NMSG_WriteString(netmessage_t *msg, char *string) +{ + if (msg->size + strlen(string) + 1 >= MAX_NETMESSAGE) + { + WinPrint("NMSG_WriteString: overflow\n"); + return; + } //end if + strcpy(&msg->data[msg->size], string); + msg->size += strlen(string) + 1; +} //end of the function NMSG_WriteString +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void NMSG_ReadStart(netmessage_t *msg) +{ + msg->readoverflow = qfalse; + msg->read = 4; +} //end of the function NMSG_ReadStart +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int NMSG_ReadChar(netmessage_t *msg) +{ + if (msg->size + 1 > msg->size) + { + msg->readoverflow = qtrue; + WinPrint("NMSG_ReadChar: read overflow\n"); + return 0; + } //end if + msg->read++; + return msg->data[msg->read-1]; +} //end of the function NMSG_ReadChar +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int NMSG_ReadByte(netmessage_t *msg) +{ + if (msg->read + 1 > msg->size) + { + msg->readoverflow = qtrue; + WinPrint("NMSG_ReadByte: read overflow\n"); + return 0; + } //end if + msg->read++; + return msg->data[msg->read-1]; +} //end of the function NMSG_ReadByte +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int NMSG_ReadShort(netmessage_t *msg) +{ + int c; + + if (msg->read + 2 > msg->size) + { + msg->readoverflow = qtrue; + WinPrint("NMSG_ReadShort: read overflow\n"); + return 0; + } //end if + c = (short)(msg->data[msg->read] + (msg->data[msg->read+1]<<8)); + msg->read += 2; + return c; +} //end of the function NMSG_ReadShort +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int NMSG_ReadLong(netmessage_t *msg) +{ + int c; + + if (msg->read + 4 > msg->size) + { + msg->readoverflow = qtrue; + WinPrint("NMSG_ReadLong: read overflow\n"); + return 0; + } //end if + c = msg->data[msg->read] + + (msg->data[msg->read+1]<<8) + + (msg->data[msg->read+2]<<16) + + (msg->data[msg->read+3]<<24); + msg->read += 4; + return c; +} //end of the function NMSG_ReadLong +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +float NMSG_ReadFloat(netmessage_t *msg) +{ + int c; + + if (msg->read + 4 > msg->size) + { + msg->readoverflow = qtrue; + WinPrint("NMSG_ReadLong: read overflow\n"); + return 0; + } //end if + c = msg->data[msg->read] + + (msg->data[msg->read+1]<<8) + + (msg->data[msg->read+2]<<16) + + (msg->data[msg->read+3]<<24); + msg->read += 4; + return *(float *)&c; +} //end of the function NMSG_ReadFloat +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +char *NMSG_ReadString(netmessage_t *msg) +{ + static char string[2048]; + int l, c; + + l = 0; + do + { + if (msg->read + 1 > msg->size) + { + msg->readoverflow = qtrue; + WinPrint("NMSG_ReadString: read overflow\n"); + string[l] = 0; + return string; + } //end if + c = msg->data[msg->read]; + msg->read++; + if (c == 0) break; + string[l] = c; + l++; + } while (l < sizeof(string)-1); + string[l] = 0; + return string; +} //end of the function NMSG_ReadString diff --git a/tools/urt/libs/l_net/l_net.dsp b/tools/urt/libs/l_net/l_net.dsp new file mode 100644 index 00000000..36a13189 --- /dev/null +++ b/tools/urt/libs/l_net/l_net.dsp @@ -0,0 +1,118 @@ +# Microsoft Developer Studio Project File - Name="l_net" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=l_net - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "l_net.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "l_net.mak" CFG="l_net - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "l_net - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "l_net - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "l_net" +# PROP Scc_LocalPath "." +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "l_net - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +MTL=midl.exe +F90=df.exe +# ADD BASE F90 /compile_only /include:"Release/" /nologo /warn:nofileopt +# ADD F90 /compile_only /include:"Release/" /nologo /warn:nofileopt +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "l_net - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +MTL=midl.exe +F90=df.exe +# ADD BASE F90 /check:bounds /compile_only /debug:full /include:"Debug/" /nologo /warn:argument_checking /warn:nofileopt +# ADD F90 /check:bounds /compile_only /debug:full /include:"Debug/" /nologo /warn:argument_checking /warn:nofileopt +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FR /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "l_net - Win32 Release" +# Name "l_net - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat;f90;for;f;fpp" +# Begin Source File + +SOURCE=.\l_net.c +# End Source File +# Begin Source File + +SOURCE=.\l_net_wins.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd" +# Begin Source File + +SOURCE=.\l_net.h +# End Source File +# Begin Source File + +SOURCE=.\l_net_wins.h +# End Source File +# End Group +# End Target +# End Project diff --git a/tools/urt/libs/l_net/l_net.h b/tools/urt/libs/l_net/l_net.h new file mode 100644 index 00000000..2c0f9fff --- /dev/null +++ b/tools/urt/libs/l_net/l_net.h @@ -0,0 +1,128 @@ +/* +This code is based on source provided under the terms of the Id Software +LIMITED USE SOFTWARE LICENSE AGREEMENT, a copy of which is included with the +GtkRadiant sources (see LICENSE_ID). If you did not receive a copy of +LICENSE_ID, please contact Id Software immediately at info@idsoftware.com. + +All changes and additions to the original source which have been developed by +other contributors (see CONTRIBUTORS) are provided under the terms of the +license the contributors choose (see LICENSE), to the extent permitted by the +LICENSE_ID. If you did not receive a copy of the contributor license, +please contact the GtkRadiant maintainers at info@gtkradiant.com immediately. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +//==================================================================== +// +// Name: l_net.h +// Function: - +// Programmer: MrElusive +// Last update: TTimo: cross-platform version, l_net library +// Tab size: 2 +// Notes: +//==================================================================== + +//++timo FIXME: the l_net code understands that as the max size for the netmessage_s structure +// we have defined unsigned char data[MAX_NETMESSAGE] in netmessage_s but actually it cannot be filled completely +// we need to introduce a new #define and adapt to data[MAX_NETBUFFER] +#define MAX_NETMESSAGE 1024 +#define MAX_NETADDRESS 32 + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef __BYTEBOOL__ +#define __BYTEBOOL__ +typedef enum { qfalse, qtrue } qboolean; +typedef unsigned char byte; +#endif + +typedef struct address_s +{ + char ip[MAX_NETADDRESS]; +} address_t; + +typedef struct sockaddr_s +{ + short sa_family; + unsigned char sa_data[14]; +} sockaddr_t; + +typedef struct netmessage_s +{ + unsigned char data[MAX_NETMESSAGE]; + int size; + int read; + int readoverflow; +} netmessage_t; + +typedef struct socket_s +{ + int socket; //socket number + sockaddr_t addr; //socket address + netmessage_t msg; //current message being read + int remaining; //remaining bytes to read for the current message + struct socket_s *prev, *next; //prev and next socket in a list +} socket_t; + +//compare addresses +int Net_AddressCompare(address_t *addr1, address_t *addr2); +//gives the address of a socket +void Net_SocketToAddress(socket_t *sock, address_t *address); +//converts a string to an address +void Net_StringToAddress(char *string, address_t *address); +//set the address ip port +void Net_SetAddressPort(address_t *address, int port); +//send a message to the given socket +int Net_Send(socket_t *sock, netmessage_t *msg); +//recieve a message from the given socket +int Net_Receive(socket_t *sock, netmessage_t *msg); +//connect to a host +// NOTE: port is the localhost port, usually 0 +// ex: Net_Connect( "192.168.0.1:39000", 0 ) +socket_t *Net_Connect(address_t *address, int port); +//disconnect from a host +void Net_Disconnect(socket_t *sock); +//returns the local address +void Net_MyAddress(address_t *address); +//listen at the given port +socket_t *Net_ListenSocket(int port); +//accept new connections at the given socket +socket_t *Net_Accept(socket_t *sock); +//setup networking +int Net_Setup(void); +//shutdown networking +void Net_Shutdown(void); +//message handling +void NMSG_Clear(netmessage_t *msg); +void NMSG_WriteChar(netmessage_t *msg, int c); +void NMSG_WriteByte(netmessage_t *msg, int c); +void NMSG_WriteShort(netmessage_t *msg, int c); +void NMSG_WriteLong(netmessage_t *msg, int c); +void NMSG_WriteFloat(netmessage_t *msg, float c); +void NMSG_WriteString(netmessage_t *msg, char *string); +void NMSG_ReadStart(netmessage_t *msg); +int NMSG_ReadChar(netmessage_t *msg); +int NMSG_ReadByte(netmessage_t *msg); +int NMSG_ReadShort(netmessage_t *msg); +int NMSG_ReadLong(netmessage_t *msg); +float NMSG_ReadFloat(netmessage_t *msg); +char *NMSG_ReadString(netmessage_t *msg); + +//++timo FIXME: the WINS_ things are not necessary, they can be made portable arther easily +char *WINS_ErrorMessage(int error); + +#ifdef __cplusplus +} +#endif diff --git a/tools/urt/libs/l_net/l_net.vcproj b/tools/urt/libs/l_net/l_net.vcproj new file mode 100644 index 00000000..e9d1c473 --- /dev/null +++ b/tools/urt/libs/l_net/l_net.vcproj @@ -0,0 +1,230 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/urt/libs/l_net/l_net_berkley.c b/tools/urt/libs/l_net/l_net_berkley.c new file mode 100644 index 00000000..2c03373f --- /dev/null +++ b/tools/urt/libs/l_net/l_net_berkley.c @@ -0,0 +1,769 @@ +/* +This code is based on source provided under the terms of the Id Software +LIMITED USE SOFTWARE LICENSE AGREEMENT, a copy of which is included with the +GtkRadiant sources (see LICENSE_ID). If you did not receive a copy of +LICENSE_ID, please contact Id Software immediately at info@idsoftware.com. + +All changes and additions to the original source which have been developed by +other contributors (see CONTRIBUTORS) are provided under the terms of the +license the contributors choose (see LICENSE), to the extent permitted by the +LICENSE_ID. If you did not receive a copy of the contributor license, +please contact the GtkRadiant maintainers at info@gtkradiant.com immediately. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +//=========================================================================== +// +// Name: l_net_wins.c +// Function: WinSock +// Programmer: MrElusive +// Last update: TTimo: cross-platform version, l_net library +// Tab Size: 2 +// Notes: +//=========================================================================== + +//#include +#include +#include +#include +#include "l_net.h" +#include "l_net_wins.h" + +#include +#include +#include +#include +#include +#include +#include +#define SOCKET_ERROR -1 +#define INVALID_SOCKET -1 + +#define WinError WinPrint + +#define qtrue 1 +#define qfalse 0 + +#define ioctlsocket ioctl +#define closesocket close + +int WSAGetLastError() +{ + return errno; +} + +/* +typedef struct tag_error_struct +{ + int errnum; + LPSTR errstr; +} ERROR_STRUCT; +*/ + +typedef struct tag_error_struct +{ + int errnum; + const char *errstr; +} ERROR_STRUCT; + +#define NET_NAMELEN 64 + +static char my_tcpip_address[NET_NAMELEN]; + +#define DEFAULTnet_hostport 26000 + +#define MAXHOSTNAMELEN 256 + +static int net_acceptsocket = -1; // socket for fielding new connections +static int net_controlsocket; +static int net_hostport; // udp port number for acceptsocket +static int net_broadcastsocket = 0; +//static qboolean ifbcastinit = qfalse; +//static struct sockaddr_s broadcastaddr; +static struct sockaddr_s broadcastaddr; + +static unsigned long myAddr; + +ERROR_STRUCT errlist[] = { + {EACCES,"EACCES - The address is protected, user is not root"}, + {EAGAIN,"EAGAIN - Operation on non-blocking socket that cannot return immediatly"}, + {EBADF, "EBADF - sockfd is not a valid descriptor"}, + {EFAULT, "EFAULT - The parameter is not in a writable part of the user address space"}, + {EINVAL,"EINVAL - The socket is already bound to an address"}, + {ENOBUFS,"ENOBUFS - not enough memory"}, + {ENOMEM, "ENOMEM - not enough memory"}, + {ENOTCONN, "ENOTCONN - not connected"}, + {ENOTSOCK,"ENOTSOCK - Argument is file descriptor not a socket"}, + {EOPNOTSUPP,"ENOTSUPP - The referenced socket is not of type SOCK_STREAM"}, + {EPERM, "EPERM - Firewall rules forbid connection"}, + {-1, NULL} +}; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +char *WINS_ErrorMessage(int error) +{ + int search = 0; + + if (!error) return "No error occurred"; + + for (search = 0; errlist[search].errstr; search++) + { + if (error == errlist[search].errnum) + return (char *)errlist[search].errstr; + } //end for + + return "Unknown error"; +} //end of the function WINS_ErrorMessage +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WINS_Init(void) +{ + int i; + struct hostent *local; + char buff[MAXHOSTNAMELEN]; + struct sockaddr_s addr; + char *p; + int r; +/* + linux doesn't have anything to initialize for the net + "Windows .. built for the internet .. the internet .. built with unix" + */ +#if 0 + WORD wVersionRequested; + + wVersionRequested = MAKEWORD(2, 2); + + r = WSAStartup (wVersionRequested, &winsockdata); + + if (r) + { + WinPrint("Winsock initialization failed.\n"); + return -1; + } +#endif + /* + i = COM_CheckParm ("-udpport"); + if (i == 0)*/ + net_hostport = DEFAULTnet_hostport; + /* + else if (i < com_argc-1) + net_hostport = Q_atoi (com_argv[i+1]); + else + Sys_Error ("WINS_Init: you must specify a number after -udpport"); + */ + + // determine my name & address + gethostname(buff, MAXHOSTNAMELEN); + local = gethostbyname(buff); + myAddr = *(int *)local->h_addr_list[0]; + + // if the quake hostname isn't set, set it to the machine name +// if (Q_strcmp(hostname.string, "UNNAMED") == 0) + { + // see if it's a text IP address (well, close enough) + for (p = buff; *p; p++) + if ((*p < '0' || *p > '9') && *p != '.') + break; + + // if it is a real name, strip off the domain; we only want the host + if (*p) + { + for (i = 0; i < 15; i++) + if (buff[i] == '.') + break; + buff[i] = 0; + } +// Cvar_Set ("hostname", buff); + } + + //++timo WTF is that net_controlsocket? it's sole purpose is to retrieve the local IP? + if ((net_controlsocket = WINS_OpenSocket (0)) == SOCKET_ERROR) + WinError("WINS_Init: Unable to open control socket\n"); + + ((struct sockaddr_in *)&broadcastaddr)->sin_family = AF_INET; + ((struct sockaddr_in *)&broadcastaddr)->sin_addr.s_addr = INADDR_BROADCAST; + ((struct sockaddr_in *)&broadcastaddr)->sin_port = htons((u_short)net_hostport); + + WINS_GetSocketAddr (net_controlsocket, &addr); + strcpy(my_tcpip_address, WINS_AddrToString (&addr)); + p = strrchr (my_tcpip_address, ':'); + if (p) *p = 0; + WinPrint("Winsock Initialized\n"); + + return net_controlsocket; +} //end of the function WINS_Init +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +char *WINS_MyAddress(void) +{ + return my_tcpip_address; +} //end of the function WINS_MyAddress +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void WINS_Shutdown(void) +{ + //WINS_Listen(0); + WINS_CloseSocket(net_controlsocket); +// WSACleanup(); + // + //WinPrint("Winsock Shutdown\n"); +} //end of the function WINS_Shutdown +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +/* +void WINS_Listen(int state) +{ + // enable listening + if (state) + { + if (net_acceptsocket != -1) + return; + if ((net_acceptsocket = WINS_OpenSocket (net_hostport)) == -1) + WinError ("WINS_Listen: Unable to open accept socket\n"); + return; + } + + // disable listening + if (net_acceptsocket == -1) + return; + WINS_CloseSocket (net_acceptsocket); + net_acceptsocket = -1; +} //end of the function WINS_Listen*/ +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WINS_OpenSocket(int port) +{ + int newsocket; + struct sockaddr_in address; + u_long _true = 1; + + if ((newsocket = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == SOCKET_ERROR) + { + WinPrint("WINS_OpenSocket: %s\n", WINS_ErrorMessage(WSAGetLastError())); + return -1; + } //end if + + if (ioctlsocket (newsocket, FIONBIO, &_true) == SOCKET_ERROR) + { + WinPrint("WINS_OpenSocket: %s\n", WINS_ErrorMessage(WSAGetLastError())); + closesocket(newsocket); + return -1; + } //end if + + memset((char *) &address, 0, sizeof(address)); + address.sin_family = AF_INET; + address.sin_addr.s_addr = INADDR_ANY; + address.sin_port = htons((u_short)port); + if( bind (newsocket, (void *)&address, sizeof(address)) == -1) + { + WinPrint("WINS_OpenSocket: %s\n", WINS_ErrorMessage(WSAGetLastError())); + closesocket(newsocket); + return -1; + } //end if + + return newsocket; +} //end of the function WINS_OpenSocket +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WINS_OpenReliableSocket(int port) +{ + int newsocket; + struct sockaddr_in address; + qboolean _true = 0xFFFFFFFF; + + //IPPROTO_TCP + // + if ((newsocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) + { + WinPrint("WINS_OpenReliableSocket: %s\n", WINS_ErrorMessage(WSAGetLastError())); + return -1; + } //end if + + memset((char *) &address, 0, sizeof(address)); + address.sin_family = AF_INET; + address.sin_addr.s_addr = htonl(INADDR_ANY); + address.sin_port = htons((u_short)port); + if (bind(newsocket, (void *)&address, sizeof(address)) == -1) + { + WinPrint("WINS_OpenReliableSocket: %s\n", WINS_ErrorMessage(WSAGetLastError())); + closesocket(newsocket); + return -1; + } //end if + + // + if (setsockopt(newsocket, IPPROTO_TCP, TCP_NODELAY, (void *) &_true, sizeof(int)) == -1) + { + WinPrint("WINS_OpenReliableSocket: %s\n", WINS_ErrorMessage(WSAGetLastError())); + WinPrint("setsockopt error\n"); + } //end if + + return newsocket; +} //end of the function WINS_OpenReliableSocket +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WINS_Listen(int socket) +{ + u_long _true = 1; + + if (ioctlsocket(socket, FIONBIO, &_true) == -1) + { + WinPrint("WINS_Listen: %s\n", WINS_ErrorMessage(WSAGetLastError())); + return -1; + } //end if + if (listen(socket, SOMAXCONN) == SOCKET_ERROR) + { + WinPrint("WINS_Listen: %s\n", WINS_ErrorMessage(WSAGetLastError())); + return -1; + } //end if + return 0; +} //end of the function WINS_Listen +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WINS_Accept(int socket, struct sockaddr_s *addr) +{ + int addrlen = sizeof (struct sockaddr_s); + int newsocket; + qboolean _true = 1; + + newsocket = accept(socket, (struct sockaddr *)addr, &addrlen); + if (newsocket == INVALID_SOCKET) + { + if (errno == EAGAIN) return -1; + WinPrint("WINS_Accept: %s\n", WINS_ErrorMessage(WSAGetLastError())); + return -1; + } //end if + // + if (setsockopt(newsocket, IPPROTO_TCP, TCP_NODELAY, (void *) &_true, sizeof(int)) == SOCKET_ERROR) + { + WinPrint("WINS_Accept: %s\n", WINS_ErrorMessage(WSAGetLastError())); + WinPrint("setsockopt error\n"); + } //end if + return newsocket; +} //end of the function WINS_Accept +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WINS_CloseSocket(int socket) +{ + /* + if (socket == net_broadcastsocket) + net_broadcastsocket = 0; + */ +// shutdown(socket, SD_SEND); + + if (closesocket(socket) == SOCKET_ERROR) + { + WinPrint("WINS_CloseSocket: %s\n", WINS_ErrorMessage(WSAGetLastError())); + return SOCKET_ERROR; + } //end if + return 0; +} //end of the function WINS_CloseSocket +//=========================================================================== +// this lets you type only as much of the net address as required, using +// the local network components to fill in the rest +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +static int PartialIPAddress (char *in, struct sockaddr_s *hostaddr) +{ + char buff[256]; + char *b; + int addr; + int num; + int mask; + + buff[0] = '.'; + b = buff; + strcpy(buff+1, in); + if (buff[1] == '.') b++; + + addr = 0; + mask=-1; + while (*b == '.') + { + num = 0; + if (*++b < '0' || *b > '9') return -1; + while (!( *b < '0' || *b > '9')) + num = num*10 + *(b++) - '0'; + mask<<=8; + addr = (addr<<8) + num; + } + + hostaddr->sa_family = AF_INET; + ((struct sockaddr_in *)hostaddr)->sin_port = htons((u_short)net_hostport); + ((struct sockaddr_in *)hostaddr)->sin_addr.s_addr = (myAddr & htonl(mask)) | htonl(addr); + + return 0; +} //end of the function PartialIPAddress +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WINS_Connect(int socket, struct sockaddr_s *addr) +{ + int ret; + u_long _true2 = 0xFFFFFFFF; + + ret = connect(socket, (struct sockaddr *)addr, sizeof(struct sockaddr_s)); + if (ret == SOCKET_ERROR) + { + WinPrint("WINS_Connect: %s\n", WINS_ErrorMessage(WSAGetLastError())); + return -1; + } //end if + if (ioctlsocket(socket, FIONBIO, &_true2) == -1) + { + WinPrint("WINS_Connect: %s\n", WINS_ErrorMessage(WSAGetLastError())); + return -1; + } //end if + return 0; +} //end of the function WINS_Connect +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WINS_CheckNewConnections(void) +{ + char buf[4]; + + if (net_acceptsocket == -1) + return -1; + + if (recvfrom(net_acceptsocket, buf, 4, MSG_PEEK, NULL, NULL) > 0) + return net_acceptsocket; + return -1; +} //end of the function WINS_CheckNewConnections +//=========================================================================== +// returns the number of bytes read +// 0 if no bytes available +// -1 on failure +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WINS_Read(int socket, byte *buf, int len, struct sockaddr_s *addr) +{ + int addrlen = sizeof (struct sockaddr_s); + int ret; + + if (addr) + { + ret = recvfrom(socket, buf, len, 0, (struct sockaddr *)addr, &addrlen); + if (ret == -1) + { +// errno = WSAGetLastError(); + + if (errno == EAGAIN || errno == ENOTCONN) + return 0; + } //end if + } //end if + else + { + ret = recv(socket, buf, len, 0); + // if there's no data on the socket ret == -1 and errno == EAGAIN + // MSDN states that if ret == 0 the socket has been closed + // man recv doesn't say anything + if (ret == 0) + return -1; + if (ret == SOCKET_ERROR) + { +// errno = WSAGetLastError(); + + if (errno == EAGAIN || errno == ENOTCONN) + return 0; + } //end if + } //end else + if (ret == SOCKET_ERROR) + { + WinPrint("WINS_Read: %s\n", WINS_ErrorMessage(WSAGetLastError())); + } //end if + return ret; +} //end of the function WINS_Read +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WINS_MakeSocketBroadcastCapable (int socket) +{ + int i = 1; + + // make this socket broadcast capable + if (setsockopt(socket, SOL_SOCKET, SO_BROADCAST, (char *)&i, sizeof(i)) < 0) + return -1; + net_broadcastsocket = socket; + + return 0; +} //end of the function WINS_MakeSocketBroadcastCapable +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WINS_Broadcast (int socket, byte *buf, int len) +{ + int ret; + + if (socket != net_broadcastsocket) + { + if (net_broadcastsocket != 0) + WinError("Attempted to use multiple broadcasts sockets\n"); + ret = WINS_MakeSocketBroadcastCapable (socket); + if (ret == -1) + { + WinPrint("Unable to make socket broadcast capable\n"); + return ret; + } + } + + return WINS_Write (socket, buf, len, &broadcastaddr); +} //end of the function WINS_Broadcast +//=========================================================================== +// returns qtrue on success or qfalse on failure +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WINS_Write(int socket, byte *buf, int len, struct sockaddr_s *addr) +{ + int ret, written; + + if (addr) + { + written = 0; + while(written < len) + { + ret = sendto (socket, &buf[written], len-written, 0, (struct sockaddr *)addr, sizeof(struct sockaddr_s)); + if (ret == SOCKET_ERROR) + { + if (WSAGetLastError() != EAGAIN) + return qfalse; + //++timo FIXME: what is this used for? +// Sleep(1000); + } //end if + else + { + written += ret; + } + } + } //end if + else + { + written = 0; + while(written < len) + { + ret = send(socket, buf, len, 0); + if (ret == SOCKET_ERROR) + { + if (WSAGetLastError() != EAGAIN) + return qfalse; + //++timo FIXME: what is this used for? +// Sleep(1000); + } //end if + else + { + written += ret; + } + } + } //end else + if (ret == SOCKET_ERROR) + { + WinPrint("WINS_Write: %s\n", WINS_ErrorMessage(WSAGetLastError())); + } //end if + return (ret == len); +} //end of the function WINS_Write +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +char *WINS_AddrToString (struct sockaddr_s *addr) +{ + static char buffer[22]; + int haddr; + + haddr = ntohl(((struct sockaddr_in *)addr)->sin_addr.s_addr); + sprintf(buffer, "%d.%d.%d.%d:%d", (haddr >> 24) & 0xff, (haddr >> 16) & 0xff, (haddr >> 8) & 0xff, haddr & 0xff, ntohs(((struct sockaddr_in *)addr)->sin_port)); + return buffer; +} //end of the function WINS_AddrToString +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WINS_StringToAddr(char *string, struct sockaddr_s *addr) +{ + int ha1, ha2, ha3, ha4, hp; + int ipaddr; + + sscanf(string, "%d.%d.%d.%d:%d", &ha1, &ha2, &ha3, &ha4, &hp); + ipaddr = (ha1 << 24) | (ha2 << 16) | (ha3 << 8) | ha4; + + addr->sa_family = AF_INET; + ((struct sockaddr_in *)addr)->sin_addr.s_addr = htonl(ipaddr); + ((struct sockaddr_in *)addr)->sin_port = htons((u_short)hp); + return 0; +} //end of the function WINS_StringToAddr +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WINS_GetSocketAddr(int socket, struct sockaddr_s *addr) +{ + int addrlen = sizeof(struct sockaddr_s); + unsigned int a; + + memset(addr, 0, sizeof(struct sockaddr_s)); + getsockname(socket, (struct sockaddr *)addr, &addrlen); + a = ((struct sockaddr_in *)addr)->sin_addr.s_addr; + if (a == 0 || a == inet_addr("127.0.0.1")) + ((struct sockaddr_in *)addr)->sin_addr.s_addr = myAddr; + + return 0; +} //end of the function WINS_GetSocketAddr +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WINS_GetNameFromAddr (struct sockaddr_s *addr, char *name) +{ + struct hostent *hostentry; + + hostentry = gethostbyaddr ((char *)&((struct sockaddr_in *)addr)->sin_addr, sizeof(struct in_addr), AF_INET); + if (hostentry) + { + strncpy (name, (char *)hostentry->h_name, NET_NAMELEN - 1); + return 0; + } + + strcpy (name, WINS_AddrToString (addr)); + return 0; +} //end of the function WINS_GetNameFromAddr +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WINS_GetAddrFromName(char *name, struct sockaddr_s *addr) +{ + struct hostent *hostentry; + + if (name[0] >= '0' && name[0] <= '9') + return PartialIPAddress (name, addr); + + hostentry = gethostbyname (name); + if (!hostentry) + return -1; + + addr->sa_family = AF_INET; + ((struct sockaddr_in *)addr)->sin_port = htons((u_short)net_hostport); + ((struct sockaddr_in *)addr)->sin_addr.s_addr = *(int *)hostentry->h_addr_list[0]; + + return 0; +} //end of the function WINS_GetAddrFromName +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WINS_AddrCompare (struct sockaddr_s *addr1, struct sockaddr_s *addr2) +{ + if (addr1->sa_family != addr2->sa_family) + return -1; + + if (((struct sockaddr_in *)addr1)->sin_addr.s_addr != ((struct sockaddr_in *)addr2)->sin_addr.s_addr) + return -1; + + if (((struct sockaddr_in *)addr1)->sin_port != ((struct sockaddr_in *)addr2)->sin_port) + return 1; + + return 0; +} //end of the function WINS_AddrCompare +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WINS_GetSocketPort (struct sockaddr_s *addr) +{ + return ntohs(((struct sockaddr_in *)addr)->sin_port); +} //end of the function WINS_GetSocketPort +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WINS_SetSocketPort (struct sockaddr_s *addr, int port) +{ + ((struct sockaddr_in *)addr)->sin_port = htons((u_short)port); + return 0; +} //end of the function WINS_SetSocketPort diff --git a/tools/urt/libs/l_net/l_net_wins.c b/tools/urt/libs/l_net/l_net_wins.c new file mode 100644 index 00000000..25c42989 --- /dev/null +++ b/tools/urt/libs/l_net/l_net_wins.c @@ -0,0 +1,792 @@ +/* +This code is based on source provided under the terms of the Id Software +LIMITED USE SOFTWARE LICENSE AGREEMENT, a copy of which is included with the +GtkRadiant sources (see LICENSE_ID). If you did not receive a copy of +LICENSE_ID, please contact Id Software immediately at info@idsoftware.com. + +All changes and additions to the original source which have been developed by +other contributors (see CONTRIBUTORS) are provided under the terms of the +license the contributors choose (see LICENSE), to the extent permitted by the +LICENSE_ID. If you did not receive a copy of the contributor license, +please contact the GtkRadiant maintainers at info@gtkradiant.com immediately. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +//=========================================================================== +// +// Name: l_net_wins.c +// Function: WinSock +// Programmer: MrElusive +// Last update: - +// Tab Size: 3 +// Notes: +//=========================================================================== + +#include +#include +#include +#include +#include "l_net.h" +#include "l_net_wins.h" +//#include +//#include "mpdosock.h" + +#define WinError WinPrint + +#define qtrue 1 +#define qfalse 0 + +typedef struct tag_error_struct +{ + int errnum; + LPSTR errstr; +} ERROR_STRUCT; + +#define NET_NAMELEN 64 + +char my_tcpip_address[NET_NAMELEN]; + +#define DEFAULTnet_hostport 26000 + +#define MAXHOSTNAMELEN 256 + +static int net_acceptsocket = -1; // socket for fielding new connections +static int net_controlsocket; +static int net_hostport; // udp port number for acceptsocket +static int net_broadcastsocket = 0; +//static qboolean ifbcastinit = qfalse; +static struct sockaddr_s broadcastaddr; + +static unsigned long myAddr; + +WSADATA winsockdata; + +ERROR_STRUCT errlist[] = { + {WSAEINTR, "WSAEINTR - Interrupted"}, + {WSAEBADF, "WSAEBADF - Bad file number"}, + {WSAEFAULT, "WSAEFAULT - Bad address"}, + {WSAEINVAL, "WSAEINVAL - Invalid argument"}, + {WSAEMFILE, "WSAEMFILE - Too many open files"}, + +/* +* Windows Sockets definitions of regular Berkeley error constants +*/ + + {WSAEWOULDBLOCK, "WSAEWOULDBLOCK - Socket marked as non-blocking"}, + {WSAEINPROGRESS, "WSAEINPROGRESS - Blocking call in progress"}, + {WSAEALREADY, "WSAEALREADY - Command already completed"}, + {WSAENOTSOCK, "WSAENOTSOCK - Descriptor is not a socket"}, + {WSAEDESTADDRREQ, "WSAEDESTADDRREQ - Destination address required"}, + {WSAEMSGSIZE, "WSAEMSGSIZE - Data size too large"}, + {WSAEPROTOTYPE, "WSAEPROTOTYPE - Protocol is of wrong type for this socket"}, + {WSAENOPROTOOPT, "WSAENOPROTOOPT - Protocol option not supported for this socket type"}, + {WSAEPROTONOSUPPORT, "WSAEPROTONOSUPPORT - Protocol is not supported"}, + {WSAESOCKTNOSUPPORT, "WSAESOCKTNOSUPPORT - Socket type not supported by this address family"}, + {WSAEOPNOTSUPP, "WSAEOPNOTSUPP - Option not supported"}, + {WSAEPFNOSUPPORT, "WSAEPFNOSUPPORT - "}, + {WSAEAFNOSUPPORT, "WSAEAFNOSUPPORT - Address family not supported by this protocol"}, + {WSAEADDRINUSE, "WSAEADDRINUSE - Address is in use"}, + {WSAEADDRNOTAVAIL, "WSAEADDRNOTAVAIL - Address not available from local machine"}, + {WSAENETDOWN, "WSAENETDOWN - Network subsystem is down"}, + {WSAENETUNREACH, "WSAENETUNREACH - Network cannot be reached"}, + {WSAENETRESET, "WSAENETRESET - Connection has been dropped"}, + {WSAECONNABORTED, "WSAECONNABORTED - Connection aborted"}, + {WSAECONNRESET, "WSAECONNRESET - Connection reset"}, + {WSAENOBUFS, "WSAENOBUFS - No buffer space available"}, + {WSAEISCONN, "WSAEISCONN - Socket is already connected"}, + {WSAENOTCONN, "WSAENOTCONN - Socket is not connected"}, + {WSAESHUTDOWN, "WSAESHUTDOWN - Socket has been shut down"}, + {WSAETOOMANYREFS, "WSAETOOMANYREFS - Too many references"}, + {WSAETIMEDOUT, "WSAETIMEDOUT - Command timed out"}, + {WSAECONNREFUSED, "WSAECONNREFUSED - Connection refused"}, + {WSAELOOP, "WSAELOOP - "}, + {WSAENAMETOOLONG, "WSAENAMETOOLONG - "}, + {WSAEHOSTDOWN, "WSAEHOSTDOWN - Host is down"}, + {WSAEHOSTUNREACH, "WSAEHOSTUNREACH - "}, + {WSAENOTEMPTY, "WSAENOTEMPTY - "}, + {WSAEPROCLIM, "WSAEPROCLIM - "}, + {WSAEUSERS, "WSAEUSERS - "}, + {WSAEDQUOT, "WSAEDQUOT - "}, + {WSAESTALE, "WSAESTALE - "}, + {WSAEREMOTE, "WSAEREMOTE - "}, + +/* +* Extended Windows Sockets error constant definitions +*/ + + {WSASYSNOTREADY, "WSASYSNOTREADY - Network subsystem not ready"}, + {WSAVERNOTSUPPORTED, "WSAVERNOTSUPPORTED - Version not supported"}, + {WSANOTINITIALISED, "WSANOTINITIALISED - WSAStartup() has not been successfully called"}, + +/* +* Other error constants. +*/ + + {WSAHOST_NOT_FOUND, "WSAHOST_NOT_FOUND - Host not found"}, + {WSATRY_AGAIN, "WSATRY_AGAIN - Host not found or SERVERFAIL"}, + {WSANO_RECOVERY, "WSANO_RECOVERY - Non-recoverable error"}, + {WSANO_DATA, "WSANO_DATA - (or WSANO_ADDRESS) - No data record of requested type"}, + {-1, NULL} +}; + +#ifdef _DEBUG +void WinPrint(char *str, ...); +#else +void WinPrint(char *str, ...); +#endif + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +char *WINS_ErrorMessage(int error) +{ + int search = 0; + + if (!error) return "No error occurred"; + + for (search = 0; errlist[search].errstr; search++) + { + if (error == errlist[search].errnum) + return errlist[search].errstr; + } //end for + + return "Unknown error"; +} //end of the function WINS_ErrorMessage +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WINS_Init(void) +{ + int i; + struct hostent *local; + char buff[MAXHOSTNAMELEN]; + struct sockaddr_s addr; + char *p; + int r; + WORD wVersionRequested; + + wVersionRequested = MAKEWORD(1, 1); + + r = WSAStartup (wVersionRequested, &winsockdata); + + if (r) + { + WinPrint("Winsock initialization failed.\n"); + return -1; + } + + /* + i = COM_CheckParm ("-udpport"); + if (i == 0)*/ + net_hostport = DEFAULTnet_hostport; + /* + else if (i < com_argc-1) + net_hostport = Q_atoi (com_argv[i+1]); + else + Sys_Error ("WINS_Init: you must specify a number after -udpport"); + */ + + // determine my name & address + gethostname(buff, MAXHOSTNAMELEN); + local = gethostbyname(buff); + myAddr = *(int *)local->h_addr_list[0]; + + // if the quake hostname isn't set, set it to the machine name +// if (Q_strcmp(hostname.string, "UNNAMED") == 0) + { + // see if it's a text IP address (well, close enough) + for (p = buff; *p; p++) + if ((*p < '0' || *p > '9') && *p != '.') + break; + + // if it is a real name, strip off the domain; we only want the host + if (*p) + { + for (i = 0; i < 15; i++) + if (buff[i] == '.') + break; + buff[i] = 0; + } +// Cvar_Set ("hostname", buff); + } + + if ((net_controlsocket = WINS_OpenSocket (0)) == -1) + WinError("WINS_Init: Unable to open control socket\n"); + + ((struct sockaddr_in *)&broadcastaddr)->sin_family = AF_INET; + ((struct sockaddr_in *)&broadcastaddr)->sin_addr.s_addr = INADDR_BROADCAST; + ((struct sockaddr_in *)&broadcastaddr)->sin_port = htons((u_short)net_hostport); + + WINS_GetSocketAddr (net_controlsocket, &addr); + strcpy(my_tcpip_address, WINS_AddrToString (&addr)); + p = strrchr (my_tcpip_address, ':'); + if (p) *p = 0; + WinPrint("Winsock Initialized\n"); + + return net_controlsocket; +} //end of the function WINS_Init +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +char *WINS_MyAddress(void) +{ + return my_tcpip_address; +} //end of the function WINS_MyAddress +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void WINS_Shutdown(void) +{ + //WINS_Listen(0); + WINS_CloseSocket(net_controlsocket); + WSACleanup(); + // + //WinPrint("Winsock Shutdown\n"); +} //end of the function WINS_Shutdown +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +/* +void WINS_Listen(int state) +{ + // enable listening + if (state) + { + if (net_acceptsocket != -1) + return; + if ((net_acceptsocket = WINS_OpenSocket (net_hostport)) == -1) + WinError ("WINS_Listen: Unable to open accept socket\n"); + return; + } + + // disable listening + if (net_acceptsocket == -1) + return; + WINS_CloseSocket (net_acceptsocket); + net_acceptsocket = -1; +} //end of the function WINS_Listen*/ +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WINS_OpenSocket(int port) +{ + int newsocket; + struct sockaddr_in address; + u_long _true = 1; + + if ((newsocket = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) + { + WinPrint("WINS_OpenSocket: %s\n", WINS_ErrorMessage(WSAGetLastError())); + return -1; + } //end if + + if (ioctlsocket (newsocket, FIONBIO, &_true) == -1) + { + WinPrint("WINS_OpenSocket: %s\n", WINS_ErrorMessage(WSAGetLastError())); + closesocket(newsocket); + return -1; + } //end if + + memset((char *) &address, 0, sizeof(address)); + address.sin_family = AF_INET; + address.sin_addr.s_addr = INADDR_ANY; + address.sin_port = htons((u_short)port); + if( bind (newsocket, (void *)&address, sizeof(address)) == -1) + { + WinPrint("WINS_OpenSocket: %s\n", WINS_ErrorMessage(WSAGetLastError())); + closesocket(newsocket); + return -1; + } //end if + + return newsocket; +} //end of the function WINS_OpenSocket +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WINS_OpenReliableSocket(int port) +{ + int newsocket; + struct sockaddr_in address; + BOOL _true = 0xFFFFFFFF; + + //IPPROTO_TCP + // + if ((newsocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) + { + WinPrint("WINS_OpenReliableSocket: %s\n", WINS_ErrorMessage(WSAGetLastError())); + return -1; + } //end if + + memset((char *) &address, 0, sizeof(address)); + address.sin_family = AF_INET; + address.sin_addr.s_addr = htonl(INADDR_ANY); + address.sin_port = htons((u_short)port); + if (bind(newsocket, (void *)&address, sizeof(address)) == -1) + { + WinPrint("WINS_OpenReliableSocket: %s\n", WINS_ErrorMessage(WSAGetLastError())); + closesocket(newsocket); + return -1; + } //end if + + // + if (setsockopt(newsocket, IPPROTO_TCP, TCP_NODELAY, (void *) &_true, sizeof(int)) == SOCKET_ERROR) + { + WinPrint("WINS_OpenReliableSocket: %s\n", WINS_ErrorMessage(WSAGetLastError())); + WinPrint("setsockopt error\n"); + } //end if + + return newsocket; +} //end of the function WINS_OpenReliableSocket +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WINS_Listen(int socket) +{ + u_long _true = 1; + + if (ioctlsocket(socket, FIONBIO, &_true) == -1) + { + WinPrint("WINS_Listen: %s\n", WINS_ErrorMessage(WSAGetLastError())); + return -1; + } //end if + if (listen(socket, SOMAXCONN) == SOCKET_ERROR) + { + WinPrint("WINS_Listen: %s\n", WINS_ErrorMessage(WSAGetLastError())); + return -1; + } //end if + return 0; +} //end of the function WINS_Listen +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WINS_Accept(int socket, struct sockaddr_s *addr) +{ + int addrlen = sizeof (struct sockaddr_s); + int newsocket; + BOOL _true = 1; + + newsocket = accept(socket, (struct sockaddr *)addr, &addrlen); + if (newsocket == INVALID_SOCKET) + { + if (WSAGetLastError() == WSAEWOULDBLOCK) return -1; + WinPrint("WINS_Accept: %s\n", WINS_ErrorMessage(WSAGetLastError())); + return -1; + } //end if + // + if (setsockopt(newsocket, IPPROTO_TCP, TCP_NODELAY, (void *) &_true, sizeof(int)) == SOCKET_ERROR) + { + WinPrint("WINS_Accept: %s\n", WINS_ErrorMessage(WSAGetLastError())); + WinPrint("setsockopt error\n"); + } //end if + return newsocket; +} //end of the function WINS_Accept +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WINS_CloseSocket(int socket) +{ + /* + if (socket == net_broadcastsocket) + net_broadcastsocket = 0; + */ +// shutdown(socket, SD_SEND); + + if (closesocket(socket) == SOCKET_ERROR) + { + WinPrint("WINS_CloseSocket: %s\n", WINS_ErrorMessage(WSAGetLastError())); + return SOCKET_ERROR; + } //end if + return 0; +} //end of the function WINS_CloseSocket +//=========================================================================== +// this lets you type only as much of the net address as required, using +// the local network components to fill in the rest +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +static int PartialIPAddress (char *in, struct sockaddr_s *hostaddr) +{ + char buff[256]; + char *b; + int addr; + int num; + int mask; + + buff[0] = '.'; + b = buff; + strcpy(buff+1, in); + if (buff[1] == '.') b++; + + addr = 0; + mask=-1; + while (*b == '.') + { + num = 0; + if (*++b < '0' || *b > '9') return -1; + while (!( *b < '0' || *b > '9')) + num = num*10 + *(b++) - '0'; + mask<<=8; + addr = (addr<<8) + num; + } + + hostaddr->sa_family = AF_INET; + ((struct sockaddr_in *)hostaddr)->sin_port = htons((u_short)net_hostport); + ((struct sockaddr_in *)hostaddr)->sin_addr.s_addr = (myAddr & htonl(mask)) | htonl(addr); + + return 0; +} //end of the function PartialIPAddress +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WINS_Connect(int socket, struct sockaddr_s *addr) +{ + int ret; + u_long _true2 = 0xFFFFFFFF; + + ret = connect(socket, (struct sockaddr *)addr, sizeof(struct sockaddr_s)); + if (ret == SOCKET_ERROR) + { + WinPrint("WINS_Connect: %s\n", WINS_ErrorMessage(WSAGetLastError())); + return -1; + } //end if + if (ioctlsocket(socket, FIONBIO, &_true2) == -1) + { + WinPrint("WINS_Connect: %s\n", WINS_ErrorMessage(WSAGetLastError())); + return -1; + } //end if + return 0; +} //end of the function WINS_Connect +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WINS_CheckNewConnections(void) +{ + char buf[4]; + + if (net_acceptsocket == -1) + return -1; + + if (recvfrom(net_acceptsocket, buf, 4, MSG_PEEK, NULL, NULL) > 0) + return net_acceptsocket; + return -1; +} //end of the function WINS_CheckNewConnections +//=========================================================================== +// returns the number of bytes read +// 0 if no bytes available +// -1 on failure +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WINS_Read(int socket, byte *buf, int len, struct sockaddr_s *addr) +{ + int addrlen = sizeof (struct sockaddr_s); + int ret, errno; + + if (addr) + { + ret = recvfrom(socket, buf, len, 0, (struct sockaddr *)addr, &addrlen); + if (ret == -1) + { + errno = WSAGetLastError(); + + if (errno == WSAEWOULDBLOCK || errno == WSAECONNREFUSED) + return 0; + } //end if + } //end if + else + { + ret = recv(socket, buf, len, 0); + if (ret == SOCKET_ERROR) + { + errno = WSAGetLastError(); + + if (errno == WSAEWOULDBLOCK || errno == WSAECONNREFUSED) + return 0; + } //end if + } //end else + if (ret == SOCKET_ERROR) + { + WinPrint("WINS_Read: %s\n", WINS_ErrorMessage(WSAGetLastError())); + } //end if + return ret; +} //end of the function WINS_Read +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WINS_MakeSocketBroadcastCapable (int socket) +{ + int i = 1; + + // make this socket broadcast capable + if (setsockopt(socket, SOL_SOCKET, SO_BROADCAST, (char *)&i, sizeof(i)) < 0) + return -1; + net_broadcastsocket = socket; + + return 0; +} //end of the function WINS_MakeSocketBroadcastCapable +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WINS_Broadcast (int socket, byte *buf, int len) +{ + int ret; + + if (socket != net_broadcastsocket) + { + if (net_broadcastsocket != 0) + WinError("Attempted to use multiple broadcasts sockets\n"); + ret = WINS_MakeSocketBroadcastCapable (socket); + if (ret == -1) + { + WinPrint("Unable to make socket broadcast capable\n"); + return ret; + } + } + + return WINS_Write (socket, buf, len, &broadcastaddr); +} //end of the function WINS_Broadcast +//=========================================================================== +// returns qtrue on success or qfalse on failure +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WINS_Write(int socket, byte *buf, int len, struct sockaddr_s *addr) +{ + int ret, written; + + if (addr) + { + written = 0; + while(written < len) + { + ret = sendto (socket, &buf[written], len-written, 0, (struct sockaddr *)addr, sizeof(struct sockaddr_s)); + if (ret == SOCKET_ERROR) + { + if (WSAGetLastError() != WSAEWOULDBLOCK) + return qfalse; + Sleep(1000); + } //end if + else + { + written += ret; + } + } + } //end if + else + { + written = 0; + while(written < len) + { + ret = send(socket, buf, len, 0); + if (ret == SOCKET_ERROR) + { + if (WSAGetLastError() != WSAEWOULDBLOCK) + return qfalse; + Sleep(1000); + } //end if + else + { + written += ret; + } + } + } //end else + if (ret == SOCKET_ERROR) + { + WinPrint("WINS_Write: %s\n", WINS_ErrorMessage(WSAGetLastError())); + } //end if + return (ret == len); +} //end of the function WINS_Write +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +char *WINS_AddrToString (struct sockaddr_s *addr) +{ + static char buffer[22]; + int haddr; + + haddr = ntohl(((struct sockaddr_in *)addr)->sin_addr.s_addr); + sprintf(buffer, "%d.%d.%d.%d:%d", (haddr >> 24) & 0xff, (haddr >> 16) & 0xff, (haddr >> 8) & 0xff, haddr & 0xff, ntohs(((struct sockaddr_in *)addr)->sin_port)); + return buffer; +} //end of the function WINS_AddrToString +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WINS_StringToAddr(char *string, struct sockaddr_s *addr) +{ + int ha1, ha2, ha3, ha4, hp; + int ipaddr; + + sscanf(string, "%d.%d.%d.%d:%d", &ha1, &ha2, &ha3, &ha4, &hp); + ipaddr = (ha1 << 24) | (ha2 << 16) | (ha3 << 8) | ha4; + + addr->sa_family = AF_INET; + ((struct sockaddr_in *)addr)->sin_addr.s_addr = htonl(ipaddr); + ((struct sockaddr_in *)addr)->sin_port = htons((u_short)hp); + return 0; +} //end of the function WINS_StringToAddr +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WINS_GetSocketAddr(int socket, struct sockaddr_s *addr) +{ + int addrlen = sizeof(struct sockaddr_s); + unsigned int a; + + memset(addr, 0, sizeof(struct sockaddr_s)); + getsockname(socket, (struct sockaddr *)addr, &addrlen); + a = ((struct sockaddr_in *)addr)->sin_addr.s_addr; + if (a == 0 || a == inet_addr("127.0.0.1")) + ((struct sockaddr_in *)addr)->sin_addr.s_addr = myAddr; + + return 0; +} //end of the function WINS_GetSocketAddr +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WINS_GetNameFromAddr (struct sockaddr_s *addr, char *name) +{ + struct hostent *hostentry; + + hostentry = gethostbyaddr ((char *)&((struct sockaddr_in *)addr)->sin_addr, sizeof(struct in_addr), AF_INET); + if (hostentry) + { + strncpy (name, (char *)hostentry->h_name, NET_NAMELEN - 1); + return 0; + } + + strcpy (name, WINS_AddrToString (addr)); + return 0; +} //end of the function WINS_GetNameFromAddr +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WINS_GetAddrFromName(char *name, struct sockaddr_s *addr) +{ + struct hostent *hostentry; + + if (name[0] >= '0' && name[0] <= '9') + return PartialIPAddress (name, addr); + + hostentry = gethostbyname (name); + if (!hostentry) + return -1; + + addr->sa_family = AF_INET; + ((struct sockaddr_in *)addr)->sin_port = htons((u_short)net_hostport); + ((struct sockaddr_in *)addr)->sin_addr.s_addr = *(int *)hostentry->h_addr_list[0]; + + return 0; +} //end of the function WINS_GetAddrFromName +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WINS_AddrCompare (struct sockaddr_s *addr1, struct sockaddr_s *addr2) +{ + if (addr1->sa_family != addr2->sa_family) + return -1; + + if (((struct sockaddr_in *)addr1)->sin_addr.s_addr != ((struct sockaddr_in *)addr2)->sin_addr.s_addr) + return -1; + + if (((struct sockaddr_in *)addr1)->sin_port != ((struct sockaddr_in *)addr2)->sin_port) + return 1; + + return 0; +} //end of the function WINS_AddrCompare +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WINS_GetSocketPort (struct sockaddr_s *addr) +{ + return ntohs(((struct sockaddr_in *)addr)->sin_port); +} //end of the function WINS_GetSocketPort +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int WINS_SetSocketPort (struct sockaddr_s *addr, int port) +{ + ((struct sockaddr_in *)addr)->sin_port = htons((u_short)port); + return 0; +} //end of the function WINS_SetSocketPort diff --git a/tools/urt/libs/l_net/l_net_wins.h b/tools/urt/libs/l_net/l_net_wins.h new file mode 100644 index 00000000..e9680888 --- /dev/null +++ b/tools/urt/libs/l_net/l_net_wins.h @@ -0,0 +1,55 @@ +/* +This code is based on source provided under the terms of the Id Software +LIMITED USE SOFTWARE LICENSE AGREEMENT, a copy of which is included with the +GtkRadiant sources (see LICENSE_ID). If you did not receive a copy of +LICENSE_ID, please contact Id Software immediately at info@idsoftware.com. + +All changes and additions to the original source which have been developed by +other contributors (see CONTRIBUTORS) are provided under the terms of the +license the contributors choose (see LICENSE), to the extent permitted by the +LICENSE_ID. If you did not receive a copy of the contributor license, +please contact the GtkRadiant maintainers at info@gtkradiant.com immediately. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +//=========================================================================== +// +// Name: l_net_wins.h +// Function: WinSock +// Programmer: MrElusive +// Last update: TTimo: cross-platform version, l_net library +// Tab Size: 3 +// Notes: +//=========================================================================== + +int WINS_Init(void); +void WINS_Shutdown(void); +char *WINS_MyAddress(void); +int WINS_Listen(int socket); +int WINS_Accept(int socket, struct sockaddr_s *addr); +int WINS_OpenSocket(int port); +int WINS_OpenReliableSocket(int port); +int WINS_CloseSocket(int socket); +int WINS_Connect (int socket, struct sockaddr_s *addr); +int WINS_CheckNewConnections(void); +int WINS_Read(int socket, byte *buf, int len, struct sockaddr_s *addr); +int WINS_Write(int socket, byte *buf, int len, struct sockaddr_s *addr); +int WINS_Broadcast (int socket, byte *buf, int len); +char *WINS_AddrToString (struct sockaddr_s *addr); +int WINS_StringToAddr (char *string, struct sockaddr_s *addr); +int WINS_GetSocketAddr (int socket, struct sockaddr_s *addr); +int WINS_GetNameFromAddr (struct sockaddr_s *addr, char *name); +int WINS_GetAddrFromName (char *name, struct sockaddr_s *addr); +int WINS_AddrCompare (struct sockaddr_s *addr1, struct sockaddr_s *addr2); +int WINS_GetSocketPort (struct sockaddr_s *addr); +int WINS_SetSocketPort (struct sockaddr_s *addr, int port); diff --git a/tools/urt/libs/libs.vcproj b/tools/urt/libs/libs.vcproj new file mode 100644 index 00000000..71b155a3 --- /dev/null +++ b/tools/urt/libs/libs.vcproj @@ -0,0 +1,592 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/urt/libs/maplib.cpp b/tools/urt/libs/maplib.cpp new file mode 100644 index 00000000..2dfa6985 --- /dev/null +++ b/tools/urt/libs/maplib.cpp @@ -0,0 +1,2 @@ + +#include "maplib.h" diff --git a/tools/urt/libs/maplib.h b/tools/urt/libs/maplib.h new file mode 100644 index 00000000..94950c5b --- /dev/null +++ b/tools/urt/libs/maplib.h @@ -0,0 +1,262 @@ + +#if !defined (INCLUDED_MAPLIB_H) +#define INCLUDED_MAPLIB_H + +#include "nameable.h" +#include "mapfile.h" + +#include "traverselib.h" +#include "transformlib.h" +#include "scenelib.h" +#include "string/string.h" +#include "instancelib.h" +#include "selectionlib.h" +#include "generic/callback.h" + + +class NameableString : public Nameable +{ + CopiedString m_name; +public: + NameableString(const char* name) + : m_name(name) + { + } + + const char* name() const + { + return m_name.c_str(); + } + void attach(const NameCallback& callback) + { + } + void detach(const NameCallback& callback) + { + } +}; + + +class UndoFileChangeTracker : public UndoTracker, public MapFile +{ + std::size_t m_size; + std::size_t m_saved; + typedef void (UndoFileChangeTracker::*Pending)(); + Pending m_pending; + Callback m_changed; + +public: + UndoFileChangeTracker() : m_size(0), m_saved(MAPFILE_MAX_CHANGES), m_pending(0) + { + } + void print() + { + globalOutputStream() << "saved: " << Unsigned(m_saved) << " size: " << Unsigned(m_size) << "\n"; + } + + void push() + { + ++m_size; + m_changed(); + //print(); + } + void pop() + { + --m_size; + m_changed(); + //print(); + } + void pushOperation() + { + if(m_size < m_saved) + { + // redo queue has been flushed.. it is now impossible to get back to the saved state via undo/redo + m_saved = MAPFILE_MAX_CHANGES; + } + push(); + } + void clear() + { + m_size = 0; + m_changed(); + //print(); + } + void begin() + { + m_pending = Pending(&UndoFileChangeTracker::pushOperation); + } + void undo() + { + m_pending = Pending(&UndoFileChangeTracker::pop); + } + void redo() + { + m_pending = Pending(&UndoFileChangeTracker::push); + } + + void changed() + { + if(m_pending != 0) + { + ((*this).*m_pending)(); + m_pending = 0; + } + } + + void save() + { + m_saved = m_size; + m_changed(); + } + bool saved() const + { + return m_saved == m_size; + } + + void setChangedCallback(const Callback& changed) + { + m_changed = changed; + m_changed(); + } + + std::size_t changes() const + { + return m_size; + } +}; + + +class MapRoot : public scene::Node::Symbiot, public scene::Instantiable, public scene::Traversable::Observer +{ + class TypeCasts + { + NodeTypeCastTable m_casts; + public: + TypeCasts() + { + NodeStaticCast::install(m_casts); + NodeContainedCast::install(m_casts); + NodeContainedCast::install(m_casts); + NodeContainedCast::install(m_casts); + NodeContainedCast::install(m_casts); + } + NodeTypeCastTable& get() + { + return m_casts; + } + }; + + scene::Node m_node; + IdentityTransform m_transform; + TraversableNodeSet m_traverse; + Instances m_instances; + typedef SelectableInstance Instance; + NameableString m_name; + UndoFileChangeTracker m_changeTracker; +public: + typedef LazyStatic StaticTypeCasts; + + scene::Traversable& get(NullType) + { + return m_traverse; + } + Transformable& get(NullType) + { + return m_transform; + } + Nameable& get(NullType) + { + return m_name; + } + MapFile& get(NullType) + { + return m_changeTracker; + } + + MapRoot(const char* name) : m_node(this, this, StaticTypeCasts::instance().get()), m_name(name) + { + m_node.m_isRoot = true; + + m_traverse.attach(this); + + GlobalUndoSystem().trackerAttach(m_changeTracker); + } + ~MapRoot() + { + } + void release() + { + GlobalUndoSystem().trackerDetach(m_changeTracker); + + m_traverse.detach(this); + delete this; + } + scene::Node& node() + { + return m_node; + } + + InstanceCounter m_instanceCounter; + void instanceAttach(const scene::Path& path) + { + if(++m_instanceCounter.m_count == 1) + { + m_traverse.instanceAttach(path_find_mapfile(path.begin(), path.end())); + } + } + void instanceDetach(const scene::Path& path) + { + if(--m_instanceCounter.m_count == 0) + { + m_traverse.instanceDetach(path_find_mapfile(path.begin(), path.end())); + } + } + + void insert(scene::Node& child) + { + m_instances.insert(child); + } + void erase(scene::Node& child) + { + m_instances.erase(child); + } + + scene::Node& clone() const + { + return (new MapRoot(*this))->node(); + } + + scene::Instance* create(const scene::Path& path, scene::Instance* parent) + { + return new Instance(path, parent); + } + void forEachInstance(const scene::Instantiable::Visitor& visitor) + { + m_instances.forEachInstance(visitor); + } + void insert(scene::Instantiable::Observer* observer, const scene::Path& path, scene::Instance* instance) + { + m_instances.insert(observer, path, instance); + instanceAttach(path); + } + scene::Instance* erase(scene::Instantiable::Observer* observer, const scene::Path& path) + { + instanceDetach(path); + return m_instances.erase(observer, path); + } +}; + +inline void MapRoot_construct() +{ +} + +inline void MapRoot_destroy() +{ +} + +inline NodeSmartReference NewMapRoot(const char* name) +{ + return NodeSmartReference((new MapRoot(name))->node()); +} + + +#endif diff --git a/tools/urt/libs/math/aabb.cpp b/tools/urt/libs/math/aabb.cpp new file mode 100644 index 00000000..c53006ec --- /dev/null +++ b/tools/urt/libs/math/aabb.cpp @@ -0,0 +1,3 @@ + +#include "aabb.h" + diff --git a/tools/urt/libs/math/aabb.h b/tools/urt/libs/math/aabb.h new file mode 100644 index 00000000..ea6cc9f0 --- /dev/null +++ b/tools/urt/libs/math/aabb.h @@ -0,0 +1,281 @@ + +#if !defined(INCLUDED_MATH_AABB_H) +#define INCLUDED_MATH_AABB_H + +/// \file +/// \brief Axis-aligned bounding-box data types and related operations. + +#include "math/matrix.h" +#include "math/plane.h" + +class AABB +{ +public: + Vector3 origin, extents; + + AABB() : origin(0, 0, 0), extents(-1,-1,-1) + { + } + AABB(const Vector3& origin_, const Vector3& extents_) : + origin(origin_), extents(extents_) + { + } +}; + +const float c_aabb_max = FLT_MAX; + +inline bool extents_valid(float f) +{ + return f >= 0.0f && f <= c_aabb_max; +} + +inline bool origin_valid(float f) +{ + return f >= -c_aabb_max && f <= c_aabb_max; +} + +inline bool aabb_valid(const AABB& aabb) +{ + return origin_valid(aabb.origin[0]) + && origin_valid(aabb.origin[1]) + && origin_valid(aabb.origin[2]) + && extents_valid(aabb.extents[0]) + && extents_valid(aabb.extents[1]) + && extents_valid(aabb.extents[2]); +} + +inline AABB aabb_for_minmax(const Vector3& min, const Vector3& max) +{ + AABB aabb; + aabb.origin = vector3_mid(min, max); + aabb.extents = vector3_subtracted(max, aabb.origin); + return aabb; +} + +template +class AABBExtend +{ +public: + static void apply(AABB& aabb, const Vector3& point) + { + float displacement = point[Index::value] - aabb.origin[Index::value]; + float half_difference = static_cast(0.5 * (fabs(displacement) - aabb.extents[Index::value])); + if(half_difference > 0.0f) + { + aabb.origin[Index::value] += (displacement >= 0.0f) ? half_difference : -half_difference; + aabb.extents[Index::value] += half_difference; + } + } + static void apply(AABB& aabb, const AABB& other) + { + float displacement = other.origin[Index::value] - aabb.origin[Index::value]; + float difference = other.extents[Index::value] - aabb.extents[Index::value]; + if(fabs(displacement) > fabs(difference)) + { + float half_difference = static_cast(0.5 * (fabs(displacement) + difference)); + if(half_difference > 0.0f) + { + aabb.origin[Index::value] += (displacement >= 0.0f) ? half_difference : -half_difference; + aabb.extents[Index::value] += half_difference; + } + } + else if(difference > 0.0f) + { + aabb.origin[Index::value] = other.origin[Index::value]; + aabb.extents[Index::value] = other.extents[Index::value]; + } + } +}; + +inline void aabb_extend_by_point(AABB& aabb, const Vector3& point) +{ + AABBExtend< ct_int<0> >::apply(aabb, point); + AABBExtend< ct_int<1> >::apply(aabb, point); + AABBExtend< ct_int<2> >::apply(aabb, point); +} + +inline void aabb_extend_by_point_safe(AABB& aabb, const Vector3& point) +{ + if(aabb_valid(aabb)) + { + aabb_extend_by_point(aabb, point); + } + else + { + aabb.origin = point; + aabb.extents = Vector3(0, 0, 0); + } +} + +class AABBExtendByPoint +{ + AABB& m_aabb; +public: + AABBExtendByPoint(AABB& aabb) : m_aabb(aabb) + { + } + void operator()(const Vector3& point) const + { + aabb_extend_by_point_safe(m_aabb, point); + } +}; + +inline void aabb_extend_by_aabb(AABB& aabb, const AABB& other) +{ + AABBExtend< ct_int<0> >::apply(aabb, other); + AABBExtend< ct_int<1> >::apply(aabb, other); + AABBExtend< ct_int<2> >::apply(aabb, other); +} + +inline void aabb_extend_by_aabb_safe(AABB& aabb, const AABB& other) +{ + if(aabb_valid(aabb) && aabb_valid(other)) + { + aabb_extend_by_aabb(aabb, other); + } + else if(aabb_valid(other)) + { + aabb = other; + } +} + +inline void aabb_extend_by_vec3(AABB& aabb, const Vector3& extension) +{ + vector3_add(aabb.extents, extension); +} + + + + +template +inline bool aabb_intersects_point_dimension(const AABB& aabb, const Vector3& point) +{ + return fabs(point[Index::value] - aabb.origin[Index::value]) < aabb.extents[Index::value]; +} + +inline bool aabb_intersects_point(const AABB& aabb, const Vector3& point) +{ + return aabb_intersects_point_dimension< ct_int<0> >(aabb, point) + && aabb_intersects_point_dimension< ct_int<1> >(aabb, point) + && aabb_intersects_point_dimension< ct_int<2> >(aabb, point); +} + +template +inline bool aabb_intersects_aabb_dimension(const AABB& aabb, const AABB& other) +{ + return fabs(other.origin[Index::value] - aabb.origin[Index::value]) < (aabb.extents[Index::value] + other.extents[Index::value]); +} + +inline bool aabb_intersects_aabb(const AABB& aabb, const AABB& other) +{ + return aabb_intersects_aabb_dimension< ct_int<0> >(aabb, other) + && aabb_intersects_aabb_dimension< ct_int<1> >(aabb, other) + && aabb_intersects_aabb_dimension< ct_int<2> >(aabb, other); +} + +inline unsigned int aabb_classify_plane(const AABB& aabb, const Plane3& plane) +{ + double distance_origin = vector3_dot(plane.normal(), aabb.origin) + plane.dist(); + + if(fabs(distance_origin) < (fabs(plane.a * aabb.extents[0]) + + fabs(plane.b * aabb.extents[1]) + + fabs(plane.c * aabb.extents[2]))) + { + return 1; // partially inside + } + else if (distance_origin < 0) + { + return 2; // totally inside + } + return 0; // totally outside +} + +inline unsigned int aabb_oriented_classify_plane(const AABB& aabb, const Matrix4& transform, const Plane3& plane) +{ + double distance_origin = vector3_dot(plane.normal(), aabb.origin) + plane.dist(); + + if(fabs(distance_origin) < (fabs(aabb.extents[0] * vector3_dot(plane.normal(), vector4_to_vector3(transform.x()))) + + fabs(aabb.extents[1] * vector3_dot(plane.normal(), vector4_to_vector3(transform.y()))) + + fabs(aabb.extents[2] * vector3_dot(plane.normal(), vector4_to_vector3(transform.z()))))) + { + return 1; // partially inside + } + else if (distance_origin < 0) + { + return 2; // totally inside + } + return 0; // totally outside +} + +inline void aabb_corners(const AABB& aabb, Vector3 corners[8]) +{ + Vector3 min(vector3_subtracted(aabb.origin, aabb.extents)); + Vector3 max(vector3_added(aabb.origin, aabb.extents)); + corners[0] = Vector3(min[0], max[1], max[2]); + corners[1] = Vector3(max[0], max[1], max[2]); + corners[2] = Vector3(max[0], min[1], max[2]); + corners[3] = Vector3(min[0], min[1], max[2]); + corners[4] = Vector3(min[0], max[1], min[2]); + corners[5] = Vector3(max[0], max[1], min[2]); + corners[6] = Vector3(max[0], min[1], min[2]); + corners[7] = Vector3(min[0], min[1], min[2]); +} + +inline void aabb_planes(const AABB& aabb, Plane3 planes[6]) +{ + planes[0] = Plane3(g_vector3_axes[0], aabb.origin[0] + aabb.extents[0]); + planes[1] = Plane3(vector3_negated(g_vector3_axes[0]), -(aabb.origin[0] - aabb.extents[0])); + planes[2] = Plane3(g_vector3_axes[1], aabb.origin[1] + aabb.extents[1]); + planes[3] = Plane3(vector3_negated(g_vector3_axes[1]), -(aabb.origin[1] - aabb.extents[1])); + planes[4] = Plane3(g_vector3_axes[2], aabb.origin[2] + aabb.extents[2]); + planes[5] = Plane3(vector3_negated(g_vector3_axes[2]), -(aabb.origin[2] - aabb.extents[2])); +} + +const Vector3 aabb_normals[6] = { + Vector3( 1, 0, 0 ), + Vector3( 0, 1, 0 ), + Vector3( 0, 0, 1 ), + Vector3(-1, 0, 0 ), + Vector3( 0,-1, 0 ), + Vector3( 0, 0,-1 ), +}; + +const float aabb_texcoord_topleft[2] = { 0, 0 }; +const float aabb_texcoord_topright[2] = { 1, 0 }; +const float aabb_texcoord_botleft[2] = { 0, 1 }; +const float aabb_texcoord_botright[2] = { 1, 1 }; + + +inline AABB aabb_for_oriented_aabb(const AABB& aabb, const Matrix4& transform) +{ + return AABB( + matrix4_transformed_point(transform, aabb.origin), + Vector3( + static_cast(fabs(transform[0] * aabb.extents[0]) + + fabs(transform[4] * aabb.extents[1]) + + fabs(transform[8] * aabb.extents[2])), + static_cast(fabs(transform[1] * aabb.extents[0]) + + fabs(transform[5] * aabb.extents[1]) + + fabs(transform[9] * aabb.extents[2])), + static_cast(fabs(transform[2] * aabb.extents[0]) + + fabs(transform[6] * aabb.extents[1]) + + fabs(transform[10] * aabb.extents[2])) + ) + ); +} + +inline AABB aabb_for_oriented_aabb_safe(const AABB& aabb, const Matrix4& transform) +{ + if(aabb_valid(aabb)) + { + return aabb_for_oriented_aabb(aabb, transform); + } + return aabb; +} + +inline AABB aabb_infinite() +{ + return AABB(Vector3(0, 0, 0), Vector3(c_aabb_max, c_aabb_max, c_aabb_max)); +} + +#endif diff --git a/tools/urt/libs/math/curve.cpp b/tools/urt/libs/math/curve.cpp new file mode 100644 index 00000000..263ca5a3 --- /dev/null +++ b/tools/urt/libs/math/curve.cpp @@ -0,0 +1,3 @@ + +#include "curve.h" + diff --git a/tools/urt/libs/math/curve.h b/tools/urt/libs/math/curve.h new file mode 100644 index 00000000..17de838b --- /dev/null +++ b/tools/urt/libs/math/curve.h @@ -0,0 +1,253 @@ + +#if !defined(INCLUDED_MATH_CURVE_H) +#define INCLUDED_MATH_CURVE_H + +/// \file +/// \brief Curve data types and related operations. + +#include "debugging/debugging.h" +#include "container/array.h" +#include + + +template +struct BernsteinPolynomial +{ + static double apply(double t) + { + return 1; // general case not implemented + } +}; + +typedef ct_int<0> Zero; +typedef ct_int<1> One; +typedef ct_int<2> Two; +typedef ct_int<3> Three; +typedef ct_int<4> Four; + +template<> +struct BernsteinPolynomial +{ + static double apply(double t) + { + return 1; + } +}; + +template<> +struct BernsteinPolynomial +{ + static double apply(double t) + { + return 1 - t; + } +}; + +template<> +struct BernsteinPolynomial +{ + static double apply(double t) + { + return t; + } +}; + +template<> +struct BernsteinPolynomial +{ + static double apply(double t) + { + return (1 - t) * (1 - t); + } +}; + +template<> +struct BernsteinPolynomial +{ + static double apply(double t) + { + return 2 * (1 - t) * t; + } +}; + +template<> +struct BernsteinPolynomial +{ + static double apply(double t) + { + return t * t; + } +}; + +template<> +struct BernsteinPolynomial +{ + static double apply(double t) + { + return (1 - t) * (1 - t) * (1 - t); + } +}; + +template<> +struct BernsteinPolynomial +{ + static double apply(double t) + { + return 3 * (1 - t) * (1 - t) * t; + } +}; + +template<> +struct BernsteinPolynomial +{ + static double apply(double t) + { + return 3 * (1 - t) * t * t; + } +}; + +template<> +struct BernsteinPolynomial +{ + static double apply(double t) + { + return t * t * t; + } +}; + +typedef Array ControlPoints; + +inline Vector3 CubicBezier_evaluate(const Vector3* firstPoint, double t) +{ + Vector3 result(0, 0, 0); + double denominator = 0; + + { + double weight = BernsteinPolynomial::apply(t); + result += vector3_scaled(*firstPoint++, weight); + denominator += weight; + } + { + double weight = BernsteinPolynomial::apply(t); + result += vector3_scaled(*firstPoint++, weight); + denominator += weight; + } + { + double weight = BernsteinPolynomial::apply(t); + result += vector3_scaled(*firstPoint++, weight); + denominator += weight; + } + { + double weight = BernsteinPolynomial::apply(t); + result += vector3_scaled(*firstPoint++, weight); + denominator += weight; + } + + return result / denominator; +} + +inline Vector3 CubicBezier_evaluateMid(const Vector3* firstPoint) +{ + return vector3_scaled(firstPoint[0], 0.125) + + vector3_scaled(firstPoint[1], 0.375) + + vector3_scaled(firstPoint[2], 0.375) + + vector3_scaled(firstPoint[3], 0.125); +} + +inline Vector3 CatmullRom_evaluate(const ControlPoints& controlPoints, double t) +{ + // scale t to be segment-relative + t *= double(controlPoints.size() - 1); + + // subtract segment index; + std::size_t segment = 0; + for(std::size_t i = 0; i < controlPoints.size() - 1; ++i) + { + if(t <= double(i+1)) + { + segment = i; + break; + } + } + t -= segment; + + const double reciprocal_alpha = 1.0 / 3.0; + + Vector3 bezierPoints[4]; + bezierPoints[0] = controlPoints[segment]; + bezierPoints[1] = (segment > 0) + ? controlPoints[segment] + vector3_scaled(controlPoints[segment + 1] - controlPoints[segment - 1], reciprocal_alpha * 0.5) + : controlPoints[segment] + vector3_scaled(controlPoints[segment + 1] - controlPoints[segment], reciprocal_alpha); + bezierPoints[2] = (segment < controlPoints.size() - 2) + ? controlPoints[segment + 1] + vector3_scaled(controlPoints[segment] - controlPoints[segment + 2], reciprocal_alpha * 0.5) + : controlPoints[segment + 1] + vector3_scaled(controlPoints[segment] - controlPoints[segment + 1], reciprocal_alpha); + bezierPoints[3] = controlPoints[segment + 1]; + return CubicBezier_evaluate(bezierPoints, t); +} + +typedef Array Knots; + +inline double BSpline_basis(const Knots& knots, std::size_t i, std::size_t degree, double t) +{ + if(degree == 0) + { + if(knots[i] <= t + && t < knots[i + 1] + && knots[i] < knots[i + 1]) + { + return 1; + } + return 0; + } + double leftDenom = knots[i + degree] - knots[i]; + double left = (leftDenom == 0) ? 0 : ((t - knots[i]) / leftDenom) * BSpline_basis(knots, i, degree - 1, t); + double rightDenom = knots[i + degree + 1] - knots[i + 1]; + double right = (rightDenom == 0) ? 0 : ((knots[i + degree + 1] - t) / rightDenom) * BSpline_basis(knots, i + 1, degree - 1, t); + return left + right; +} + +inline Vector3 BSpline_evaluate(const ControlPoints& controlPoints, const Knots& knots, std::size_t degree, double t) +{ + Vector3 result(0, 0, 0); + for(std::size_t i = 0; i < controlPoints.size(); ++i) + { + result += vector3_scaled(controlPoints[i], BSpline_basis(knots, i, degree, t)); + } + return result; +} + +typedef Array NURBSWeights; + +inline Vector3 NURBS_evaluate(const ControlPoints& controlPoints, const NURBSWeights& weights, const Knots& knots, std::size_t degree, double t) +{ + Vector3 result(0, 0, 0); + double denominator = 0; + for(std::size_t i = 0; i < controlPoints.size(); ++i) + { + double weight = weights[i] * BSpline_basis(knots, i, degree, t); + result += vector3_scaled(controlPoints[i], weight); + denominator += weight; + } + return result / denominator; +} + +inline void KnotVector_openUniform(Knots& knots, std::size_t count, std::size_t degree) +{ + knots.resize(count + degree + 1); + + std::size_t equalKnots = 1; + + for(std::size_t i = 0; i < equalKnots; ++i) + { + knots[i] = 0; + knots[knots.size() - (i + 1)] = 1; + } + + std::size_t difference = knots.size() - 2 * (equalKnots); + for(std::size_t i = 0; i < difference; ++i) + { + knots[i + equalKnots] = Knots::value_type(double(i + 1) * 1.0 / double(difference + 1)); + } +} + +#endif diff --git a/tools/urt/libs/math/expression.cpp b/tools/urt/libs/math/expression.cpp new file mode 100644 index 00000000..ba0cc3c8 --- /dev/null +++ b/tools/urt/libs/math/expression.cpp @@ -0,0 +1,223 @@ + +#include "expression.h" + +Vector3 testAdded1(const Vector3& a, const Vector3& b) +{ + return vector3_added(a, vector3_added(a, b)); +} + +Vector3 testAdded2(const Vector3& a, const Vector3& b) +{ + return vector3_for_expression( vector_added( vector3_identity(a), vector_added( vector3_identity(a), vector3_identity(b) ) ) ); +} + +Vector3 testMultiplied1(const Vector3& a, const Vector3& b) +{ + return vector3_scaled(a, b); +} + +Vector3 testMultiplied2(const Vector3& a, const Vector3& b) +{ + return vector3_for_expression( vector_multiplied( vector3_identity(a), vector3_identity(b) ) ); +} + +Vector3 testCross1(const Vector3& a, const Vector3& b) +{ + return vector3_cross(a, b); +} + +Vector3 testCross2(const Vector3& a, const Vector3& b) +{ + return vector3_for_expression( vector_cross( vector3_identity(a), vector3_identity(b) ) ); +} + +double testDot1(const Vector3& a, const Vector3& b) +{ + return vector3_dot(a, b); +} + +double testDot2(const Vector3& a, const Vector3& b) +{ + return float_for_expression( vector_dot( vector3_identity(a), vector3_identity(b) ) ); +} + +double testLength1(const Vector3& a) +{ + return vector3_length(a); +} + +double testLength2(const Vector3& a) +{ + return float_for_expression( vector_length( vector3_identity(a) ) ); +} + +Vector3 testNormalised1(const Vector3& a) +{ + return vector3_normalised(a); +} + +Vector3 testNormalised2(const Vector3& a) +{ + return vector3_for_expression( vector_normalised( vector3_identity(a) ) ); +} + +Vector3 testNegated1(const Vector3& a) +{ + return vector3_negated(a); +} + +Vector3 testNegated2(const Vector3& a) +{ + return vector3_for_expression( vector_negated( vector3_identity(a) ) ); +} + +Vector3 testScaled1(const Vector3& a, const double& b) +{ + return vector3_scaled(a, b); +} + +Vector3 testScaled2(const Vector3& a, const double& b) +{ + return vector3_for_expression( vector_scaled( vector3_identity(a), float_literal(b) ) ); +} + +Vector3 testMatrixMultiplied1(const Vector3& a, const Matrix4& b) +{ + return matrix4_transformed_point(b, vector3_added(a, Vector3(1, 0, 0))); +} + +Vector3 testMatrixMultiplied2(const Vector3& a, const Matrix4& b) +{ + return vector3_for_expression( + point_multiplied( + vector_added( + vector3_identity(a), + vector3_literal(Vector3(1, 0, 0)) + ), + matrix4_identity(b) + ) + ); +} + +Matrix4 testMatrix4Multiplied1(const Matrix4& a, const Matrix4& b) +{ + return matrix4_multiplied_by_matrix4(a, matrix4_multiplied_by_matrix4(a, b)); +} + +Matrix4 testMatrix4Multiplied2(const Matrix4& a, const Matrix4& b) +{ + return matrix4_for_expression( + matrix4_multiplied( + matrix4_identity(a), + matrix4_identity(b) + ) + ); +} + +Matrix4 testMatrix4AffineMultiplied1(const Matrix4& a, const Matrix4& b) +{ + return matrix4_affine_multiplied_by_matrix4(a, b); +} + +Matrix4 testMatrix4AffineMultiplied2(const Matrix4& a, const Matrix4& b) +{ + return matrix4_affine_for_expression( + matrix4_multiplied( + matrix4_identity(a), + matrix4_identity(b) + ) + ); +} + +Matrix4 testMatrix4MultipliedConstant1(const Matrix4& a) +{ + return matrix4_multiplied_by_matrix4(a, g_matrix4_identity); +} + +Matrix4 testMatrix4MultipliedConstant2(const Matrix4& a) +{ + return matrix4_for_expression( + matrix4_multiplied( + matrix4_identity(a), + matrix4_identity(g_matrix4_identity) + ) + ); +} +Matrix4 testMatrix4Transposed1(const Matrix4& a) +{ + return matrix4_transposed(a); +} + +Matrix4 testMatrix4Transposed2(const Matrix4& a) +{ + return matrix4_for_expression( matrix_transposed( matrix4_identity(a) ) ); +} + +Vector3 testMulti1(const Matrix4& a, const Vector3& b, const Vector3& c) +{ + return vector3_added(matrix4_transformed_point(matrix4_transposed(a), b), c); +} + +Vector3 testMulti2(const Matrix4& a, const Vector3& b, const Vector3& c) +{ + return vector3_for_expression( + vector_added( + point_multiplied( + vector3_identity(b), + matrix_transposed(matrix4_identity(a)) + ), + vector3_identity(c) + ) + ); +} + +template +class TestBinaryFunction +{ + typedef Value(*Function)(const First&, const Second&); + Function m_function; +public: + + TestBinaryFunction(Function function) : m_function(function) + { + } + Value run(const First& first, const Second& second) const + { + return m_function(first, second); + } +}; + +template +class TestUnaryFunction +{ + typedef Value(*Function)(const First&); + Function m_function; +public: + + TestUnaryFunction(Function function) : m_function(function) + { + } + Value run(const First& first) const + { + return m_function(first); + } +}; + +class TestAll +{ +public: + TestAll() + { + Vector3 result1 = TestBinaryFunction(testAdded1).run(Vector3(0, 0, 0), Vector3(1, 1, 1)); + Vector3 result2 = TestBinaryFunction(testAdded2).run(Vector3(0, 0, 0), Vector3(1, 1, 1)); + Vector3 result3 = TestBinaryFunction(testMultiplied1).run(Vector3(1, 2, 3), Vector3(2, 1, 0.5f)); + Vector3 result4 = TestBinaryFunction(testMultiplied2).run(Vector3(1, 2, 3), Vector3(2, 1, 0.5f)); + Vector3 result5 = TestBinaryFunction(testScaled1).run(Vector3(1, 2, 3), 2.0); + Vector3 result6 = TestBinaryFunction(testScaled2).run(Vector3(1, 2, 3), 2.0); + Vector3 result7 = TestBinaryFunction(testMatrixMultiplied1).run(Vector3(1, 2, 3), matrix4_rotation_for_x_degrees(90)); + Vector3 result8 = TestBinaryFunction(testMatrixMultiplied2).run(Vector3(1, 2, 3), matrix4_rotation_for_x_degrees(90)); + Vector3 result9 = TestUnaryFunction(testNormalised1).run(Vector3(1, 2, 3)); + Vector3 result10 = TestUnaryFunction(testNormalised2).run(Vector3(1, 2, 3)); + } +} g_testAll; + diff --git a/tools/urt/libs/math/expression.h b/tools/urt/libs/math/expression.h new file mode 100644 index 00000000..c48204d4 --- /dev/null +++ b/tools/urt/libs/math/expression.h @@ -0,0 +1,597 @@ + +#if !defined (INCLUDED_EXPRESSION_H) +#define INCLUDED_EXPRESSION_H + +#include + +template +class Literal +{ + Value m_value; +public: + typedef Value value_type; + + Literal(const Value& value) + : m_value(value) + { + } + const value_type& eval() const + { + return m_value; + } +}; + +template +inline Literal float_literal(const Value& value) +{ + return Literal(value); +} + +template +inline float float_for_expression(const Expression& expression) +{ + return expression.eval(); +} + + +template +class ScalarDivided +{ + First first; + Second second; +public: + typedef typename First::value_type value_type; + + ScalarDivided(const First& first_, const Second& second_) : first(first_), second(second_) + { + } + value_type eval() const + { + return static_cast(first.eval() / second.eval()); + } +}; + +template +inline ScalarDivided float_divided(const First& first, const Second& second) +{ + return ScalarDivided(first, second); +} + +template +inline ScalarDivided, First> float_reciprocal(const First& first) +{ + typedef typename First::value_type first_value_type; + return ScalarDivided, First>(float_literal(first_value_type(1.0)), first); +} + +template +class SquareRoot +{ + First first; +public: + typedef typename First::value_type value_type; + + SquareRoot(const First& first_) : first(first_) + { + } + value_type eval() const + { + return static_cast(sqrt(first.eval())); + } +}; + +template +inline SquareRoot float_square_root(const First& first) +{ + return SquareRoot(first); +} + + +template +class BasicVector3Literal +{ + const BasicVector3 m_value; +public: + typedef Element value_type; + typedef ct_int<3> dimension; + + BasicVector3Literal(const BasicVector3& value) + : m_value(value) + { + } + const value_type& eval(unsigned int i) const + { + return m_value[i]; + } +}; + +template +inline BasicVector3Literal vector3_literal(const BasicVector3& value) +{ + return BasicVector3Literal(value); +} + +typedef BasicVector3Literal Vector3Literal; + +template +class BasicVector3Identity +{ + const BasicVector3& m_value; +public: + typedef Element value_type; + typedef ct_int<3> dimension; + + BasicVector3Identity(const BasicVector3& value) + : m_value(value) + { + } + const value_type& eval(unsigned int i) const + { + return m_value[i]; + } +}; + +template +inline BasicVector3Identity vector3_identity(const BasicVector3& value) +{ + return BasicVector3Identity(value); +} + +typedef BasicVector3Identity Vector3Identity; + +template +inline BasicVector3 vector3_for_expression(const Expression& expression) +{ + return Vector3(expression.eval(0), expression.eval(1), expression.eval(2)); +} + + +template +class VectorScalar +{ + First first; + Literal second; +public: + typedef typename First::value_type value_type; + typedef typename First::dimension dimension; + + VectorScalar(const First& first_, const Second& second_) + : first(first_), second(second_.eval()) + { + } + value_type eval(unsigned int i) const + { + return Operation::apply( first.eval(i), second.eval() ); + } +}; + + + +template +class VectorVector +{ + First first; + Second second; +public: + typedef typename First::value_type value_type; + typedef typename First::dimension dimension; + + VectorVector(const First& first_, const Second& second_) + : first(first_), second(second_) + { + } + value_type eval(unsigned int i) const + { + return Operation::apply(first.eval(i), second.eval(i)); + } +}; + +template +class Added +{ +public: + typedef First value_type; + + static value_type apply(const First& first, const Second& second) + { + return static_cast(first + second); + } +}; + +template +inline VectorVector, First, Second> +vector_added(const First& first, const Second& second) +{ + typedef typename First::value_type first_value_type; + typedef typename Second::value_type second_value_type; + return VectorVector, First, Second>(first, second); +} + +template +class Multiplied +{ +public: + typedef First value_type; + + static value_type apply(const First& first, const Second& second) + { + return static_cast(first * second); + } +}; + +template +inline VectorVector, First, Second> +vector_multiplied(const First& first, const Second& second) +{ + typedef typename First::value_type first_value_type; + typedef typename Second::value_type second_value_type; + return VectorVector, First, Second>(first, second); +} + +template +inline VectorScalar, First, Second> +vector_scaled(const First& first, const Second& second) +{ + typedef typename First::value_type first_value_type; + typedef typename Second::value_type second_value_type; + return VectorScalar, First, Second>(first, second); +} + +template +class Negated +{ +public: + typedef First value_type; + + static value_type apply(const First& first) + { + return -first; + } +}; + +template +class VectorUnary +{ + First first; +public: + typedef typename First::value_type value_type; + typedef typename First::dimension dimension; + + VectorUnary(const First& first_) : first(first_) + { + } + value_type eval(unsigned int i) const + { + return Operation::apply(first.eval(i)); + } +}; + +template +inline VectorUnary > +vector_negated(const First& first) +{ + typedef typename First::value_type first_value_type; + return VectorUnary >(first); +} + +template +class VectorCross +{ + First first; + Second second; +public: + typedef typename First::value_type value_type; + typedef typename First::dimension dimension; + + VectorCross(const First& first_, const Second& second_) + : first(first_), second(second_) + { + } + value_type eval(unsigned int i) const + { + return first.eval((i+1)%3) * second.eval((i+2)%3) - first.eval((i+2)%3) * second.eval((i+1)%3); + } +}; + +template +inline VectorCross +vector_cross(const First& first, const Second& second) +{ + return VectorCross(first, second); +} + + +template +class VectorDot +{ + First first; + Second second; +public: + typedef typename First::value_type value_type; + typedef typename First::dimension dimension; + + VectorDot(const First& first_, const Second& second_) + : first(first_), second(second_) + { + } + + template + struct eval_dot + { + static value_type apply(const First& first, const Second& second) + { + return static_cast( + first.eval(Index::value) * second.eval(Index::value) + + eval_dot< ct_int >::apply(first, second) + ); + } + }; + + template<> + struct eval_dot< ct_int<0> > + { + static value_type apply(const First& first, const Second& second) + { + return first.eval(0) * second.eval(0); + } + }; + + value_type eval() const + { + return eval_dot< ct_int >::apply(first, second); + } +}; + + +template +inline VectorDot vector_dot(const First& first, const Second& second) +{ + return VectorDot(first, second); +} + +template +class VectorLengthSquared +{ + First first; +public: + typedef typename First::value_type value_type; + typedef typename First::dimension dimension; + + VectorLengthSquared(const First& first_) + : first(first_) + { + } + + static value_type squared(const value_type& value) + { + return value * value; + } + + template + struct eval_squared + { + static value_type apply(const First& first) + { + return static_cast( + squared(first.eval(Index::value)) + + eval_squared< ct_int >::apply(first) + ); + } + }; + + template<> + struct eval_squared< ct_int<0> > + { + static value_type apply(const First& first) + { + return squared(first.eval(0)); + } + }; + + value_type eval() const + { + return eval_squared< ct_int >::apply(first); + } +}; + +template +inline VectorLengthSquared vector_length_squared(const First& first) +{ + return VectorLengthSquared(first); +} + +template +inline SquareRoot< VectorLengthSquared > vector_length(const First& first) +{ + return float_square_root(vector_length_squared(first)); +} + +#if 1 +template +inline VectorScalar< + Multiplied, + First, + // multiple evaulations of subexpression + ScalarDivided< + Literal, + SquareRoot< + VectorLengthSquared + > + > +> vector_normalised(const First& first) +{ + typedef typename First::value_type first_value_type; + return vector_scaled(first, float_reciprocal(vector_length(first))); +} +#else +template +inline VectorScalar< + Multiplied, + First, + // single evaluation of subexpression + Literal +> +vector_normalised(const First& first) +{ + typedef typename First::value_type first_value_type; + return vector_scaled(first, float_literal(static_cast(first_value_type(1.0) / vector_length(first).eval()))); +} +#endif + + +class Matrix4Literal +{ + const Matrix4 m_value; +public: + typedef float value_type; + typedef ct_int<4> dimension0; + typedef ct_int<4> dimension1; + + Matrix4Literal(const Matrix4& value) + : m_value(value) + { + } + const value_type& eval(unsigned int r, unsigned int c) const + { + return m_value[r*4+c]; + } +}; + +inline Matrix4Literal matrix4_literal(const Matrix4& value) +{ + return Matrix4Literal(value); +} + +class Matrix4Identity +{ + const Matrix4& m_value; +public: + typedef float value_type; + typedef ct_int<4> dimension0; + typedef ct_int<4> dimension1; + + Matrix4Identity(const Matrix4& value) + : m_value(value) + { + } + const value_type& eval(unsigned int r, unsigned int c) const + { + return m_value[r*4+c]; + } +}; + +inline Matrix4Identity matrix4_identity(const Matrix4& value) +{ + return Matrix4Identity(value); +} + +template +inline Matrix4 matrix4_for_expression(const Expression& expression) +{ + return Matrix4( + expression.eval(0, 0), expression.eval(0, 1), expression.eval(0, 2), expression.eval(0, 3), + expression.eval(1, 0), expression.eval(1, 1), expression.eval(1, 2), expression.eval(1, 3), + expression.eval(2, 0), expression.eval(2, 1), expression.eval(2, 2), expression.eval(2, 3), + expression.eval(3, 0), expression.eval(3, 1), expression.eval(3, 2), expression.eval(3, 3) + ); +} + +template +inline Matrix4 matrix4_affine_for_expression(const Expression& expression) +{ + return Matrix4( + expression.eval(0, 0), expression.eval(0, 1), expression.eval(0, 2), 0, + expression.eval(1, 0), expression.eval(1, 1), expression.eval(1, 2), 0, + expression.eval(2, 0), expression.eval(2, 1), expression.eval(2, 2), 0, + expression.eval(3, 0), expression.eval(3, 1), expression.eval(3, 2), 1 + ); +} + + +template +class PointMultiplied +{ + const First& first; + const Second& second; +public: + typedef typename First::value_type value_type; + typedef typename First::dimension dimension; + + PointMultiplied(const First& first_, const Second& second_) + : first(first_), second(second_) + { + } + value_type eval(unsigned int i) const + { + return static_cast(second.eval(0, i) * first.eval(0) + + second.eval(1, i) * first.eval(1) + + second.eval(2, i) * first.eval(2) + + second.eval(3, i)); + } +}; + +template +inline PointMultiplied point_multiplied(const First& point, const Second& matrix) +{ + return PointMultiplied(point, matrix); +} + +template +class Matrix4Multiplied +{ + const First& first; + const Second& second; +public: + typedef typename First::value_type value_type; + typedef typename First::dimension0 dimension0; + typedef typename First::dimension1 dimension1; + + Matrix4Multiplied(const First& first_, const Second& second_) + : first(first_), second(second_) + { + } + + value_type eval(unsigned int r, unsigned int c) const + { + return static_cast( + second.eval(r, 0) * first.eval(0, c) + + second.eval(r, 1) * first.eval(1, c) + + second.eval(r, 2) * first.eval(2, c) + + second.eval(r, 3) * first.eval(3, c) + ); + } +}; + +template +inline Matrix4Multiplied matrix4_multiplied(const First& first, const Second& second) +{ + return Matrix4Multiplied(first, second); +} + +template +class MatrixTransposed +{ + const First& first; +public: + typedef typename First::value_type value_type; + typedef typename First::dimension0 dimension0; + typedef typename First::dimension1 dimension1; + + MatrixTransposed(const First& first_) + : first(first_) + { + } + + value_type eval(unsigned int r, unsigned int c) const + { + return first.eval(c, r); + } +}; + +template +inline MatrixTransposed matrix_transposed(const First& first) +{ + return MatrixTransposed(first); +} + +#endif diff --git a/tools/urt/libs/math/frustum.cpp b/tools/urt/libs/math/frustum.cpp new file mode 100644 index 00000000..00c2b6e7 --- /dev/null +++ b/tools/urt/libs/math/frustum.cpp @@ -0,0 +1,3 @@ + +#include "frustum.h" + diff --git a/tools/urt/libs/math/frustum.h b/tools/urt/libs/math/frustum.h new file mode 100644 index 00000000..3d5b023d --- /dev/null +++ b/tools/urt/libs/math/frustum.h @@ -0,0 +1,609 @@ + +#if !defined(INCLUDED_MATH_FRUSTUM_H) +#define INCLUDED_MATH_FRUSTUM_H + +/// \file +/// \brief View-frustum data types and related operations. + +#include "generic/enumeration.h" +#include "math/matrix.h" +#include "math/plane.h" +#include "math/aabb.h" +#include "math/line.h" + +inline Matrix4 matrix4_frustum(float left, float right, float bottom, float top, float nearval, float farval) +{ + return Matrix4( + static_cast( (2*nearval) / (right-left) ), + 0, + 0, + 0, + 0, + static_cast( (2*nearval) / (top-bottom) ), + 0, + 0, + static_cast( (right+left) / (right-left) ), + static_cast( (top+bottom) / (top-bottom) ), + static_cast( -(farval+nearval) / (farval-nearval) ), + -1, + 0, + 0, + static_cast( -(2*farval*nearval) / (farval-nearval) ), + 0 + ); +} + + + +typedef unsigned char ClipResult; +const ClipResult c_CLIP_PASS = 0x00; // 000000 +const ClipResult c_CLIP_LT_X = 0x01; // 000001 +const ClipResult c_CLIP_GT_X = 0x02; // 000010 +const ClipResult c_CLIP_LT_Y = 0x04; // 000100 +const ClipResult c_CLIP_GT_Y = 0x08; // 001000 +const ClipResult c_CLIP_LT_Z = 0x10; // 010000 +const ClipResult c_CLIP_GT_Z = 0x20; // 100000 +const ClipResult c_CLIP_FAIL = 0x3F; // 111111 + +template +class Vector4ClipLT +{ +public: + static bool compare(const Vector4& self) + { + return self[Index::value] < self[3]; + } + static double scale(const Vector4& self, const Vector4& other) + { + return (self[Index::value] - self[3]) / (other[3] - other[Index::value]); + } +}; + +template +class Vector4ClipGT +{ +public: + static bool compare(const Vector4& self) + { + return self[Index::value] > -self[3]; + } + static double scale(const Vector4& self, const Vector4& other) + { + return (self[Index::value] + self[3]) / (-other[3] - other[Index::value]); + } +}; + +template +class Vector4ClipPolygon +{ +public: + typedef Vector4* iterator; + typedef const Vector4* const_iterator; + + static std::size_t apply(const_iterator first, const_iterator last, iterator out) + { + const_iterator next = first, i = last - 1; + iterator tmp(out); + bool b0 = ClipPlane::compare(*i); + while(next != last) + { + bool b1 = ClipPlane::compare(*next); + if(b0 ^ b1) + { + *out = vector4_subtracted(*next, *i); + + double scale = ClipPlane::scale(*i, *out); + + (*out)[0] = static_cast((*i)[0] + scale*((*out)[0])); + (*out)[1] = static_cast((*i)[1] + scale*((*out)[1])); + (*out)[2] = static_cast((*i)[2] + scale*((*out)[2])); + (*out)[3] = static_cast((*i)[3] + scale*((*out)[3])); + + ++out; + } + + if(b1) + { + *out = *next; + ++out; + } + + i = next; + ++next; + b0 = b1; + } + + return out - tmp; + } +}; + +#define CLIP_X_LT_W(p) (Vector4ClipLT< ct_int<0> >::compare(p)) +#define CLIP_X_GT_W(p) (Vector4ClipGT< ct_int<0> >::compare(p)) +#define CLIP_Y_LT_W(p) (Vector4ClipLT< ct_int<1> >::compare(p)) +#define CLIP_Y_GT_W(p) (Vector4ClipGT< ct_int<1> >::compare(p)) +#define CLIP_Z_LT_W(p) (Vector4ClipLT< ct_int<2> >::compare(p)) +#define CLIP_Z_GT_W(p) (Vector4ClipGT< ct_int<2> >::compare(p)) + +inline ClipResult homogenous_clip_point(const Vector4& clipped) +{ + ClipResult result = c_CLIP_FAIL; + if(CLIP_X_LT_W(clipped)) result &= ~c_CLIP_LT_X; // X < W + if(CLIP_X_GT_W(clipped)) result &= ~c_CLIP_GT_X; // X > -W + if(CLIP_Y_LT_W(clipped)) result &= ~c_CLIP_LT_Y; // Y < W + if(CLIP_Y_GT_W(clipped)) result &= ~c_CLIP_GT_Y; // Y > -W + if(CLIP_Z_LT_W(clipped)) result &= ~c_CLIP_LT_Z; // Z < W + if(CLIP_Z_GT_W(clipped)) result &= ~c_CLIP_GT_Z; // Z > -W + return result; +} + +/// \brief Clips \p point by canonical matrix \p self. +/// Stores the result in \p clipped. +/// Returns a bitmask indicating which clip-planes the point was outside. +inline ClipResult matrix4_clip_point(const Matrix4& self, const Vector3& point, Vector4& clipped) +{ + clipped[0] = point[0]; + clipped[1] = point[1]; + clipped[2] = point[2]; + clipped[3] = 1; + matrix4_transform_vector4(self, clipped); + return homogenous_clip_point(clipped); +} + + +inline std::size_t homogenous_clip_triangle(Vector4 clipped[9]) +{ + Vector4 buffer[9]; + std::size_t count = 3; + count = Vector4ClipPolygon< Vector4ClipLT< ct_int<0> > >::apply(clipped, clipped + count, buffer); + count = Vector4ClipPolygon< Vector4ClipGT< ct_int<0> > >::apply(buffer, buffer + count, clipped); + count = Vector4ClipPolygon< Vector4ClipLT< ct_int<1> > >::apply(clipped, clipped + count, buffer); + count = Vector4ClipPolygon< Vector4ClipGT< ct_int<1> > >::apply(buffer, buffer + count, clipped); + count = Vector4ClipPolygon< Vector4ClipLT< ct_int<2> > >::apply(clipped, clipped + count, buffer); + return Vector4ClipPolygon< Vector4ClipGT< ct_int<2> > >::apply(buffer, buffer + count, clipped); +} + +/// \brief Transforms and clips the triangle formed by \p p0, \p p1, \p p2 by the canonical matrix \p self. +/// Stores the resulting polygon in \p clipped. +/// Returns the number of points in the resulting polygon. +inline std::size_t matrix4_clip_triangle(const Matrix4& self, const Vector3& p0, const Vector3& p1, const Vector3& p2, Vector4 clipped[9]) +{ + clipped[0][0] = p0[0]; + clipped[0][1] = p0[1]; + clipped[0][2] = p0[2]; + clipped[0][3] = 1; + clipped[1][0] = p1[0]; + clipped[1][1] = p1[1]; + clipped[1][2] = p1[2]; + clipped[1][3] = 1; + clipped[2][0] = p2[0]; + clipped[2][1] = p2[1]; + clipped[2][2] = p2[2]; + clipped[2][3] = 1; + + matrix4_transform_vector4(self, clipped[0]); + matrix4_transform_vector4(self, clipped[1]); + matrix4_transform_vector4(self, clipped[2]); + + return homogenous_clip_triangle(clipped); +} + +inline std::size_t homogenous_clip_line(Vector4 clipped[2]) +{ + const Vector4& p0 = clipped[0]; + const Vector4& p1 = clipped[1]; + + // early out + { + ClipResult mask0 = homogenous_clip_point(clipped[0]); + ClipResult mask1 = homogenous_clip_point(clipped[1]); + + if((mask0 | mask1) == c_CLIP_PASS) // both points passed all planes + return 2; + + if(mask0 & mask1) // both points failed any one plane + return 0; + } + + { + const bool index = CLIP_X_LT_W(p0); + if(index ^ CLIP_X_LT_W(p1)) + { + Vector4 clip(vector4_subtracted(p1, p0)); + + double scale = (p0[0] - p0[3]) / (clip[3] - clip[0]); + + clip[0] = static_cast(p0[0] + scale * clip[0]); + clip[1] = static_cast(p0[1] + scale * clip[1]); + clip[2] = static_cast(p0[2] + scale * clip[2]); + clip[3] = static_cast(p0[3] + scale * clip[3]); + + clipped[index] = clip; + } + else if(index == 0) + return 0; + } + + { + const bool index = CLIP_X_GT_W(p0); + if(index ^ CLIP_X_GT_W(p1)) + { + Vector4 clip(vector4_subtracted(p1, p0)); + + double scale = (p0[0] + p0[3]) / (-clip[3] - clip[0]); + + clip[0] = static_cast(p0[0] + scale * clip[0]); + clip[1] = static_cast(p0[1] + scale * clip[1]); + clip[2] = static_cast(p0[2] + scale * clip[2]); + clip[3] = static_cast(p0[3] + scale * clip[3]); + + clipped[index] = clip; + } + else if(index == 0) + return 0; + } + + { + const bool index = CLIP_Y_LT_W(p0); + if(index ^ CLIP_Y_LT_W(p1)) + { + Vector4 clip(vector4_subtracted(p1, p0)); + + double scale = (p0[1] - p0[3]) / (clip[3] - clip[1]); + + clip[0] = static_cast(p0[0] + scale * clip[0]); + clip[1] = static_cast(p0[1] + scale * clip[1]); + clip[2] = static_cast(p0[2] + scale * clip[2]); + clip[3] = static_cast(p0[3] + scale * clip[3]); + + clipped[index] = clip; + } + else if(index == 0) + return 0; + } + + { + const bool index = CLIP_Y_GT_W(p0); + if(index ^ CLIP_Y_GT_W(p1)) + { + Vector4 clip(vector4_subtracted(p1, p0)); + + double scale = (p0[1] + p0[3]) / (-clip[3] - clip[1]); + + clip[0] = static_cast(p0[0] + scale * clip[0]); + clip[1] = static_cast(p0[1] + scale * clip[1]); + clip[2] = static_cast(p0[2] + scale * clip[2]); + clip[3] = static_cast(p0[3] + scale * clip[3]); + + clipped[index] = clip; + } + else if(index == 0) + return 0; + } + + { + const bool index = CLIP_Z_LT_W(p0); + if(index ^ CLIP_Z_LT_W(p1)) + { + Vector4 clip(vector4_subtracted(p1, p0)); + + double scale = (p0[2] - p0[3]) / (clip[3] - clip[2]); + + clip[0] = static_cast(p0[0] + scale * clip[0]); + clip[1] = static_cast(p0[1] + scale * clip[1]); + clip[2] = static_cast(p0[2] + scale * clip[2]); + clip[3] = static_cast(p0[3] + scale * clip[3]); + + clipped[index] = clip; + } + else if(index == 0) + return 0; + } + + { + const bool index = CLIP_Z_GT_W(p0); + if(index ^ CLIP_Z_GT_W(p1)) + { + Vector4 clip(vector4_subtracted(p1, p0)); + + double scale = (p0[2] + p0[3]) / (-clip[3] - clip[2]); + + clip[0] = static_cast(p0[0] + scale * clip[0]); + clip[1] = static_cast(p0[1] + scale * clip[1]); + clip[2] = static_cast(p0[2] + scale * clip[2]); + clip[3] = static_cast(p0[3] + scale * clip[3]); + + clipped[index] = clip; + } + else if(index == 0) + return 0; + } + + return 2; +} + +/// \brief Transforms and clips the line formed by \p p0, \p p1 by the canonical matrix \p self. +/// Stores the resulting line in \p clipped. +/// Returns the number of points in the resulting line. +inline std::size_t matrix4_clip_line(const Matrix4& self, const Vector3& p0, const Vector3& p1, Vector4 clipped[2]) +{ + clipped[0][0] = p0[0]; + clipped[0][1] = p0[1]; + clipped[0][2] = p0[2]; + clipped[0][3] = 1; + clipped[1][0] = p1[0]; + clipped[1][1] = p1[1]; + clipped[1][2] = p1[2]; + clipped[1][3] = 1; + + matrix4_transform_vector4(self, clipped[0]); + matrix4_transform_vector4(self, clipped[1]); + + return homogenous_clip_line(clipped); +} + + + + +struct Frustum +{ + Plane3 right, left, bottom, top, back, front; + + Frustum() + { + } + Frustum(const Plane3& _right, + const Plane3& _left, + const Plane3& _bottom, + const Plane3& _top, + const Plane3& _back, + const Plane3& _front) + : right(_right), left(_left), bottom(_bottom), top(_top), back(_back), front(_front) + { + } +}; + +inline Frustum frustum_transformed(const Frustum& frustum, const Matrix4& transform) +{ + return Frustum( + plane3_transformed(frustum.right, transform), + plane3_transformed(frustum.left, transform), + plane3_transformed(frustum.bottom, transform), + plane3_transformed(frustum.top, transform), + plane3_transformed(frustum.back, transform), + plane3_transformed(frustum.front, transform) + ); +} + +inline Frustum frustum_inverse_transformed(const Frustum& frustum, const Matrix4& transform) +{ + return Frustum( + plane3_inverse_transformed(frustum.right, transform), + plane3_inverse_transformed(frustum.left, transform), + plane3_inverse_transformed(frustum.bottom, transform), + plane3_inverse_transformed(frustum.top, transform), + plane3_inverse_transformed(frustum.back, transform), + plane3_inverse_transformed(frustum.front, transform) + ); +} + +inline bool viewproj_test_point(const Matrix4& viewproj, const Vector3& point) +{ + Vector4 hpoint(matrix4_transformed_vector4(viewproj, Vector4(point, 1.0f))); + if(fabs(hpoint[0]) < fabs(hpoint[3]) + && fabs(hpoint[1]) < fabs(hpoint[3]) + && fabs(hpoint[2]) < fabs(hpoint[3])) + return true; + return false; +} + +inline bool viewproj_test_transformed_point(const Matrix4& viewproj, const Vector3& point, const Matrix4& localToWorld) +{ + return viewproj_test_point(viewproj, matrix4_transformed_point(localToWorld, point)); +} + +inline Frustum frustum_from_viewproj(const Matrix4& viewproj) +{ + return Frustum + ( + plane3_normalised(Plane3(viewproj[ 3] - viewproj[ 0], viewproj[ 7] - viewproj[ 4], viewproj[11] - viewproj[ 8], viewproj[15] - viewproj[12])), + plane3_normalised(Plane3(viewproj[ 3] + viewproj[ 0], viewproj[ 7] + viewproj[ 4], viewproj[11] + viewproj[ 8], viewproj[15] + viewproj[12])), + plane3_normalised(Plane3(viewproj[ 3] + viewproj[ 1], viewproj[ 7] + viewproj[ 5], viewproj[11] + viewproj[ 9], viewproj[15] + viewproj[13])), + plane3_normalised(Plane3(viewproj[ 3] - viewproj[ 1], viewproj[ 7] - viewproj[ 5], viewproj[11] - viewproj[ 9], viewproj[15] - viewproj[13])), + plane3_normalised(Plane3(viewproj[ 3] - viewproj[ 2], viewproj[ 7] - viewproj[ 6], viewproj[11] - viewproj[10], viewproj[15] - viewproj[14])), + plane3_normalised(Plane3(viewproj[ 3] + viewproj[ 2], viewproj[ 7] + viewproj[ 6], viewproj[11] + viewproj[10], viewproj[15] + viewproj[14])) + ); +} + +struct VolumeIntersection +{ + enum Value + { + OUTSIDE, + INSIDE, + PARTIAL + }; +}; + +typedef EnumeratedValue VolumeIntersectionValue; + +const VolumeIntersectionValue c_volumeOutside(VolumeIntersectionValue::OUTSIDE); +const VolumeIntersectionValue c_volumeInside(VolumeIntersectionValue::INSIDE); +const VolumeIntersectionValue c_volumePartial(VolumeIntersectionValue::PARTIAL); + +inline VolumeIntersectionValue frustum_test_aabb(const Frustum& frustum, const AABB& aabb) +{ + VolumeIntersectionValue result = c_volumeInside; + + switch(aabb_classify_plane(aabb, frustum.right)) + { + case 2: + return c_volumeOutside; + case 1: + result = c_volumePartial; + } + + switch(aabb_classify_plane(aabb, frustum.left)) + { + case 2: + return c_volumeOutside; + case 1: + result = c_volumePartial; + } + + switch(aabb_classify_plane(aabb, frustum.bottom)) + { + case 2: + return c_volumeOutside; + case 1: + result = c_volumePartial; + } + + switch(aabb_classify_plane(aabb, frustum.top)) + { + case 2: + return c_volumeOutside; + case 1: + result = c_volumePartial; + } + + switch(aabb_classify_plane(aabb, frustum.back)) + { + case 2: + return c_volumeOutside; + case 1: + result = c_volumePartial; + } + + switch(aabb_classify_plane(aabb, frustum.front)) + { + case 2: + return c_volumeOutside; + case 1: + result = c_volumePartial; + } + + return result; +} + +inline double plane_distance_to_point(const Plane3& plane, const Vector3& point) +{ + return vector3_dot(plane.normal(), point) + plane.d; +} + +inline double plane_distance_to_oriented_extents(const Plane3& plane, const Vector3& extents, const Matrix4& orientation) +{ + return fabs(extents[0] * vector3_dot(plane.normal(), vector4_to_vector3(orientation.x()))) + + fabs(extents[1] * vector3_dot(plane.normal(), vector4_to_vector3(orientation.y()))) + + fabs(extents[2] * vector3_dot(plane.normal(), vector4_to_vector3(orientation.z()))); +} + +/// \brief Return false if \p aabb with \p orientation is partially or completely outside \p plane. +inline bool plane_contains_oriented_aabb(const Plane3& plane, const AABB& aabb, const Matrix4& orientation) +{ + double dot = plane_distance_to_point(plane, aabb.origin); + return !(dot > 0 || -dot < plane_distance_to_oriented_extents(plane, aabb.extents, orientation)); +} + +inline VolumeIntersectionValue frustum_intersects_transformed_aabb(const Frustum& frustum, const AABB& aabb, const Matrix4& localToWorld) +{ + AABB aabb_world(aabb); + matrix4_transform_point(localToWorld, aabb_world.origin); + + if(plane_contains_oriented_aabb(frustum.right, aabb_world, localToWorld) + || plane_contains_oriented_aabb(frustum.left, aabb_world, localToWorld) + || plane_contains_oriented_aabb(frustum.bottom, aabb_world, localToWorld) + || plane_contains_oriented_aabb(frustum.top, aabb_world, localToWorld) + || plane_contains_oriented_aabb(frustum.back, aabb_world, localToWorld) + || plane_contains_oriented_aabb(frustum.front, aabb_world, localToWorld)) + return c_volumeOutside; + return c_volumeInside; +} + +inline bool plane3_test_point(const Plane3& plane, const Vector3& point) +{ + return vector3_dot(point, plane.normal()) + plane.dist() <= 0; +} + +inline bool plane3_test_line(const Plane3& plane, const Segment& segment) +{ + return segment_classify_plane(segment, plane) == 2; +} + +inline bool frustum_test_point(const Frustum& frustum, const Vector3& point) +{ + return !plane3_test_point(frustum.right, point) + && !plane3_test_point(frustum.left, point) + && !plane3_test_point(frustum.bottom, point) + && !plane3_test_point(frustum.top, point) + && !plane3_test_point(frustum.back, point) + && !plane3_test_point(frustum.front, point); +} + +inline bool frustum_test_line(const Frustum& frustum, const Segment& segment) +{ + return !plane3_test_line(frustum.right, segment) + && !plane3_test_line(frustum.left, segment) + && !plane3_test_line(frustum.bottom, segment) + && !plane3_test_line(frustum.top, segment) + && !plane3_test_line(frustum.back, segment) + && !plane3_test_line(frustum.front, segment); +} + +inline bool viewer_test_plane(const Vector4& viewer, const Plane3& plane) +{ + return ((plane.a * viewer[0]) + + (plane.b * viewer[1]) + + (plane.c * viewer[2]) + + (plane.d * viewer[3])) > 0; +} + +inline Vector3 triangle_cross(const Vector3& p0, const Vector3& p1, const Vector3& p2) +{ + return vector3_cross(vector3_subtracted(p1, p0), vector3_subtracted(p1, p2)); +} + +inline bool viewer_test_triangle(const Vector4& viewer, const Vector3& p0, const Vector3& p1, const Vector3& p2) +{ + Vector3 cross(triangle_cross(p0, p1, p2)); + return ((viewer[0] * cross[0]) + + (viewer[1] * cross[1]) + + (viewer[2] * cross[2]) + + (viewer[3] * 0)) > 0; +} + +inline Vector4 viewer_from_transformed_viewer(const Vector4& viewer, const Matrix4& transform) +{ + if(viewer[3] == 0) + { + return Vector4(matrix4_transformed_direction(transform, vector4_to_vector3(viewer)), 0); + } + else + { + return Vector4(matrix4_transformed_point(transform, vector4_to_vector3(viewer)), viewer[3]); + } +} + +inline bool viewer_test_transformed_plane(const Vector4& viewer, const Plane3& plane, const Matrix4& localToWorld) +{ +#if 0 + return viewer_test_plane(viewer_from_transformed_viewer(viewer, matrix4_affine_inverse(localToWorld)), plane); +#else + return viewer_test_plane(viewer, plane3_transformed(plane, localToWorld)); +#endif +} + +inline Vector4 viewer_from_viewproj(const Matrix4& viewproj) +{ + // get viewer pos in object coords + Vector4 viewer(matrix4_transformed_vector4(matrix4_full_inverse(viewproj), Vector4(0, 0, -1, 0))); + if(viewer[3] != 0) // non-affine matrix + { + viewer[0] /= viewer[3]; + viewer[1] /= viewer[3]; + viewer[2] /= viewer[3]; + viewer[3] /= viewer[3]; + } + return viewer; +} + +#endif diff --git a/tools/urt/libs/math/line.cpp b/tools/urt/libs/math/line.cpp new file mode 100644 index 00000000..5ab6459b --- /dev/null +++ b/tools/urt/libs/math/line.cpp @@ -0,0 +1,3 @@ + +#include "line.h" + diff --git a/tools/urt/libs/math/line.h b/tools/urt/libs/math/line.h new file mode 100644 index 00000000..334f64c5 --- /dev/null +++ b/tools/urt/libs/math/line.h @@ -0,0 +1,131 @@ + +#if !defined(INCLUDED_MATH_LINE_H) +#define INCLUDED_MATH_LINE_H + +/// \file +/// \brief Line data types and related operations. + +#include "math/vector.h" +#include "math/plane.h" + +/// \brief A line segment defined by a start point and and end point. +class Line +{ +public: + Vector3 start, end; + + Line() + { + } + Line(const Vector3& start_, const Vector3& end_) : start(start_), end(end_) + { + } +}; + +inline Vector3 line_closest_point(const Line& line, const Vector3& point) +{ + Vector3 v = line.end - line.start; + Vector3 w = point - line.start; + + double c1 = vector3_dot(w,v); + if ( c1 <= 0 ) + return line.start; + + double c2 = vector3_dot(v,v); + if ( c2 <= c1 ) + return line.end; + + return Vector3(line.start + v * (c1 / c2)); +} + + +class Segment +{ +public: + Vector3 origin, extents; + + Segment() + { + } + Segment(const Vector3& origin_, const Vector3& extents_) : + origin(origin_), extents(extents_) + { + } +}; + + +inline Segment segment_for_startend(const Vector3& start, const Vector3& end) +{ + Segment segment; + segment.origin = vector3_mid(start, end); + segment.extents = vector3_subtracted(end, segment.origin); + return segment; +} + +inline unsigned int segment_classify_plane(const Segment& segment, const Plane3& plane) +{ + double distance_origin = vector3_dot(plane.normal(), segment.origin) + plane.dist(); + + if (fabs(distance_origin) < fabs(vector3_dot(plane.normal(), segment.extents))) + { + return 1; // partially inside + } + else if (distance_origin < 0) + { + return 2; // totally inside + } + return 0; // totally outside +} + + +class Ray +{ +public: + Vector3 origin, direction; + + Ray() + { + } + Ray(const Vector3& origin_, const Vector3& direction_) : + origin(origin_), direction(direction_) + { + } +}; + +inline Ray ray_for_points(const Vector3& origin, const Vector3& p2) +{ + return Ray(origin, vector3_normalised(vector3_subtracted(p2, origin))); +} + +inline void ray_transform(Ray& ray, const Matrix4& matrix) +{ + matrix4_transform_point(matrix, ray.origin); + matrix4_transform_direction(matrix, ray.direction); +} + +// closest-point-on-line +inline double ray_squared_distance_to_point(const Ray& ray, const Vector3& point) +{ + return vector3_length_squared( + vector3_subtracted( + point, + vector3_added( + ray.origin, + vector3_scaled( + ray.direction, + vector3_dot( + vector3_subtracted(point, ray.origin), + ray.direction + ) + ) + ) + ) + ); +} + +inline double ray_distance_to_plane(const Ray& ray, const Plane3& plane) +{ + return -(vector3_dot(plane.normal(), ray.origin) - plane.dist()) / vector3_dot(ray.direction, plane.normal()); +} + +#endif diff --git a/tools/urt/libs/math/matrix.cpp b/tools/urt/libs/math/matrix.cpp new file mode 100644 index 00000000..0a63d78f --- /dev/null +++ b/tools/urt/libs/math/matrix.cpp @@ -0,0 +1,3 @@ + +#include "matrix.h" + diff --git a/tools/urt/libs/math/matrix.h b/tools/urt/libs/math/matrix.h new file mode 100644 index 00000000..429aa040 --- /dev/null +++ b/tools/urt/libs/math/matrix.h @@ -0,0 +1,1290 @@ + +#if !defined(INCLUDED_MATH_MATRIX_H) +#define INCLUDED_MATH_MATRIX_H + +/// \file +/// \brief Matrix data types and related operations. + +#include "math/vector.h" + +/// \brief A 4x4 matrix stored in single-precision floating-point. +class Matrix4 +{ + float m_elements[16]; +public: + + Matrix4() + { + } + Matrix4(float xx_, float xy_, float xz_, float xw_, + float yx_, float yy_, float yz_, float yw_, + float zx_, float zy_, float zz_, float zw_, + float tx_, float ty_, float tz_, float tw_) + { + xx() = xx_; + xy() = xy_; + xz() = xz_; + xw() = xw_; + yx() = yx_; + yy() = yy_; + yz() = yz_; + yw() = yw_; + zx() = zx_; + zy() = zy_; + zz() = zz_; + zw() = zw_; + tx() = tx_; + ty() = ty_; + tz() = tz_; + tw() = tw_; + } + + float& xx() + { + return m_elements[0]; + } + const float& xx() const + { + return m_elements[0]; + } + float& xy() + { + return m_elements[1]; + } + const float& xy() const + { + return m_elements[1]; + } + float& xz() + { + return m_elements[2]; + } + const float& xz() const + { + return m_elements[2]; + } + float& xw() + { + return m_elements[3]; + } + const float& xw() const + { + return m_elements[3]; + } + float& yx() + { + return m_elements[4]; + } + const float& yx() const + { + return m_elements[4]; + } + float& yy() + { + return m_elements[5]; + } + const float& yy() const + { + return m_elements[5]; + } + float& yz() + { + return m_elements[6]; + } + const float& yz() const + { + return m_elements[6]; + } + float& yw() + { + return m_elements[7]; + } + const float& yw() const + { + return m_elements[7]; + } + float& zx() + { + return m_elements[8]; + } + const float& zx() const + { + return m_elements[8]; + } + float& zy() + { + return m_elements[9]; + } + const float& zy() const + { + return m_elements[9]; + } + float& zz() + { + return m_elements[10]; + } + const float& zz() const + { + return m_elements[10]; + } + float& zw() + { + return m_elements[11]; + } + const float& zw() const + { + return m_elements[11]; + } + float& tx() + { + return m_elements[12]; + } + const float& tx() const + { + return m_elements[12]; + } + float& ty() + { + return m_elements[13]; + } + const float& ty() const + { + return m_elements[13]; + } + float& tz() + { + return m_elements[14]; + } + const float& tz() const + { + return m_elements[14]; + } + float& tw() + { + return m_elements[15]; + } + const float& tw() const + { + return m_elements[15]; + } + + Vector4& x() + { + return reinterpret_cast(xx()); + } + const Vector4& x() const + { + return reinterpret_cast(xx()); + } + Vector4& y() + { + return reinterpret_cast(yx()); + } + const Vector4& y() const + { + return reinterpret_cast(yx()); + } + Vector4& z() + { + return reinterpret_cast(zx()); + } + const Vector4& z() const + { + return reinterpret_cast(zx()); + } + Vector4& t() + { + return reinterpret_cast(tx()); + } + const Vector4& t() const + { + return reinterpret_cast(tx()); + } + + const float& index(std::size_t i) const + { + return m_elements[i]; + } + float& index(std::size_t i) + { + return m_elements[i]; + } + const float& operator[](std::size_t i) const + { + return m_elements[i]; + } + float& operator[](std::size_t i) + { + return m_elements[i]; + } + const float& index(std::size_t r, std::size_t c) const + { + return m_elements[(r << 2) + c]; + } + float& index(std::size_t r, std::size_t c) + { + return m_elements[(r << 2) + c]; + } +}; + +/// \brief The 4x4 identity matrix. +const Matrix4 g_matrix4_identity( + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 +); + + +/// \brief Returns true if \p self and \p other are exactly element-wise equal. +inline bool operator==(const Matrix4& self, const Matrix4& other) +{ + return self.xx() == other.xx() && self.xy() == other.xy() && self.xz() == other.xz() && self.xw() == other.xw() + && self.yx() == other.yx() && self.yy() == other.yy() && self.yz() == other.yz() && self.yw() == other.yw() + && self.zx() == other.zx() && self.zy() == other.zy() && self.zz() == other.zz() && self.zw() == other.zw() + && self.tx() == other.tx() && self.ty() == other.ty() && self.tz() == other.tz() && self.tw() == other.tw(); +} + +/// \brief Returns true if \p self and \p other are exactly element-wise equal. +inline bool matrix4_equal(const Matrix4& self, const Matrix4& other) +{ + return self == other; +} + +/// \brief Returns true if \p self and \p other are element-wise equal within \p epsilon. +inline bool matrix4_equal_epsilon(const Matrix4& self, const Matrix4& other, float epsilon) +{ + return float_equal_epsilon(self.xx(), other.xx(), epsilon) + && float_equal_epsilon(self.xy(), other.xy(), epsilon) + && float_equal_epsilon(self.xz(), other.xz(), epsilon) + && float_equal_epsilon(self.xw(), other.xw(), epsilon) + && float_equal_epsilon(self.yx(), other.yx(), epsilon) + && float_equal_epsilon(self.yy(), other.yy(), epsilon) + && float_equal_epsilon(self.yz(), other.yz(), epsilon) + && float_equal_epsilon(self.yw(), other.yw(), epsilon) + && float_equal_epsilon(self.zx(), other.zx(), epsilon) + && float_equal_epsilon(self.zy(), other.zy(), epsilon) + && float_equal_epsilon(self.zz(), other.zz(), epsilon) + && float_equal_epsilon(self.zw(), other.zw(), epsilon) + && float_equal_epsilon(self.tx(), other.tx(), epsilon) + && float_equal_epsilon(self.ty(), other.ty(), epsilon) + && float_equal_epsilon(self.tz(), other.tz(), epsilon) + && float_equal_epsilon(self.tw(), other.tw(), epsilon); +} + +/// \brief Returns true if \p self and \p other are exactly element-wise equal. +/// \p self and \p other must be affine. +inline bool matrix4_affine_equal(const Matrix4& self, const Matrix4& other) +{ + return self[0] == other[0] + && self[1] == other[1] + && self[2] == other[2] + && self[4] == other[4] + && self[5] == other[5] + && self[6] == other[6] + && self[8] == other[8] + && self[9] == other[9] + && self[10] == other[10] + && self[12] == other[12] + && self[13] == other[13] + && self[14] == other[14]; +} + +enum Matrix4Handedness +{ + MATRIX4_RIGHTHANDED = 0, + MATRIX4_LEFTHANDED = 1, +}; + +/// \brief Returns MATRIX4_RIGHTHANDED if \p self is right-handed, else returns MATRIX4_LEFTHANDED. +inline Matrix4Handedness matrix4_handedness(const Matrix4& self) +{ + return ( + vector3_dot( + vector3_cross(vector4_to_vector3(self.x()), vector4_to_vector3(self.y())), + vector4_to_vector3(self.z()) + ) + < 0.0 + ) ? MATRIX4_LEFTHANDED : MATRIX4_RIGHTHANDED; +} + + + + + +/// \brief Returns \p self post-multiplied by \p other. +inline Matrix4 matrix4_multiplied_by_matrix4(const Matrix4& self, const Matrix4& other) +{ + return Matrix4( + other[0] * self[0] + other[1] * self[4] + other[2] * self[8] + other[3] * self[12], + other[0] * self[1] + other[1] * self[5] + other[2] * self[9] + other[3] * self[13], + other[0] * self[2] + other[1] * self[6] + other[2] * self[10]+ other[3] * self[14], + other[0] * self[3] + other[1] * self[7] + other[2] * self[11]+ other[3] * self[15], + other[4] * self[0] + other[5] * self[4] + other[6] * self[8] + other[7] * self[12], + other[4] * self[1] + other[5] * self[5] + other[6] * self[9] + other[7] * self[13], + other[4] * self[2] + other[5] * self[6] + other[6] * self[10]+ other[7] * self[14], + other[4] * self[3] + other[5] * self[7] + other[6] * self[11]+ other[7] * self[15], + other[8] * self[0] + other[9] * self[4] + other[10]* self[8] + other[11]* self[12], + other[8] * self[1] + other[9] * self[5] + other[10]* self[9] + other[11]* self[13], + other[8] * self[2] + other[9] * self[6] + other[10]* self[10]+ other[11]* self[14], + other[8] * self[3] + other[9] * self[7] + other[10]* self[11]+ other[11]* self[15], + other[12]* self[0] + other[13]* self[4] + other[14]* self[8] + other[15]* self[12], + other[12]* self[1] + other[13]* self[5] + other[14]* self[9] + other[15]* self[13], + other[12]* self[2] + other[13]* self[6] + other[14]* self[10]+ other[15]* self[14], + other[12]* self[3] + other[13]* self[7] + other[14]* self[11]+ other[15]* self[15] + ); +} + +/// \brief Post-multiplies \p self by \p other in-place. +inline void matrix4_multiply_by_matrix4(Matrix4& self, const Matrix4& other) +{ + self = matrix4_multiplied_by_matrix4(self, other); +} + + +/// \brief Returns \p self pre-multiplied by \p other. +inline Matrix4 matrix4_premultiplied_by_matrix4(const Matrix4& self, const Matrix4& other) +{ +#if 1 + return matrix4_multiplied_by_matrix4(other, self); +#else + return Matrix4( + self[0] * other[0] + self[1] * other[4] + self[2] * other[8] + self[3] * other[12], + self[0] * other[1] + self[1] * other[5] + self[2] * other[9] + self[3] * other[13], + self[0] * other[2] + self[1] * other[6] + self[2] * other[10]+ self[3] * other[14], + self[0] * other[3] + self[1] * other[7] + self[2] * other[11]+ self[3] * other[15], + self[4] * other[0] + self[5] * other[4] + self[6] * other[8] + self[7] * other[12], + self[4] * other[1] + self[5] * other[5] + self[6] * other[9] + self[7] * other[13], + self[4] * other[2] + self[5] * other[6] + self[6] * other[10]+ self[7] * other[14], + self[4] * other[3] + self[5] * other[7] + self[6] * other[11]+ self[7] * other[15], + self[8] * other[0] + self[9] * other[4] + self[10]* other[8] + self[11]* other[12], + self[8] * other[1] + self[9] * other[5] + self[10]* other[9] + self[11]* other[13], + self[8] * other[2] + self[9] * other[6] + self[10]* other[10]+ self[11]* other[14], + self[8] * other[3] + self[9] * other[7] + self[10]* other[11]+ self[11]* other[15], + self[12]* other[0] + self[13]* other[4] + self[14]* other[8] + self[15]* other[12], + self[12]* other[1] + self[13]* other[5] + self[14]* other[9] + self[15]* other[13], + self[12]* other[2] + self[13]* other[6] + self[14]* other[10]+ self[15]* other[14], + self[12]* other[3] + self[13]* other[7] + self[14]* other[11]+ self[15]* other[15] + ); +#endif +} + +/// \brief Pre-multiplies \p self by \p other in-place. +inline void matrix4_premultiply_by_matrix4(Matrix4& self, const Matrix4& other) +{ + self = matrix4_premultiplied_by_matrix4(self, other); +} + +/// \brief returns true if \p transform is affine. +inline bool matrix4_is_affine(const Matrix4& transform) +{ + return transform[3] == 0 && transform[7] == 0 && transform[11] == 0 && transform[15] == 1; +} + +/// \brief Returns \p self post-multiplied by \p other. +/// \p self and \p other must be affine. +inline Matrix4 matrix4_affine_multiplied_by_matrix4(const Matrix4& self, const Matrix4& other) +{ + return Matrix4( + other[0] * self[0] + other[1] * self[4] + other[2] * self[8], + other[0] * self[1] + other[1] * self[5] + other[2] * self[9], + other[0] * self[2] + other[1] * self[6] + other[2] * self[10], + 0, + other[4] * self[0] + other[5] * self[4] + other[6] * self[8], + other[4] * self[1] + other[5] * self[5] + other[6] * self[9], + other[4] * self[2] + other[5] * self[6] + other[6] * self[10], + 0, + other[8] * self[0] + other[9] * self[4] + other[10]* self[8], + other[8] * self[1] + other[9] * self[5] + other[10]* self[9], + other[8] * self[2] + other[9] * self[6] + other[10]* self[10], + 0, + other[12]* self[0] + other[13]* self[4] + other[14]* self[8] + self[12], + other[12]* self[1] + other[13]* self[5] + other[14]* self[9] + self[13], + other[12]* self[2] + other[13]* self[6] + other[14]* self[10]+ self[14], + 1 + ); +} + +/// \brief Post-multiplies \p self by \p other in-place. +/// \p self and \p other must be affine. +inline void matrix4_affine_multiply_by_matrix4(Matrix4& self, const Matrix4& other) +{ + self = matrix4_affine_multiplied_by_matrix4(self, other); +} + +/// \brief Returns \p self pre-multiplied by \p other. +/// \p self and \p other must be affine. +inline Matrix4 matrix4_affine_premultiplied_by_matrix4(const Matrix4& self, const Matrix4& other) +{ +#if 1 + return matrix4_affine_multiplied_by_matrix4(other, self); +#else + return Matrix4( + self[0] * other[0] + self[1] * other[4] + self[2] * other[8], + self[0] * other[1] + self[1] * other[5] + self[2] * other[9], + self[0] * other[2] + self[1] * other[6] + self[2] * other[10], + 0, + self[4] * other[0] + self[5] * other[4] + self[6] * other[8], + self[4] * other[1] + self[5] * other[5] + self[6] * other[9], + self[4] * other[2] + self[5] * other[6] + self[6] * other[10], + 0, + self[8] * other[0] + self[9] * other[4] + self[10]* other[8], + self[8] * other[1] + self[9] * other[5] + self[10]* other[9], + self[8] * other[2] + self[9] * other[6] + self[10]* other[10], + 0, + self[12]* other[0] + self[13]* other[4] + self[14]* other[8] + other[12], + self[12]* other[1] + self[13]* other[5] + self[14]* other[9] + other[13], + self[12]* other[2] + self[13]* other[6] + self[14]* other[10]+ other[14], + 1 + ) + ); +#endif +} + +/// \brief Pre-multiplies \p self by \p other in-place. +/// \p self and \p other must be affine. +inline void matrix4_affine_premultiply_by_matrix4(Matrix4& self, const Matrix4& other) +{ + self = matrix4_affine_premultiplied_by_matrix4(self, other); +} + +/// \brief Returns \p point transformed by \p self. +template +inline BasicVector3 matrix4_transformed_point(const Matrix4& self, const BasicVector3& point) +{ + return BasicVector3( + static_cast(self[0] * point[0] + self[4] * point[1] + self[8] * point[2] + self[12]), + static_cast(self[1] * point[0] + self[5] * point[1] + self[9] * point[2] + self[13]), + static_cast(self[2] * point[0] + self[6] * point[1] + self[10] * point[2] + self[14]) + ); +} + +/// \brief Transforms \p point by \p self in-place. +template +inline void matrix4_transform_point(const Matrix4& self, BasicVector3& point) +{ + point = matrix4_transformed_point(self, point); +} + +/// \brief Returns \p vector4 transformed by \p self. +template +inline BasicVector3 matrix4_transformed_direction(const Matrix4& self, const BasicVector3& direction) +{ + return BasicVector3( + static_cast(self[0] * direction[0] + self[4] * direction[1] + self[8] * direction[2]), + static_cast(self[1] * direction[0] + self[5] * direction[1] + self[9] * direction[2]), + static_cast(self[2] * direction[0] + self[6] * direction[1] + self[10] * direction[2]) + ); +} + +/// \brief Transforms \p direction by \p self in-place. +template +inline void matrix4_transform_direction(const Matrix4& self, BasicVector3& normal) +{ + normal = matrix4_transformed_direction(self, normal); +} + +/// \brief Returns \p vector4 transformed by \p self. +inline Vector4 matrix4_transformed_vector4(const Matrix4& self, const Vector4& vector4) +{ + return Vector4( + self[0] * vector4[0] + self[4] * vector4[1] + self[8] * vector4[2] + self[12] * vector4[3], + self[1] * vector4[0] + self[5] * vector4[1] + self[9] * vector4[2] + self[13] * vector4[3], + self[2] * vector4[0] + self[6] * vector4[1] + self[10] * vector4[2] + self[14] * vector4[3], + self[3] * vector4[0] + self[7] * vector4[1] + self[11] * vector4[2] + self[15] * vector4[3] + ); +} + +/// \brief Transforms \p vector4 by \p self in-place. +inline void matrix4_transform_vector4(const Matrix4& self, Vector4& vector4) +{ + vector4 = matrix4_transformed_vector4(self, vector4); +} + + +/// \brief Transposes \p self in-place. +inline void matrix4_transpose(Matrix4& self) +{ + std::swap(self.xy(), self.yx()); + std::swap(self.xz(), self.zx()); + std::swap(self.xw(), self.tx()); + std::swap(self.yz(), self.zy()); + std::swap(self.yw(), self.ty()); + std::swap(self.zw(), self.tz()); +} + +/// \brief Returns \p self transposed. +inline Matrix4 matrix4_transposed(const Matrix4& self) +{ + return Matrix4( + self.xx(), + self.yx(), + self.zx(), + self.tx(), + self.xy(), + self.yy(), + self.zy(), + self.ty(), + self.xz(), + self.yz(), + self.zz(), + self.tz(), + self.xw(), + self.yw(), + self.zw(), + self.tw() + ); +} + + +/// \brief Inverts an affine transform in-place. +/// Adapted from Graphics Gems 2. +inline void matrix4_affine_invert(Matrix4& self) +{ + Matrix4 other(self); + + // determinant of rotation submatrix + float det + = other[0] * ( other[5]*other[10] - other[9]*other[6] ) + - other[1] * ( other[4]*other[10] - other[8]*other[6] ) + + other[2] * ( other[4]*other[9] - other[8]*other[5] ); + + // throw exception here if (det*det < 1e-25) + + // invert rotation submatrix + det = 1.0f / det; + self[0] = ( (other[5]*other[10]- other[6]*other[9] )*det); + self[1] = (- (other[1]*other[10]- other[2]*other[9] )*det); + self[2] = ( (other[1]*other[6] - other[2]*other[5] )*det); + self[4] = (- (other[4]*other[10]- other[6]*other[8] )*det); + self[5] = ( (other[0]*other[10]- other[2]*other[8] )*det); + self[6] = (- (other[0]*other[6] - other[2]*other[4] )*det); + self[8] = ( (other[4]*other[9] - other[5]*other[8] )*det); + self[9] = (- (other[0]*other[9] - other[1]*other[8] )*det); + self[10]= ( (other[0]*other[5] - other[1]*other[4] )*det); + + // multiply translation part by rotation + self[12] = - (other[12] * self[0] + + other[13] * self[4] + + other[14] * self[8]); + self[13] = - (other[12] * self[1] + + other[13] * self[5] + + other[14] * self[9]); + self[14] = - (other[12] * self[2] + + other[13] * self[6] + + other[14] * self[10]); +} + +/// \brief A compile-time-constant integer. +template +struct ct_int +{ + enum { value = Value }; +}; + +/// \brief A compile-time-constant row/column index into a 4x4 matrix. +template +class Matrix4Index +{ +public: + typedef ct_int r; + typedef ct_int c; + typedef ct_int<(r::value * 4) + c::value> i; +}; + +/// \brief A functor which returns the cofactor of a 3x3 submatrix obtained by ignoring a given row and column of a 4x4 matrix. +/// \param Row Defines the compile-time-constant integers x, y and z with values corresponding to the indices of the three rows to use. +/// \param Col Defines the compile-time-constant integers x, y and z with values corresponding to the indices of the three columns to use. +template +class Matrix4Cofactor +{ +public: + typedef typename Matrix4Index::i xx; + typedef typename Matrix4Index::i xy; + typedef typename Matrix4Index::i xz; + typedef typename Matrix4Index::i yx; + typedef typename Matrix4Index::i yy; + typedef typename Matrix4Index::i yz; + typedef typename Matrix4Index::i zx; + typedef typename Matrix4Index::i zy; + typedef typename Matrix4Index::i zz; + static double apply(const Matrix4& self) + { + return self[xx::value] * ( self[yy::value]*self[zz::value] - self[zy::value]*self[yz::value] ) + - self[xy::value] * ( self[yx::value]*self[zz::value] - self[zx::value]*self[yz::value] ) + + self[xz::value] * ( self[yx::value]*self[zy::value] - self[zx::value]*self[yy::value] ); + } +}; + +/// \brief The cofactor element indices for a 4x4 matrix row or column. +/// \param Element The index of the element to ignore. +template +class Cofactor4 +{ +public: + typedef ct_int<(Element <= 0) ? 1 : 0> x; + typedef ct_int<(Element <= 1) ? 2 : 1> y; + typedef ct_int<(Element <= 2) ? 3 : 2> z; +}; + +/// \brief Returns the determinant of \p self. +inline double matrix4_determinant(const Matrix4& self) +{ + return self.xx() * Matrix4Cofactor< Cofactor4<0>, Cofactor4<0> >::apply(self) + - self.xy() * Matrix4Cofactor< Cofactor4<0>, Cofactor4<1> >::apply(self) + + self.xz() * Matrix4Cofactor< Cofactor4<0>, Cofactor4<2> >::apply(self) + - self.xw() * Matrix4Cofactor< Cofactor4<0>, Cofactor4<3> >::apply(self); +} + +/// \brief Returns the inverse of \p self using the Adjoint method. +/// \todo Throw an exception if the determinant is zero. +inline Matrix4 matrix4_full_inverse(const Matrix4& self) +{ + double determinant = 1.0 / matrix4_determinant(self); + + return Matrix4( + static_cast( Matrix4Cofactor< Cofactor4<0>, Cofactor4<0> >::apply(self) * determinant), + static_cast(-Matrix4Cofactor< Cofactor4<1>, Cofactor4<0> >::apply(self) * determinant), + static_cast( Matrix4Cofactor< Cofactor4<2>, Cofactor4<0> >::apply(self) * determinant), + static_cast(-Matrix4Cofactor< Cofactor4<3>, Cofactor4<0> >::apply(self) * determinant), + static_cast(-Matrix4Cofactor< Cofactor4<0>, Cofactor4<1> >::apply(self) * determinant), + static_cast( Matrix4Cofactor< Cofactor4<1>, Cofactor4<1> >::apply(self) * determinant), + static_cast(-Matrix4Cofactor< Cofactor4<2>, Cofactor4<1> >::apply(self) * determinant), + static_cast( Matrix4Cofactor< Cofactor4<3>, Cofactor4<1> >::apply(self) * determinant), + static_cast( Matrix4Cofactor< Cofactor4<0>, Cofactor4<2> >::apply(self) * determinant), + static_cast(-Matrix4Cofactor< Cofactor4<1>, Cofactor4<2> >::apply(self) * determinant), + static_cast( Matrix4Cofactor< Cofactor4<2>, Cofactor4<2> >::apply(self) * determinant), + static_cast(-Matrix4Cofactor< Cofactor4<3>, Cofactor4<2> >::apply(self) * determinant), + static_cast(-Matrix4Cofactor< Cofactor4<0>, Cofactor4<3> >::apply(self) * determinant), + static_cast( Matrix4Cofactor< Cofactor4<1>, Cofactor4<3> >::apply(self) * determinant), + static_cast(-Matrix4Cofactor< Cofactor4<2>, Cofactor4<3> >::apply(self) * determinant), + static_cast( Matrix4Cofactor< Cofactor4<3>, Cofactor4<3> >::apply(self) * determinant) + ); +} + +/// \brief Inverts \p self in-place using the Adjoint method. +inline void matrix4_full_invert(Matrix4& self) +{ + self = matrix4_full_inverse(self); +} + + +/// \brief Constructs a pure-translation matrix from \p translation. +inline Matrix4 matrix4_translation_for_vec3(const Vector3& translation) +{ + return Matrix4( + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + translation[0], translation[1], translation[2], 1 + ); +} + +/// \brief Returns the translation part of \p self. +inline Vector3 matrix4_get_translation_vec3(const Matrix4& self) +{ + return vector4_to_vector3(self.t()); +} + +/// \brief Concatenates \p self with \p translation. +/// The concatenated \p translation occurs before \p self. +inline void matrix4_translate_by_vec3(Matrix4& self, const Vector3& translation) +{ + matrix4_multiply_by_matrix4(self, matrix4_translation_for_vec3(translation)); +} + +/// \brief Returns \p self Concatenated with \p translation. +/// The concatenated translation occurs before \p self. +inline Matrix4 matrix4_translated_by_vec3(const Matrix4& self, const Vector3& translation) +{ + return matrix4_multiplied_by_matrix4(self, matrix4_translation_for_vec3(translation)); +} + + +#include "math/pi.h" + +/// \brief Returns \p angle modulated by the range [0, 360). +/// \p angle must be in the range [-360, 360). +inline float angle_modulate_degrees_range(float angle) +{ + return static_cast(float_mod_range(angle, 360.0)); +} + +/// \brief Returns \p euler angles converted from radians to degrees. +inline Vector3 euler_radians_to_degrees(const Vector3& euler) +{ + return Vector3( + static_cast(radians_to_degrees(euler.x())), + static_cast(radians_to_degrees(euler.y())), + static_cast(radians_to_degrees(euler.z())) + ); +} + +/// \brief Returns \p euler angles converted from degrees to radians. +inline Vector3 euler_degrees_to_radians(const Vector3& euler) +{ + return Vector3( + static_cast(degrees_to_radians(euler.x())), + static_cast(degrees_to_radians(euler.y())), + static_cast(degrees_to_radians(euler.z())) + ); +} + + + +/// \brief Constructs a pure-rotation matrix about the x axis from sin \p s and cosine \p c of an angle. +inline Matrix4 matrix4_rotation_for_sincos_x(float s, float c) +{ + return Matrix4( + 1, 0, 0, 0, + 0, c, s, 0, + 0,-s, c, 0, + 0, 0, 0, 1 + ); +} + +/// \brief Constructs a pure-rotation matrix about the x axis from an angle in radians. +inline Matrix4 matrix4_rotation_for_x(double x) +{ + return matrix4_rotation_for_sincos_x(static_cast(sin(x)), static_cast(cos(x))); +} + +/// \brief Constructs a pure-rotation matrix about the x axis from an angle in degrees. +inline Matrix4 matrix4_rotation_for_x_degrees(float x) +{ + return matrix4_rotation_for_x(degrees_to_radians(x)); +} + +/// \brief Constructs a pure-rotation matrix about the y axis from sin \p s and cosine \p c of an angle. +inline Matrix4 matrix4_rotation_for_sincos_y(float s, float c) +{ + return Matrix4( + c, 0,-s, 0, + 0, 1, 0, 0, + s, 0, c, 0, + 0, 0, 0, 1 + ); +} + +/// \brief Constructs a pure-rotation matrix about the y axis from an angle in radians. +inline Matrix4 matrix4_rotation_for_y(double y) +{ + return matrix4_rotation_for_sincos_y(static_cast(sin(y)), static_cast(cos(y))); +} + +/// \brief Constructs a pure-rotation matrix about the y axis from an angle in degrees. +inline Matrix4 matrix4_rotation_for_y_degrees(float y) +{ + return matrix4_rotation_for_y(degrees_to_radians(y)); +} + +/// \brief Constructs a pure-rotation matrix about the z axis from sin \p s and cosine \p c of an angle. +inline Matrix4 matrix4_rotation_for_sincos_z(float s, float c) +{ + return Matrix4( + c, s, 0, 0, + -s, c, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + ); +} + +/// \brief Constructs a pure-rotation matrix about the z axis from an angle in radians. +inline Matrix4 matrix4_rotation_for_z(double z) +{ + return matrix4_rotation_for_sincos_z(static_cast(sin(z)), static_cast(cos(z))); +} + +/// \brief Constructs a pure-rotation matrix about the z axis from an angle in degrees. +inline Matrix4 matrix4_rotation_for_z_degrees(float z) +{ + return matrix4_rotation_for_z(degrees_to_radians(z)); +} + +/// \brief Constructs a pure-rotation matrix from a set of euler angles (radians) in the order (x, y, z). +/*! \verbatim +clockwise rotation around X, Y, Z, facing along axis + 1 0 0 cy 0 -sy cz sz 0 + 0 cx sx 0 1 0 -sz cz 0 + 0 -sx cx sy 0 cy 0 0 1 + +rows of Z by cols of Y + cy*cz -sy*cz+sz -sy*sz+cz +-sz*cy -sz*sy+cz + + .. or something like that.. + +final rotation is Z * Y * X + cy*cz -sx*-sy*cz+cx*sz cx*-sy*sz+sx*cz +-cy*sz sx*sy*sz+cx*cz -cx*-sy*sz+sx*cz + sy -sx*cy cx*cy + +transposed +cy.cz + 0.sz + sy.0 cy.-sz + 0 .cz + sy.0 cy.0 + 0 .0 + sy.1 | +sx.sy.cz + cx.sz + -sx.cy.0 sx.sy.-sz + cx.cz + -sx.cy.0 sx.sy.0 + cx.0 + -sx.cy.1 | +-cx.sy.cz + sx.sz + cx.cy.0 -cx.sy.-sz + sx.cz + cx.cy.0 -cx.sy.0 + 0 .0 + cx.cy.1 | +\endverbatim */ +inline Matrix4 matrix4_rotation_for_euler_xyz(const Vector3& euler) +{ +#if 1 + + double cx = cos(euler[0]); + double sx = sin(euler[0]); + double cy = cos(euler[1]); + double sy = sin(euler[1]); + double cz = cos(euler[2]); + double sz = sin(euler[2]); + + return Matrix4( + static_cast(cy*cz), + static_cast(cy*sz), + static_cast(-sy), + 0, + static_cast(sx*sy*cz + cx*-sz), + static_cast(sx*sy*sz + cx*cz), + static_cast(sx*cy), + 0, + static_cast(cx*sy*cz + sx*sz), + static_cast(cx*sy*sz + -sx*cz), + static_cast(cx*cy), + 0, + 0, + 0, + 0, + 1 + ); + +#else + + return matrix4_premultiply_by_matrix4( + matrix4_premultiply_by_matrix4( + matrix4_rotation_for_x(euler[0]), + matrix4_rotation_for_y(euler[1]) + ), + matrix4_rotation_for_z(euler[2]) + ); + +#endif +} + +/// \brief Constructs a pure-rotation matrix from a set of euler angles (degrees) in the order (x, y, z). +inline Matrix4 matrix4_rotation_for_euler_xyz_degrees(const Vector3& euler) +{ + return matrix4_rotation_for_euler_xyz(euler_degrees_to_radians(euler)); +} + +/// \brief Concatenates \p self with the rotation transform produced by \p euler angles (degrees) in the order (x, y, z). +/// The concatenated rotation occurs before \p self. +inline void matrix4_rotate_by_euler_xyz_degrees(Matrix4& self, const Vector3& euler) +{ + matrix4_multiply_by_matrix4(self, matrix4_rotation_for_euler_xyz_degrees(euler)); +} + + +/// \brief Constructs a pure-rotation matrix from a set of euler angles (radians) in the order (y, z, x). +inline Matrix4 matrix4_rotation_for_euler_yzx(const Vector3& euler) +{ + return matrix4_premultiplied_by_matrix4( + matrix4_premultiplied_by_matrix4( + matrix4_rotation_for_y(euler[1]), + matrix4_rotation_for_z(euler[2]) + ), + matrix4_rotation_for_x(euler[0]) + ); +} + +/// \brief Constructs a pure-rotation matrix from a set of euler angles (degrees) in the order (y, z, x). +inline Matrix4 matrix4_rotation_for_euler_yzx_degrees(const Vector3& euler) +{ + return matrix4_rotation_for_euler_yzx(euler_degrees_to_radians(euler)); +} + +/// \brief Constructs a pure-rotation matrix from a set of euler angles (radians) in the order (x, z, y). +inline Matrix4 matrix4_rotation_for_euler_xzy(const Vector3& euler) +{ + return matrix4_premultiplied_by_matrix4( + matrix4_premultiplied_by_matrix4( + matrix4_rotation_for_x(euler[0]), + matrix4_rotation_for_z(euler[2]) + ), + matrix4_rotation_for_y(euler[1]) + ); +} + +/// \brief Constructs a pure-rotation matrix from a set of euler angles (degrees) in the order (x, z, y). +inline Matrix4 matrix4_rotation_for_euler_xzy_degrees(const Vector3& euler) +{ + return matrix4_rotation_for_euler_xzy(euler_degrees_to_radians(euler)); +} + +/// \brief Constructs a pure-rotation matrix from a set of euler angles (radians) in the order (y, x, z). +/*! \verbatim +| cy.cz + sx.sy.-sz + -cx.sy.0 0.cz + cx.-sz + sx.0 sy.cz + -sx.cy.-sz + cx.cy.0 | +| cy.sz + sx.sy.cz + -cx.sy.0 0.sz + cx.cz + sx.0 sy.sz + -sx.cy.cz + cx.cy.0 | +| cy.0 + sx.sy.0 + -cx.sy.1 0.0 + cx.0 + sx.1 sy.0 + -sx.cy.0 + cx.cy.1 | +\endverbatim */ +inline Matrix4 matrix4_rotation_for_euler_yxz(const Vector3& euler) +{ +#if 1 + + double cx = cos(euler[0]); + double sx = sin(euler[0]); + double cy = cos(euler[1]); + double sy = sin(euler[1]); + double cz = cos(euler[2]); + double sz = sin(euler[2]); + + return Matrix4( + static_cast(cy*cz + sx*sy*-sz), + static_cast(cy*sz + sx*sy*cz), + static_cast(-cx*sy), + 0, + static_cast(cx*-sz), + static_cast(cx*cz), + static_cast(sx), + 0, + static_cast(sy*cz + -sx*cy*-sz), + static_cast(sy*sz + -sx*cy*cz), + static_cast(cx*cy), + 0, + 0, + 0, + 0, + 1 + ); + +#else + + return matrix4_premultiply_by_matrix4( + matrix4_premultiply_by_matrix4( + matrix4_rotation_for_y(euler[1]), + matrix4_rotation_for_x(euler[0]) + ), + matrix4_rotation_for_z(euler[2]) + ); + +#endif +} + +/// \brief Constructs a pure-rotation matrix from a set of euler angles (degrees) in the order (y, x, z). +inline Matrix4 matrix4_rotation_for_euler_yxz_degrees(const Vector3& euler) +{ + return matrix4_rotation_for_euler_yxz(euler_degrees_to_radians(euler)); +} + +/// \brief Returns \p self concatenated with the rotation transform produced by \p euler angles (degrees) in the order (y, x, z). +/// The concatenated rotation occurs before \p self. +inline Matrix4 matrix4_rotated_by_euler_yxz_degrees(const Matrix4& self, const Vector3& euler) +{ + return matrix4_multiplied_by_matrix4(self, matrix4_rotation_for_euler_yxz_degrees(euler)); +} + +/// \brief Concatenates \p self with the rotation transform produced by \p euler angles (degrees) in the order (y, x, z). +/// The concatenated rotation occurs before \p self. +inline void matrix4_rotate_by_euler_yxz_degrees(Matrix4& self, const Vector3& euler) +{ + self = matrix4_rotated_by_euler_yxz_degrees(self, euler); +} + +/// \brief Constructs a pure-rotation matrix from a set of euler angles (radians) in the order (z, x, y). +inline Matrix4 matrix4_rotation_for_euler_zxy(const Vector3& euler) +{ +#if 1 + return matrix4_premultiplied_by_matrix4( + matrix4_premultiplied_by_matrix4( + matrix4_rotation_for_z(euler[2]), + matrix4_rotation_for_x(euler[0]) + ), + matrix4_rotation_for_y(euler[1]) + ); +#else + double cx = cos(euler[0]); + double sx = sin(euler[0]); + double cy = cos(euler[1]); + double sy = sin(euler[1]); + double cz = cos(euler[2]); + double sz = sin(euler[2]); + + return Matrix4( + static_cast(cz * cy + sz * sx * sy), + static_cast(sz * cx), + static_cast(cz * -sy + sz * sx * cy), + 0, + static_cast(-sz * cy + cz * sx * sy), + static_cast(cz * cx), + static_cast(-sz * -sy + cz * cx * cy), + 0, + static_cast(cx* sy), + static_cast(-sx), + static_cast(cx* cy), + 0, + 0, + 0, + 0, + 1 + ); +#endif +} + +/// \brief Constructs a pure-rotation matrix from a set of euler angles (degres=es) in the order (z, x, y). +inline Matrix4 matrix4_rotation_for_euler_zxy_degrees(const Vector3& euler) +{ + return matrix4_rotation_for_euler_zxy(euler_degrees_to_radians(euler)); +} + +/// \brief Returns \p self concatenated with the rotation transform produced by \p euler angles (degrees) in the order (z, x, y). +/// The concatenated rotation occurs before \p self. +inline Matrix4 matrix4_rotated_by_euler_zxy_degrees(const Matrix4& self, const Vector3& euler) +{ + return matrix4_multiplied_by_matrix4(self, matrix4_rotation_for_euler_zxy_degrees(euler)); +} + +/// \brief Concatenates \p self with the rotation transform produced by \p euler angles (degrees) in the order (z, x, y). +/// The concatenated rotation occurs before \p self. +inline void matrix4_rotate_by_euler_zxy_degrees(Matrix4& self, const Vector3& euler) +{ + self = matrix4_rotated_by_euler_zxy_degrees(self, euler); +} + +/// \brief Constructs a pure-rotation matrix from a set of euler angles (radians) in the order (z, y, x). +inline Matrix4 matrix4_rotation_for_euler_zyx(const Vector3& euler) +{ +#if 1 + + double cx = cos(euler[0]); + double sx = sin(euler[0]); + double cy = cos(euler[1]); + double sy = sin(euler[1]); + double cz = cos(euler[2]); + double sz = sin(euler[2]); + + return Matrix4( + static_cast(cy*cz), + static_cast(sx*sy*cz + cx*sz), + static_cast(cx*-sy*cz + sx*sz), + 0, + static_cast(cy*-sz), + static_cast(sx*sy*-sz + cx*cz), + static_cast(cx*-sy*-sz + sx*cz), + 0, + static_cast(sy), + static_cast(-sx*cy), + static_cast(cx*cy), + 0, + 0, + 0, + 0, + 1 + ); + +#else + + return matrix4_premultiply_by_matrix4( + matrix4_premultiply_by_matrix4( + matrix4_rotation_for_z(euler[2]), + matrix4_rotation_for_y(euler[1]) + ), + matrix4_rotation_for_x(euler[0]) + ); + +#endif +} + +/// \brief Constructs a pure-rotation matrix from a set of euler angles (degrees) in the order (z, y, x). +inline Matrix4 matrix4_rotation_for_euler_zyx_degrees(const Vector3& euler) +{ + return matrix4_rotation_for_euler_zyx(euler_degrees_to_radians(euler)); +} + + +/// \brief Calculates and returns a set of euler angles that produce the rotation component of \p self when applied in the order (x, y, z). +/// \p self must be affine and orthonormal (unscaled) to produce a meaningful result. +inline Vector3 matrix4_get_rotation_euler_xyz(const Matrix4& self) +{ + double a = asin(-self[2]); + double ca = cos(a); + + if (fabs(ca) > 0.005) // Gimbal lock? + { + return Vector3( + static_cast(atan2(self[6] / ca, self[10] / ca)), + static_cast(a), + static_cast(atan2(self[1] / ca, self[0]/ ca)) + ); + } + else // Gimbal lock has occurred + { + return Vector3( + static_cast(atan2(-self[9], self[5])), + static_cast(a), + 0 + ); + } +} + +/// \brief \copydoc matrix4_get_rotation_euler_xyz(const Matrix4&) +inline Vector3 matrix4_get_rotation_euler_xyz_degrees(const Matrix4& self) +{ + return euler_radians_to_degrees(matrix4_get_rotation_euler_xyz(self)); +} + +/// \brief Calculates and returns a set of euler angles that produce the rotation component of \p self when applied in the order (y, x, z). +/// \p self must be affine and orthonormal (unscaled) to produce a meaningful result. +inline Vector3 matrix4_get_rotation_euler_yxz(const Matrix4& self) +{ + double a = asin(self[6]); + double ca = cos(a); + + if (fabs(ca) > 0.005) // Gimbal lock? + { + return Vector3( + static_cast(a), + static_cast(atan2(-self[2] / ca, self[10]/ ca)), + static_cast(atan2(-self[4] / ca, self[5] / ca)) + ); + } + else // Gimbal lock has occurred + { + return Vector3( + static_cast(a), + static_cast(atan2(self[8], self[0])), + 0 + ); + } +} + +/// \brief \copydoc matrix4_get_rotation_euler_yxz(const Matrix4&) +inline Vector3 matrix4_get_rotation_euler_yxz_degrees(const Matrix4& self) +{ + return euler_radians_to_degrees(matrix4_get_rotation_euler_yxz(self)); +} + +/// \brief Calculates and returns a set of euler angles that produce the rotation component of \p self when applied in the order (z, x, y). +/// \p self must be affine and orthonormal (unscaled) to produce a meaningful result. +inline Vector3 matrix4_get_rotation_euler_zxy(const Matrix4& self) +{ + double a = asin(-self[9]); + double ca = cos(a); + + if (fabs(ca) > 0.005) // Gimbal lock? + { + return Vector3( + static_cast(a), + static_cast(atan2(self[8] / ca, self[10] / ca)), + static_cast(atan2(self[1] / ca, self[5]/ ca)) + ); + } + else // Gimbal lock has occurred + { + return Vector3( + static_cast(a), + 0, + static_cast(atan2(-self[4], self[0])) + ); + } +} + +/// \brief \copydoc matrix4_get_rotation_euler_zxy(const Matrix4&) +inline Vector3 matrix4_get_rotation_euler_zxy_degrees(const Matrix4& self) +{ + return euler_radians_to_degrees(matrix4_get_rotation_euler_zxy(self)); +} + +/// \brief Calculates and returns a set of euler angles that produce the rotation component of \p self when applied in the order (z, y, x). +/// \p self must be affine and orthonormal (unscaled) to produce a meaningful result. +inline Vector3 matrix4_get_rotation_euler_zyx(const Matrix4& self) +{ + double a = asin(self[8]); + double ca = cos(a); + + if (fabs(ca) > 0.005) // Gimbal lock? + { + return Vector3( + static_cast(atan2(-self[9] / ca, self[10]/ ca)), + static_cast(a), + static_cast(atan2(-self[4] / ca, self[0] / ca)) + ); + } + else // Gimbal lock has occurred + { + return Vector3( + 0, + static_cast(a), + static_cast(atan2(self[1], self[5])) + ); + } +} + +/// \brief \copydoc matrix4_get_rotation_euler_zyx(const Matrix4&) +inline Vector3 matrix4_get_rotation_euler_zyx_degrees(const Matrix4& self) +{ + return euler_radians_to_degrees(matrix4_get_rotation_euler_zyx(self)); +} + + +/// \brief Rotate \p self by \p euler angles (degrees) applied in the order (x, y, z), using \p pivotpoint. +inline void matrix4_pivoted_rotate_by_euler_xyz_degrees(Matrix4& self, const Vector3& euler, const Vector3& pivotpoint) +{ + matrix4_translate_by_vec3(self, pivotpoint); + matrix4_rotate_by_euler_xyz_degrees(self, euler); + matrix4_translate_by_vec3(self, vector3_negated(pivotpoint)); +} + + +/// \brief Constructs a pure-scale matrix from \p scale. +inline Matrix4 matrix4_scale_for_vec3(const Vector3& scale) +{ + return Matrix4( + scale[0], 0, 0, 0, + 0, scale[1], 0, 0, + 0, 0, scale[2], 0, + 0, 0, 0, 1 + ); +} + +/// \brief Calculates and returns the (x, y, z) scale values that produce the scale component of \p self. +/// \p self must be affine and orthogonal to produce a meaningful result. +inline Vector3 matrix4_get_scale_vec3(const Matrix4& self) +{ + return Vector3( + static_cast(vector3_length(vector4_to_vector3(self.x()))), + static_cast(vector3_length(vector4_to_vector3(self.y()))), + static_cast(vector3_length(vector4_to_vector3(self.z()))) + ); +} + +/// \brief Scales \p self by \p scale. +inline void matrix4_scale_by_vec3(Matrix4& self, const Vector3& scale) +{ + matrix4_multiply_by_matrix4(self, matrix4_scale_for_vec3(scale)); +} + +/// \brief Scales \p self by \p scale, using \p pivotpoint. +inline void matrix4_pivoted_scale_by_vec3(Matrix4& self, const Vector3& scale, const Vector3& pivotpoint) +{ + matrix4_translate_by_vec3(self, pivotpoint); + matrix4_scale_by_vec3(self, scale); + matrix4_translate_by_vec3(self, vector3_negated(pivotpoint)); +} + + +/// \brief Transforms \p self by \p translation, \p euler and \p scale. +/// The transforms are combined in the order: scale, rotate-z, rotate-y, rotate-x, translate. +inline void matrix4_transform_by_euler_xyz_degrees(Matrix4& self, const Vector3& translation, const Vector3& euler, const Vector3& scale) +{ + matrix4_translate_by_vec3(self, translation); + matrix4_rotate_by_euler_xyz_degrees(self, euler); + matrix4_scale_by_vec3(self, scale); +} + +/// \brief Transforms \p self by \p translation, \p euler and \p scale, using \p pivotpoint. +inline void matrix4_pivoted_transform_by_euler_xyz_degrees(Matrix4& self, const Vector3& translation, const Vector3& euler, const Vector3& scale, const Vector3& pivotpoint) +{ + matrix4_translate_by_vec3(self, pivotpoint + translation); + matrix4_rotate_by_euler_xyz_degrees(self, euler); + matrix4_scale_by_vec3(self, scale); + matrix4_translate_by_vec3(self, vector3_negated(pivotpoint)); +} + + +#endif diff --git a/tools/urt/libs/math/pi.cpp b/tools/urt/libs/math/pi.cpp new file mode 100644 index 00000000..cdf481db --- /dev/null +++ b/tools/urt/libs/math/pi.cpp @@ -0,0 +1,3 @@ + +#include "pi.h" + diff --git a/tools/urt/libs/math/pi.h b/tools/urt/libs/math/pi.h new file mode 100644 index 00000000..5b9089c5 --- /dev/null +++ b/tools/urt/libs/math/pi.h @@ -0,0 +1,25 @@ + +#if !defined(INCLUDED_MATH_PI_H) +#define INCLUDED_MATH_PI_H + +/// \file +/// \brief Pi constants and degrees/radians conversion. + +const double c_pi = 3.1415926535897932384626433832795; +const double c_half_pi = c_pi / 2; +const double c_2pi = 2 * c_pi; +const double c_inv_2pi = 1 / c_2pi; + +const double c_DEG2RADMULT = c_pi / 180.0; +const double c_RAD2DEGMULT = 180.0 / c_pi; + +inline double radians_to_degrees(double radians) +{ + return radians * c_RAD2DEGMULT; +} +inline double degrees_to_radians(double degrees) +{ + return degrees * c_DEG2RADMULT; +} + +#endif diff --git a/tools/urt/libs/math/plane.cpp b/tools/urt/libs/math/plane.cpp new file mode 100644 index 00000000..38a44241 --- /dev/null +++ b/tools/urt/libs/math/plane.cpp @@ -0,0 +1,3 @@ + +#include "plane.h" + diff --git a/tools/urt/libs/math/plane.h b/tools/urt/libs/math/plane.h new file mode 100644 index 00000000..cba70355 --- /dev/null +++ b/tools/urt/libs/math/plane.h @@ -0,0 +1,133 @@ + +#if !defined(INCLUDED_MATH_PLANE_H) +#define INCLUDED_MATH_PLANE_H + +/// \file +/// \brief Plane data types and related operations. + +#include "math/matrix.h" + +/// \brief A plane equation stored in double-precision floating-point. +class Plane3 +{ +public: + double a, b, c, d; + + Plane3() + { + } + Plane3(double _a, double _b, double _c, double _d) + : a(_a), b(_b), c(_c), d(_d) + { + } + template + Plane3(const BasicVector3& normal, double dist) + : a(normal.x()), b(normal.y()), c(normal.z()), d(dist) + { + } + + BasicVector3& normal() + { + return reinterpret_cast&>(*this); + } + const BasicVector3& normal() const + { + return reinterpret_cast&>(*this); + } + double& dist() + { + return d; + } + const double& dist() const + { + return d; + } +}; + +inline Plane3 plane3_normalised(const Plane3& plane) +{ + double rmagnitude = 1.0 / sqrt(plane.a * plane.a + plane.b * plane.b + plane.c * plane.c); + return Plane3( + plane.a * rmagnitude, + plane.b * rmagnitude, + plane.c * rmagnitude, + plane.d * rmagnitude + ); +} + +inline Plane3 plane3_translated(const Plane3& plane, const Vector3& translation) +{ + Plane3 transformed; + transformed.a = plane.a; + transformed.b = plane.b; + transformed.c = plane.c; + transformed.d = -((-plane.d * transformed.a + translation.x()) * transformed.a + + (-plane.d * transformed.b + translation.y()) * transformed.b + + (-plane.d * transformed.c + translation.z()) * transformed.c); + return transformed; +} + +inline Plane3 plane3_transformed(const Plane3& plane, const Matrix4& transform) +{ + Plane3 transformed; + transformed.a = transform[0] * plane.a + transform[4] * plane.b + transform[8] * plane.c; + transformed.b = transform[1] * plane.a + transform[5] * plane.b + transform[9] * plane.c; + transformed.c = transform[2] * plane.a + transform[6] * plane.b + transform[10] * plane.c; + transformed.d = -((-plane.d * transformed.a + transform[12]) * transformed.a + + (-plane.d * transformed.b + transform[13]) * transformed.b + + (-plane.d * transformed.c + transform[14]) * transformed.c); + return transformed; +} + +inline Plane3 plane3_inverse_transformed(const Plane3& plane, const Matrix4& transform) +{ + return Plane3 + ( + transform[ 0] * plane.a + transform[ 1] * plane.b + transform[ 2] * plane.c + transform[ 3] * plane.d, + transform[ 4] * plane.a + transform[ 5] * plane.b + transform[ 6] * plane.c + transform[ 7] * plane.d, + transform[ 8] * plane.a + transform[ 9] * plane.b + transform[10] * plane.c + transform[11] * plane.d, + transform[12] * plane.a + transform[13] * plane.b + transform[14] * plane.c + transform[15] * plane.d + ); +} + +inline Plane3 plane3_flipped(const Plane3& plane) +{ + return Plane3(vector3_negated(plane.normal()), -plane.dist()); +} + +const double c_PLANE_NORMAL_EPSILON = 0.0001f; +const double c_PLANE_DIST_EPSILON = 0.02; + +inline bool plane3_equal(const Plane3& self, const Plane3& other) +{ + return vector3_equal_epsilon(self.normal(), other.normal(), c_PLANE_NORMAL_EPSILON) + && float_equal_epsilon(self.dist(), other.dist(), c_PLANE_DIST_EPSILON); +} + +inline bool plane3_opposing(const Plane3& self, const Plane3& other) +{ + return plane3_equal(self, plane3_flipped(other)); +} + +inline bool plane3_valid(const Plane3& self) +{ + return float_equal_epsilon(vector3_dot(self.normal(), self.normal()), 1.0, 0.01); +} + +template +inline Plane3 plane3_for_points(const BasicVector3& p0, const BasicVector3& p1, const BasicVector3& p2) +{ + Plane3 self; + self.normal() = vector3_normalised(vector3_cross(vector3_subtracted(p1, p0), vector3_subtracted(p2, p0))); + self.dist() = vector3_dot(p0, self.normal()); + return self; +} + +template +inline Plane3 plane3_for_points(const BasicVector3 planepts[3]) +{ + return plane3_for_points(planepts[2], planepts[1], planepts[0]); +} + + +#endif diff --git a/tools/urt/libs/math/quaternion.cpp b/tools/urt/libs/math/quaternion.cpp new file mode 100644 index 00000000..eae3ba00 --- /dev/null +++ b/tools/urt/libs/math/quaternion.cpp @@ -0,0 +1,3 @@ + +#include "quaternion.h" + diff --git a/tools/urt/libs/math/quaternion.h b/tools/urt/libs/math/quaternion.h new file mode 100644 index 00000000..dd4ff1bc --- /dev/null +++ b/tools/urt/libs/math/quaternion.h @@ -0,0 +1,289 @@ + +#if !defined(INCLUDED_MATH_QUATERNION_H) +#define INCLUDED_MATH_QUATERNION_H + +/// \file +/// \brief Quaternion data types and related operations. + +#include "math/matrix.h" + +/// \brief A quaternion stored in single-precision floating-point. +typedef Vector4 Quaternion; + +const Quaternion c_quaternion_identity(0, 0, 0, 1); + +inline Quaternion quaternion_multiplied_by_quaternion(const Quaternion& quaternion, const Quaternion& other) +{ + return Quaternion( + quaternion[3]*other[0] + quaternion[0]*other[3] + quaternion[1]*other[2] - quaternion[2]*other[1], + quaternion[3]*other[1] + quaternion[1]*other[3] + quaternion[2]*other[0] - quaternion[0]*other[2], + quaternion[3]*other[2] + quaternion[2]*other[3] + quaternion[0]*other[1] - quaternion[1]*other[0], + quaternion[3]*other[3] - quaternion[0]*other[0] - quaternion[1]*other[1] - quaternion[2]*other[2] + ); +} + +inline void quaternion_multiply_by_quaternion(Quaternion& quaternion, const Quaternion& other) +{ + quaternion = quaternion_multiplied_by_quaternion(quaternion, other); +} + +/// \brief Constructs a quaternion which rotates between two points on the unit-sphere, \p from and \p to. +inline Quaternion quaternion_for_unit_vectors(const Vector3& from, const Vector3& to) +{ + return Quaternion(vector3_cross(from, to), static_cast(vector3_dot(from, to))); +} + +inline Quaternion quaternion_for_axisangle(const Vector3& axis, double angle) +{ + angle *= 0.5; + float sa = static_cast(sin(angle)); + return Quaternion(axis[0] * sa, axis[1] * sa, axis[2] * sa, static_cast(cos(angle))); +} + +inline Quaternion quaternion_inverse(const Quaternion& quaternion) +{ + return Quaternion(vector3_negated(vector4_to_vector3(quaternion)), quaternion[3]); +} + +inline void quaternion_conjugate(Quaternion& quaternion) +{ + quaternion = quaternion_inverse(quaternion); +} + +inline Quaternion quaternion_normalised(const Quaternion& quaternion) +{ + const double n = (1.0 / (quaternion[0] * quaternion[0] + quaternion[1] * quaternion[1] + quaternion[2] * quaternion[2] + quaternion[3] * quaternion[3])); + return Quaternion( + static_cast(quaternion[0] * n), + static_cast(quaternion[1] * n), + static_cast(quaternion[2] * n), + static_cast(quaternion[3] * n) + ); +} + +inline void quaternion_normalise(Quaternion& quaternion) +{ + quaternion = quaternion_normalised(quaternion); +} + +/// \brief Constructs a pure-rotation matrix from \p quaternion. +inline Matrix4 matrix4_rotation_for_quaternion(const Quaternion& quaternion) +{ +#if 0 + const double xx = quaternion[0] * quaternion[0]; + const double xy = quaternion[0] * quaternion[1]; + const double xz = quaternion[0] * quaternion[2]; + const double xw = quaternion[0] * quaternion[3]; + + const double yy = quaternion[1] * quaternion[1]; + const double yz = quaternion[1] * quaternion[2]; + const double yw = quaternion[1] * quaternion[3]; + + const double zz = quaternion[2] * quaternion[2]; + const double zw = quaternion[2] * quaternion[3]; + + return Matrix4( + static_cast( 1 - 2 * ( yy + zz ) ), + static_cast( 2 * ( xy + zw ) ), + static_cast( 2 * ( xz - yw ) ), + 0, + static_cast( 2 * ( xy - zw ) ), + static_cast( 1 - 2 * ( xx + zz ) ), + static_cast( 2 * ( yz + xw ) ), + 0, + static_cast( 2 * ( xz + yw ) ), + static_cast( 2 * ( yz - xw ) ), + static_cast( 1 - 2 * ( xx + yy ) ), + 0, + 0, + 0, + 0, + 1 + ); + +#else + const double x2 = quaternion[0] + quaternion[0]; + const double y2 = quaternion[1] + quaternion[1]; + const double z2 = quaternion[2] + quaternion[2]; + const double xx = quaternion[0] * x2; + const double xy = quaternion[0] * y2; + const double xz = quaternion[0] * z2; + const double yy = quaternion[1] * y2; + const double yz = quaternion[1] * z2; + const double zz = quaternion[2] * z2; + const double wx = quaternion[3] * x2; + const double wy = quaternion[3] * y2; + const double wz = quaternion[3] * z2; + + return Matrix4( + static_cast( 1.0 - (yy + zz) ), + static_cast(xy + wz), + static_cast(xz - wy), + 0, + static_cast(xy - wz), + static_cast( 1.0 - (xx + zz) ), + static_cast(yz + wx), + 0, + static_cast(xz + wy), + static_cast(yz - wx), + static_cast( 1.0 - (xx + yy) ), + 0, + 0, + 0, + 0, + 1 + ); + +#endif +} + +const double c_half_sqrt2 = 0.70710678118654752440084436210485; +const float c_half_sqrt2f = static_cast(c_half_sqrt2); + +inline bool quaternion_component_is_90(float component) +{ + return (fabs(component) - c_half_sqrt2) < 0.001; +} + +inline Matrix4 matrix4_rotation_for_quaternion_quantised(const Quaternion& quaternion) +{ + if(quaternion.y() == 0 + && quaternion.z() == 0 + && quaternion_component_is_90(quaternion.x()) + && quaternion_component_is_90(quaternion.w())) + { + return matrix4_rotation_for_sincos_x((quaternion.x() > 0) ? 1.f : -1.f, 0); + } + + if(quaternion.x() == 0 + && quaternion.z() == 0 + && quaternion_component_is_90(quaternion.y()) + && quaternion_component_is_90(quaternion.w())) + { + return matrix4_rotation_for_sincos_y((quaternion.y() > 0) ? 1.f : -1.f, 0); + } + + if(quaternion.x() == 0 + && quaternion.y() == 0 + && quaternion_component_is_90(quaternion.z()) + && quaternion_component_is_90(quaternion.w())) + { + return matrix4_rotation_for_sincos_z((quaternion.z() > 0) ? 1.f : -1.f, 0); + } + + return matrix4_rotation_for_quaternion(quaternion); +} + +inline Quaternion quaternion_for_matrix4_rotation(const Matrix4& matrix4) +{ + Matrix4 transposed = matrix4_transposed(matrix4); + + double trace = transposed[0] + transposed[5] + transposed[10] + 1.0; + + if(trace > 0.0001) + { + double S = 0.5 / sqrt(trace); + return Quaternion( + static_cast((transposed[9] - transposed[6]) * S), + static_cast((transposed[2] - transposed[8]) * S), + static_cast((transposed[4] - transposed[1]) * S), + static_cast(0.25 / S) + ); + } + + if(transposed[0] >= transposed[5] && transposed[0] >= transposed[10]) + { + double S = 2.0 * sqrt(1.0 + transposed[0] - transposed[5] - transposed[10]); + return Quaternion( + static_cast(0.25 / S), + static_cast((transposed[1] + transposed[4]) / S), + static_cast((transposed[2] + transposed[8]) / S), + static_cast((transposed[6] + transposed[9]) / S) + ); + } + + if(transposed[5] >= transposed[0] && transposed[5] >= transposed[10]) + { + double S = 2.0 * sqrt(1.0 + transposed[5] - transposed[0] - transposed[10]); + return Quaternion( + static_cast((transposed[1] + transposed[4]) / S), + static_cast(0.25 / S), + static_cast((transposed[6] + transposed[9]) / S), + static_cast((transposed[2] + transposed[8]) / S) + ); + } + + double S = 2.0 * sqrt(1.0 + transposed[10] - transposed[0] - transposed[5]); + return Quaternion( + static_cast((transposed[2] + transposed[8]) / S), + static_cast((transposed[6] + transposed[9]) / S), + static_cast(0.25 / S), + static_cast((transposed[1] + transposed[4]) / S) + ); +} + +/// \brief Returns \p self concatenated with the rotation transform produced by \p rotation. +/// The concatenated rotation occurs before \p self. +inline Matrix4 matrix4_rotated_by_quaternion(const Matrix4& self, const Quaternion& rotation) +{ + return matrix4_multiplied_by_matrix4(self, matrix4_rotation_for_quaternion(rotation)); +} + +/// \brief Concatenates \p self with the rotation transform produced by \p rotation. +/// The concatenated rotation occurs before \p self. +inline void matrix4_rotate_by_quaternion(Matrix4& self, const Quaternion& rotation) +{ + self = matrix4_rotated_by_quaternion(self, rotation); +} + +/// \brief Rotates \p self by \p rotation, using \p pivotpoint. +inline void matrix4_pivoted_rotate_by_quaternion(Matrix4& self, const Quaternion& rotation, const Vector3& pivotpoint) +{ + matrix4_translate_by_vec3(self, pivotpoint); + matrix4_rotate_by_quaternion(self, rotation); + matrix4_translate_by_vec3(self, vector3_negated(pivotpoint)); +} + +inline Vector3 quaternion_transformed_point(const Quaternion& quaternion, const Vector3& point) +{ + double xx = quaternion.x() * quaternion.x(); + double yy = quaternion.y() * quaternion.y(); + double zz = quaternion.z() * quaternion.z(); + double ww = quaternion.w() * quaternion.w(); + + double xy2 = quaternion.x() * quaternion.y() * 2; + double xz2 = quaternion.x() * quaternion.z() * 2; + double xw2 = quaternion.x() * quaternion.w() * 2; + double yz2 = quaternion.y() * quaternion.z() * 2; + double yw2 = quaternion.y() * quaternion.w() * 2; + double zw2 = quaternion.z() * quaternion.w() * 2; + + return Vector3( + static_cast(ww * point.x() + yw2 * point.z() - zw2 * point.y() + xx * point.x() + xy2 * point.y() + xz2 * point.z() - zz * point.x() - yy * point.x()), + static_cast(xy2 * point.x() + yy * point.y() + yz2 * point.z() + zw2 * point.x() - zz * point.y() + ww * point.y() - xw2 * point.z() - xx * point.y()), + static_cast(xz2 * point.x() + yz2 * point.y() + zz * point.z() - yw2 * point.x() - yy * point.z() + xw2 * point.y() - xx * point.z() + ww * point.z()) + ); +} + +/// \brief Constructs a pure-rotation transform from \p axis and \p angle (radians). +inline Matrix4 matrix4_rotation_for_axisangle(const Vector3& axis, double angle) +{ + return matrix4_rotation_for_quaternion(quaternion_for_axisangle(axis, angle)); +} + +/// \brief Rotates \p self about \p axis by \p angle. +inline void matrix4_rotate_by_axisangle(Matrix4& self, const Vector3& axis, double angle) +{ + matrix4_multiply_by_matrix4(self, matrix4_rotation_for_axisangle(axis, angle)); +} + +/// \brief Rotates \p self about \p axis by \p angle using \p pivotpoint. +inline void matrix4_pivoted_rotate_by_axisangle(Matrix4& self, const Vector3& axis, double angle, const Vector3& pivotpoint) +{ + matrix4_translate_by_vec3(self, pivotpoint); + matrix4_rotate_by_axisangle(self, axis, angle); + matrix4_translate_by_vec3(self, vector3_negated(pivotpoint)); +} + + +#endif diff --git a/tools/urt/libs/math/vector.cpp b/tools/urt/libs/math/vector.cpp new file mode 100644 index 00000000..8092f23a --- /dev/null +++ b/tools/urt/libs/math/vector.cpp @@ -0,0 +1,3 @@ + +#include "vector.h" + diff --git a/tools/urt/libs/math/vector.h b/tools/urt/libs/math/vector.h new file mode 100644 index 00000000..1710128a --- /dev/null +++ b/tools/urt/libs/math/vector.h @@ -0,0 +1,933 @@ + +#if !defined(INCLUDED_MATH_VECTOR_H) +#define INCLUDED_MATH_VECTOR_H + +/// \file +/// \brief Vector data types and related operations. + +#if 0 + +#define lrint(dbl) ((int)((dbl) + 0.5)) +#define lrintf(flt) ((int)((flt) + 0.5)) + +#endif + +#if defined (_MSC_VER) + +inline int lrint (double flt) +{ + int i; + + _asm + { + fld flt + fistp i + }; + + return i; +} + +#else // lrint is part of ISO C99 + +#define _ISOC9X_SOURCE 1 +#define _ISOC99_SOURCE 1 + +#define __USE_ISOC9X 1 +#define __USE_ISOC99 1 + +#endif + +#include +#include +#include +#include + +//#include "debugging/debugging.h" + +/// \brief Returns true if \p self is equal to other \p other within \p epsilon. +template +inline bool float_equal_epsilon(const Element& self, const OtherElement& other, const Element& epsilon) +{ + return fabs(other - self) < epsilon; +} + +/// \brief Returns the value midway between \p self and \p other. +template +inline Element float_mid(const Element& self, const Element& other) +{ + return Element((self + other) * 0.5); +} + +/// \brief Returns \p f rounded to the nearest integer. Note that this is not the same behaviour as casting from float to int. +template +inline int float_to_integer(const Element& f) +{ + return lrint(f); +} + +/// \brief Returns \p f rounded to the nearest multiple of \snap. +template +inline Element float_snapped(const Element& f, const OtherElement& snap) +{ + return Element(float_to_integer(f / snap) * snap); +} + +/// \brief Returns true if \p f has no decimal fraction part. +template +inline bool float_is_integer(const Element& f) +{ + return f == Element(float_to_integer(f)); +} + +/// \brief Returns \p self modulated by the range [0, \p modulus) +/// \p self must be in the range [\p -modulus, \p modulus) +template +inline Element float_mod_range(const Element& self, const ModulusElement& modulus) +{ + return Element((self < 0.0) ? self + modulus : self); +} + +/// \brief Returns \p self modulated by the range [0, \p modulus) +template +inline Element float_mod(const Element& self, const ModulusElement& modulus) +{ + return float_mod_range(Element(fmod(static_cast(self), static_cast(modulus))), modulus); +} + + +template +class BasicVector2 +{ + Element m_elements[2]; +public: + BasicVector2() + { + } + BasicVector2(const Element& x_, const Element& y_) + { + x() = x_; + y() = y_; + } + + Element& x() + { + return m_elements[0]; + } + const Element& x() const + { + return m_elements[0]; + } + Element& y() + { + return m_elements[1]; + } + const Element& y() const + { + return m_elements[1]; + } + + const Element& operator[](std::size_t i) const + { + return m_elements[i]; + } + Element& operator[](std::size_t i) + { + return m_elements[i]; + } +}; + + +template +inline BasicVector2 vector2_added(const BasicVector2& self, const BasicVector2& other) +{ + return BasicVector2( + Element(self.x() + other.x()), + Element(self.y() + other.y()) + ); +} +template +inline BasicVector2 operator+(const BasicVector2& self, const BasicVector2& other) +{ + return vector2_added(self, other); +} +template +inline void vector2_add(BasicVector2& self, const BasicVector2& other) +{ + self.x() += Element(other.x()); + self.y() += Element(other.y()); +} +template +inline void operator+=(BasicVector2& self, const BasicVector2& other) +{ + vector2_add(self, other); +} + + +template +inline BasicVector2 vector2_subtracted(const BasicVector2& self, const BasicVector2& other) +{ + return BasicVector2( + Element(self.x() - other.x()), + Element(self.y() - other.y()) + ); +} +template +inline BasicVector2 operator-(const BasicVector2& self, const BasicVector2& other) +{ + return vector2_subtracted(self, other); +} +template +inline void vector2_subtract(BasicVector2& self, const BasicVector2& other) +{ + self.x() -= Element(other.x()); + self.y() -= lement(other.y()); +} +template +inline void operator-=(BasicVector2& self, const BasicVector2& other) +{ + vector2_subtract(self, other); +} + + +template +inline BasicVector2 vector2_scaled(const BasicVector2& self, OtherElement other) +{ + return BasicVector2( + Element(self.x() * other), + Element(self.y() * other) + ); +} +template +inline BasicVector2 operator*(const BasicVector2& self, OtherElement other) +{ + return vector2_scaled(self, other); +} +template +inline void vector2_scale(BasicVector2& self, OtherElement other) +{ + self.x() *= Element(other); + self.y() *= Element(other); +} +template +inline void operator*=(BasicVector2& self, OtherElement other) +{ + vector2_scale(self, other); +} + + +template +inline BasicVector2 vector2_scaled(const BasicVector2& self, const BasicVector2& other) +{ + return BasicVector2( + Element(self.x() * other.x()), + Element(self.y() * other.y()) + ); +} +template +inline BasicVector2 operator*(const BasicVector2& self, const BasicVector2& other) +{ + return vector2_scaled(self, other); +} +template +inline void vector2_scale(BasicVector2& self, const BasicVector2& other) +{ + self.x() *= Element(other.x()); + self.y() *= Element(other.y()); +} +template +inline void operator*=(BasicVector2& self, const BasicVector2& other) +{ + vector2_scale(self, other); +} + +template +inline BasicVector2 vector2_divided(const BasicVector2& self, const BasicVector2& other) +{ + return BasicVector2( + Element(self.x() / other.x()), + Element(self.y() / other.y()) + ); +} +template +inline BasicVector2 operator/(const BasicVector2& self, const BasicVector2& other) +{ + return vector2_divided(self, other); +} +template +inline void vector2_divide(BasicVector2& self, const BasicVector2& other) +{ + self.x() /= Element(other.x()); + self.y() /= Element(other.y()); +} +template +inline void operator/=(BasicVector2& self, const BasicVector2& other) +{ + vector2_divide(self, other); +} + + +template +inline BasicVector2 vector2_divided(const BasicVector2& self, OtherElement other) +{ + return BasicVector2( + Element(self.x() / other), + Element(self.y() / other) + ); +} +template +inline BasicVector2 operator/(const BasicVector2& self, OtherElement other) +{ + return vector2_divided(self, other); +} +template +inline void vector2_divide(BasicVector2& self, OtherElement other) +{ + self.x() /= Element(other); + self.y() /= Element(other); +} +template +inline void operator/=(BasicVector2& self, OtherElement other) +{ + vector2_divide(self, other); +} + +template +inline double vector2_dot(const BasicVector2& self, const BasicVector2& other) +{ + return self.x() * other.x() + self.y() * other.y(); +} + +template +inline double vector2_length_squared(const BasicVector2& self) +{ + return vector2_dot(self, self); +} + + +typedef BasicVector2 Vector2; + +/// \brief A 3-element vector. +template +class BasicVector3 +{ + Element m_elements[3]; +public: + + BasicVector3() + { + } + template + BasicVector3(const BasicVector3& other) + { + x() = static_cast(other.x()); + y() = static_cast(other.y()); + z() = static_cast(other.z()); + } + BasicVector3(const Element& x_, const Element& y_, const Element& z_) + { + x() = x_; + y() = y_; + z() = z_; + } + + Element& x() + { + return m_elements[0]; + } + const Element& x() const + { + return m_elements[0]; + } + Element& y() + { + return m_elements[1]; + } + const Element& y() const + { + return m_elements[1]; + } + Element& z() + { + return m_elements[2]; + } + const Element& z() const + { + return m_elements[2]; + } + + const Element& operator[](std::size_t i) const + { + return m_elements[i]; + } + Element& operator[](std::size_t i) + { + return m_elements[i]; + } + + operator BasicVector2&() + { + return reinterpret_cast&>(*this); + } + operator const BasicVector2&() const + { + return reinterpret_cast&>(*this); + } +}; + +/// \brief A 3-element vector stored in single-precision floating-point. +typedef BasicVector3 Vector3; + +const Vector3 g_vector3_identity(0, 0, 0); +const Vector3 g_vector3_max = Vector3(FLT_MAX, FLT_MAX, FLT_MAX); +const Vector3 g_vector3_axis_x(1, 0, 0); +const Vector3 g_vector3_axis_y(0, 1, 0); +const Vector3 g_vector3_axis_z(0, 0, 1); + +const Vector3 g_vector3_axes[3] = { g_vector3_axis_x, g_vector3_axis_y, g_vector3_axis_z }; + +template +inline Element* vector3_to_array(BasicVector3& self) +{ + return reinterpret_cast(&self); +} +template +inline const Element* vector3_to_array(const BasicVector3& self) +{ + return reinterpret_cast(&self); +} + +template +inline void vector3_swap(BasicVector3& self, BasicVector3& other) +{ + std::swap(self.x(), other.x()); + std::swap(self.y(), other.y()); + std::swap(self.z(), other.z()); +} + +template +inline bool vector3_equal(const BasicVector3& self, const BasicVector3& other) +{ + return self.x() == other.x() && self.y() == other.y() && self.z() == other.z(); +} +template +inline bool operator==(const BasicVector3& self, const BasicVector3& other) +{ + return vector3_equal(self, other); +} +template +inline bool operator!=(const BasicVector3& self, const BasicVector3& other) +{ + return !vector3_equal(self, other); +} + + +template +inline bool vector3_equal_epsilon(const BasicVector3& self, const BasicVector3& other, Epsilon epsilon) +{ + return float_equal_epsilon(self.x(), other.x(), epsilon) + && float_equal_epsilon(self.y(), other.y(), epsilon) + && float_equal_epsilon(self.z(), other.z(), epsilon); +} + + + +template +inline BasicVector3 vector3_added(const BasicVector3& self, const BasicVector3& other) +{ + return BasicVector3( + Element(self.x() + other.x()), + Element(self.y() + other.y()), + Element(self.z() + other.z()) + ); +} +template +inline BasicVector3 operator+(const BasicVector3& self, const BasicVector3& other) +{ + return vector3_added(self, other); +} +template +inline void vector3_add(BasicVector3& self, const BasicVector3& other) +{ + self.x() += static_cast(other.x()); + self.y() += static_cast(other.y()); + self.z() += static_cast(other.z()); +} +template +inline void operator+=(BasicVector3& self, const BasicVector3& other) +{ + vector3_add(self, other); +} + +template +inline BasicVector3 vector3_subtracted(const BasicVector3& self, const BasicVector3& other) +{ + return BasicVector3( + Element(self.x() - other.x()), + Element(self.y() - other.y()), + Element(self.z() - other.z()) + ); +} +template +inline BasicVector3 operator-(const BasicVector3& self, const BasicVector3& other) +{ + return vector3_subtracted(self, other); +} +template +inline void vector3_subtract(BasicVector3& self, const BasicVector3& other) +{ + self.x() -= static_cast(other.x()); + self.y() -= static_cast(other.y()); + self.z() -= static_cast(other.z()); +} +template +inline void operator-=(BasicVector3& self, const BasicVector3& other) +{ + vector3_subtract(self, other); +} + +template +inline BasicVector3 vector3_scaled(const BasicVector3& self, const BasicVector3& other) +{ + return BasicVector3( + Element(self.x() * other.x()), + Element(self.y() * other.y()), + Element(self.z() * other.z()) + ); +} +template +inline BasicVector3 operator*(const BasicVector3& self, const BasicVector3& other) +{ + return vector3_scaled(self, other); +} +template +inline void vector3_scale(BasicVector3& self, const BasicVector3& other) +{ + self.x() *= static_cast(other.x()); + self.y() *= static_cast(other.y()); + self.z() *= static_cast(other.z()); +} +template +inline void operator*=(BasicVector3& self, const BasicVector3& other) +{ + vector3_scale(self, other); +} + +template +inline BasicVector3 vector3_scaled(const BasicVector3& self, const OtherElement& scale) +{ + return BasicVector3( + Element(self.x() * scale), + Element(self.y() * scale), + Element(self.z() * scale) + ); +} +template +inline BasicVector3 operator*(const BasicVector3& self, const OtherElement& scale) +{ + return vector3_scaled(self, scale); +} +template +inline void vector3_scale(BasicVector3& self, const OtherElement& scale) +{ + self.x() *= static_cast(scale); + self.y() *= static_cast(scale); + self.z() *= static_cast(scale); +} +template +inline void operator*=(BasicVector3& self, const OtherElement& scale) +{ + vector3_scale(self, scale); +} + +template +inline BasicVector3 vector3_divided(const BasicVector3& self, const BasicVector3& other) +{ + return BasicVector3( + Element(self.x() / other.x()), + Element(self.y() / other.y()), + Element(self.z() / other.z()) + ); +} +template +inline BasicVector3 operator/(const BasicVector3& self, const BasicVector3& other) +{ + return vector3_divided(self, other); +} +template +inline void vector3_divide(BasicVector3& self, const BasicVector3& other) +{ + self.x() /= static_cast(other.x()); + self.y() /= static_cast(other.y()); + self.z() /= static_cast(other.z()); +} +template +inline void operator/=(BasicVector3& self, const BasicVector3& other) +{ + vector3_divide(self, other); +} + +template +inline BasicVector3 vector3_divided(const BasicVector3& self, const OtherElement& divisor) +{ + return BasicVector3( + Element(self.x() / divisor), + Element(self.y() / divisor), + Element(self.z() / divisor) + ); +} +template +inline BasicVector3 operator/(const BasicVector3& self, const OtherElement& divisor) +{ + return vector3_divided(self, divisor); +} +template +inline void vector3_divide(BasicVector3& self, const OtherElement& divisor) +{ + self.x() /= static_cast(divisor); + self.y() /= static_cast(divisor); + self.z() /= static_cast(divisor); +} +template +inline void operator/=(BasicVector3& self, const OtherElement& divisor) +{ + vector3_divide(self, divisor); +} + +template +inline double vector3_dot(const BasicVector3& self, const BasicVector3& other) +{ + return self.x() * other.x() + self.y() * other.y() + self.z() * other.z(); +} + +template +inline BasicVector3 vector3_mid(const BasicVector3& begin, const BasicVector3& end) +{ + return vector3_scaled(vector3_added(begin, end), 0.5); +} + +template +inline BasicVector3 vector3_cross(const BasicVector3& self, const BasicVector3& other) +{ + return BasicVector3( + Element(self.y() * other.z() - self.z() * other.y()), + Element(self.z() * other.x() - self.x() * other.z()), + Element(self.x() * other.y() - self.y() * other.x()) + ); +} + +template +inline BasicVector3 vector3_negated(const BasicVector3& self) +{ + return BasicVector3(-self.x(), -self.y(), -self.z()); +} + +template +inline void vector3_negate(BasicVector3& self) +{ + self = vector3_negated(self); +} + +template +inline double vector3_length_squared(const BasicVector3& self) +{ + return vector3_dot(self, self); +} + +template +inline double vector3_length(const BasicVector3& self) +{ + return sqrt(vector3_length_squared(self)); +} + +template +inline Element float_divided(Element f, Element other) +{ + //ASSERT_MESSAGE(other != 0, "float_divided: invalid divisor"); + return f / other; +} + +template +inline BasicVector3 vector3_normalised(const BasicVector3& self) +{ + return vector3_scaled(self, float_divided(1.0, vector3_length(self))); +} + +template +inline void vector3_normalise(BasicVector3& self) +{ + self = vector3_normalised(self); +} + + +template +inline BasicVector3 vector3_snapped(const BasicVector3& self) +{ + return BasicVector3( + Element(float_to_integer(self.x())), + Element(float_to_integer(self.y())), + Element(float_to_integer(self.z())) + ); +} +template +inline void vector3_snap(BasicVector3& self) +{ + self = vector3_snapped(self); +} +template +inline BasicVector3 vector3_snapped(const BasicVector3& self, const OtherElement& snap) +{ + return BasicVector3( + Element(float_snapped(self.x(), snap)), + Element(float_snapped(self.y(), snap)), + Element(float_snapped(self.z(), snap)) + ); +} +template +inline void vector3_snap(BasicVector3& self, const OtherElement& snap) +{ + self = vector3_snapped(self, snap); +} + +inline Vector3 vector3_for_spherical(double theta, double phi) +{ + return Vector3( + static_cast(cos(theta) * cos(phi)), + static_cast(sin(theta) * cos(phi)), + static_cast(sin(phi)) + ); +} + + +/// \brief A 4-element vector stored in single-precision floating-point. +class Vector4 +{ + float m_elements[4]; +public: + + Vector4() + { + } + Vector4(float x_, float y_, float z_, float w_) + { + x() = x_; + y() = y_; + z() = z_; + w() = w_; + } + Vector4(const Vector3& self, float w_) + { + x() = self.x(); + y() = self.y(); + z() = self.z(); + w() = w_; + } + + float& x() + { + return m_elements[0]; + } + float x() const + { + return m_elements[0]; + } + float& y() + { + return m_elements[1]; + } + float y() const + { + return m_elements[1]; + } + float& z() + { + return m_elements[2]; + } + float z() const + { + return m_elements[2]; + } + float& w() + { + return m_elements[3]; + } + float w() const + { + return m_elements[3]; + } + + float index(std::size_t i) const + { + return m_elements[i]; + } + float& index(std::size_t i) + { + return m_elements[i]; + } + float operator[](std::size_t i) const + { + return m_elements[i]; + } + float& operator[](std::size_t i) + { + return m_elements[i]; + } + + operator Vector3&() + { + return reinterpret_cast(*this); + } + operator const Vector3&() const + { + return reinterpret_cast(*this); + } + + bool operator==(const Vector4& other) const + { + return x() == other.x() && y() == other.y() && z() == other.z() && w() == other.w(); + } +}; + +inline float* vector4_to_array(Vector4& self) +{ + return reinterpret_cast(&self); +} +inline const float* vector4_to_array(const Vector4& self) +{ + return reinterpret_cast(&self); +} + +inline Vector3& vector4_to_vector3(Vector4& self) +{ + return reinterpret_cast(self); +} +inline const Vector3& vector4_to_vector3(const Vector4& self) +{ + return reinterpret_cast(self); +} + +inline Vector4 vector4_added(const Vector4& self, const Vector4& other) +{ + return Vector4( + float(self.x() + other.x()), + float(self.y() + other.y()), + float(self.z() + other.z()), + float(self.w() + other.w()) + ); +} +inline Vector4 operator+(const Vector4& self, const Vector4& other) +{ + return vector4_added(self, other); +} +inline void vector4_add(Vector4& self, const Vector4& other) +{ + self.x() += static_cast(other.x()); + self.y() += static_cast(other.y()); + self.z() += static_cast(other.z()); + self.w() += static_cast(other.w()); +} +inline void operator+=(Vector4& self, const Vector4& other) +{ + vector4_add(self, other); +} + +inline Vector4 vector4_subtracted(const Vector4& self, const Vector4& other) +{ + return Vector4( + float(self.x() - other.x()), + float(self.y() - other.y()), + float(self.z() - other.z()), + float(self.w() - other.w()) + ); +} +inline Vector4 operator-(const Vector4& self, const Vector4& other) +{ + return vector4_subtracted(self, other); +} +inline void vector4_subtract(Vector4& self, const Vector4& other) +{ + self.x() -= static_cast(other.x()); + self.y() -= static_cast(other.y()); + self.z() -= static_cast(other.z()); + self.w() -= static_cast(other.w()); +} +inline void operator-=(Vector4& self, const Vector4& other) +{ + vector4_subtract(self, other); +} + +inline Vector4 vector4_scaled(const Vector4& self, const Vector4& other) +{ + return Vector4( + float(self.x() * other.x()), + float(self.y() * other.y()), + float(self.z() * other.z()), + float(self.w() * other.w()) + ); +} +inline Vector4 operator*(const Vector4& self, const Vector4& other) +{ + return vector4_scaled(self, other); +} +inline void vector4_scale(Vector4& self, const Vector4& other) +{ + self.x() *= static_cast(other.x()); + self.y() *= static_cast(other.y()); + self.z() *= static_cast(other.z()); + self.w() *= static_cast(other.w()); +} +inline void operator*=(Vector4& self, const Vector4& other) +{ + vector4_scale(self, other); +} + +inline Vector4 vector4_scaled(const Vector4& self, const float& scale) +{ + return Vector4( + float(self.x() * scale), + float(self.y() * scale), + float(self.z() * scale), + float(self.w() * scale) + ); +} +inline Vector4 operator*(const Vector4& self, const float& scale) +{ + return vector4_scaled(self, scale); +} +inline void vector4_scale(Vector4& self, const float& scale) +{ + self.x() *= static_cast(scale); + self.y() *= static_cast(scale); + self.z() *= static_cast(scale); + self.w() *= static_cast(scale); +} +inline void operator*=(Vector4& self, const float& scale) +{ + vector4_scale(self, scale); +} + +inline Vector4 vector4_divided(const Vector4& self, const float& divisor) +{ + return Vector4( + float(self.x() / divisor), + float(self.y() / divisor), + float(self.z() / divisor), + float(self.w() / divisor) + ); +} +inline Vector4 operator/(const Vector4& self, const float& divisor) +{ + return vector4_divided(self, divisor); +} +inline void vector4_divide(Vector4& self, const float& divisor) +{ + self.x() /= static_cast(divisor); + self.y() /= static_cast(divisor); + self.z() /= static_cast(divisor); + self.w() /= static_cast(divisor); +} +inline void operator/=(Vector4& self, const float& divisor) +{ + vector4_divide(self, divisor); +} + +#endif diff --git a/tools/urt/libs/mathlib.h b/tools/urt/libs/mathlib.h new file mode 100644 index 00000000..682088f2 --- /dev/null +++ b/tools/urt/libs/mathlib.h @@ -0,0 +1,425 @@ +/* +This code is based on source provided under the terms of the Id Software +LIMITED USE SOFTWARE LICENSE AGREEMENT, a copy of which is included with the +GtkRadiant sources (see LICENSE_ID). If you did not receive a copy of +LICENSE_ID, please contact Id Software immediately at info@idsoftware.com. + +All changes and additions to the original source which have been developed by +other contributors (see CONTRIBUTORS) are provided under the terms of the +license the contributors choose (see LICENSE), to the extent permitted by the +LICENSE_ID. If you did not receive a copy of the contributor license, +please contact the GtkRadiant maintainers at info@gtkradiant.com immediately. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef __MATHLIB__ +#define __MATHLIB__ + +// mathlib.h +#include + +#ifdef __cplusplus + +// start declarations of functions defined in C library. +extern "C" +{ + +#endif + +#include "bytebool.h" + +typedef float vec_t; +typedef vec_t vec3_t[3]; +typedef vec_t vec5_t[5]; +typedef vec_t vec4_t[4]; + +#define SIDE_FRONT 0 +#define SIDE_ON 2 +#define SIDE_BACK 1 +#define SIDE_CROSS -2 + +// plane types are used to speed some tests +// 0-2 are axial planes +#define PLANE_X 0 +#define PLANE_Y 1 +#define PLANE_Z 2 +#define PLANE_NON_AXIAL 3 + +#define Q_PI 3.14159265358979323846f + +extern const vec3_t vec3_origin; + +extern const vec3_t g_vec3_axis_x; +extern const vec3_t g_vec3_axis_y; +extern const vec3_t g_vec3_axis_z; + +#define EQUAL_EPSILON 0.001 + +#define DotProduct(x,y) ((x)[0]*(y)[0]+(x)[1]*(y)[1]+(x)[2]*(y)[2]) +#define VectorSubtract(a,b,c) ((c)[0]=(a)[0]-(b)[0],(c)[1]=(a)[1]-(b)[1],(c)[2]=(a)[2]-(b)[2]) +#define VectorAdd(a,b,c) ((c)[0]=(a)[0]+(b)[0],(c)[1]=(a)[1]+(b)[1],(c)[2]=(a)[2]+(b)[2]) +#define VectorIncrement(a,b) ((b)[0]+=(a)[0],(b)[1]+=(a)[1],(b)[2]+=(a)[2]) +#define VectorCopy(a,b) ((b)[0]=(a)[0],(b)[1]=(a)[1],(b)[2]=(a)[2]) +#define VectorSet(v, a, b, c) ((v)[0]=(a),(v)[1]=(b),(v)[2]=(c)) +#define VectorScale(a,b,c) ((c)[0]=(b)*(a)[0],(c)[1]=(b)*(a)[1],(c)[2]=(b)*(a)[2]) +#define VectorMid(a,b,c) ((c)[0]=((a)[0]+(b)[0])*0.5f,(c)[1]=((a)[1]+(b)[1])*0.5f,(c)[2]=((a)[2]+(b)[2])*0.5f) +#define VectorNegate(a,b) ((b)[0]=-(a)[0],(b)[1]=-(a)[1],(b)[2]=-(a)[2]) +#define CrossProduct(a,b,c) ((c)[0]=(a)[1]*(b)[2]-(a)[2]*(b)[1],(c)[1]=(a)[2]*(b)[0]-(a)[0]*(b)[2],(c)[2]=(a)[0]*(b)[1]-(a)[1]*(b)[0]) +#define VectorClear(x) ((x)[0]=(x)[1]=(x)[2]=0) + +#define FLOAT_SNAP(f,snap) ( (float)( floor( (f) / (snap) + 0.5 ) * (snap) ) ) +#define FLOAT_TO_INTEGER(f) ( (float)( floor( (f) + 0.5 ) ) ) + +#define Q_rint(in) ((vec_t)floor(in+0.5)) + +qboolean VectorCompare (const vec3_t v1, const vec3_t v2); + +vec_t VectorLength(const vec3_t v); + +void VectorMA( const vec3_t va, vec_t scale, const vec3_t vb, vec3_t vc ); + +void _CrossProduct (vec3_t v1, vec3_t v2, vec3_t cross); +vec_t VectorNormalize (const vec3_t in, vec3_t out); +vec_t ColorNormalize( const vec3_t in, vec3_t out ); +void VectorInverse (vec3_t v); +void VectorPolar(vec3_t v, float radius, float theta, float phi); + +// default snapping, to 1 +void VectorSnap(vec3_t v); + +// integer snapping +void VectorISnap(vec3_t point, int snap); + +// Gef: added snap to float for sub-integer grid sizes +// TTimo: we still use the int version of VectorSnap when possible +// to avoid potential rounding issues +// TTimo: renaming to VectorFSnap for C implementation +void VectorFSnap(vec3_t point, float snap); + +// NOTE: added these from Ritual's Q3Radiant +void ClearBounds (vec3_t mins, vec3_t maxs); +void AddPointToBounds (vec3_t v, vec3_t mins, vec3_t maxs); + + +#define PITCH 0 // up / down +#define YAW 1 // left / right +#define ROLL 2 // fall over + +void AngleVectors (vec3_t angles, vec3_t forward, vec3_t right, vec3_t up); +void VectorToAngles( vec3_t vec, vec3_t angles ); + +#define ZERO_EPSILON 1.0E-6 +#define RAD2DEGMULT 57.29577951308232f +#define DEG2RADMULT 0.01745329251994329f +#define RAD2DEG( a ) ( (a) * RAD2DEGMULT ) +#define DEG2RAD( a ) ( (a) * DEG2RADMULT ) + +void VectorRotate (vec3_t vIn, vec3_t vRotation, vec3_t out); +void VectorRotateOrigin (vec3_t vIn, vec3_t vRotation, vec3_t vOrigin, vec3_t out); + +// some function merged from tools mathlib code + +qboolean PlaneFromPoints( vec4_t plane, const vec3_t a, const vec3_t b, const vec3_t c ); +void NormalToLatLong( const vec3_t normal, byte bytes[2] ); +int PlaneTypeForNormal (vec3_t normal); +void RotatePointAroundVector( vec3_t dst, const vec3_t dir, const vec3_t point, float degrees ); + + +/*! +\todo +FIXME test calls such as intersect tests should be named test_ +*/ + +typedef vec_t m3x3_t[9]; +/*!NOTE +m4x4 looks like this.. + + x y z +x axis ( 0 1 2) +y axis ( 4 5 6) +z axis ( 8 9 10) +translation (12 13 14) +scale ( 0 5 10) +*/ +typedef vec_t m4x4_t[16]; + +#define M4X4_INDEX(m,row,col) (m[(col<<2)+row]) + +typedef enum { eXYZ, eYZX, eZXY, eXZY, eYXZ, eZYX } eulerOrder_t; + +#define CLIP_PASS 0x00 // 000000 +#define CLIP_LT_X 0x01 // 000001 +#define CLIP_GT_X 0x02 // 000010 +#define CLIP_LT_Y 0x04 // 000100 +#define CLIP_GT_Y 0x08 // 001000 +#define CLIP_LT_Z 0x10 // 010000 +#define CLIP_GT_Z 0x20 // 100000 +#define CLIP_FAIL 0x3F // 111111 +typedef unsigned char clipmask_t; + +extern const m4x4_t g_m4x4_identity; + +#define M4X4_COPY(dst,src) (\ +(dst)[0]=(src)[0],\ +(dst)[1]=(src)[1],\ +(dst)[2]=(src)[2],\ +(dst)[3]=(src)[3],\ +(dst)[4]=(src)[4],\ +(dst)[5]=(src)[5],\ +(dst)[6]=(src)[6],\ +(dst)[7]=(src)[7],\ +(dst)[8]=(src)[8],\ +(dst)[9]=(src)[9],\ +(dst)[10]=(src)[10],\ +(dst)[11]=(src)[11],\ +(dst)[12]=(src)[12],\ +(dst)[13]=(src)[13],\ +(dst)[14]=(src)[14],\ +(dst)[15]=(src)[15]) + +typedef enum +{ + eRightHanded = 0, + eLeftHanded = 1, +} +m4x4Handedness_t; + +m4x4Handedness_t m4x4_handedness(const m4x4_t matrix); + +/*! assign other m4x4 to this m4x4 */ +void m4x4_assign(m4x4_t matrix, const m4x4_t other); + +// constructors +/*! create m4x4 as identity matrix */ +void m4x4_identity(m4x4_t matrix); +/*! create m4x4 as a translation matrix, for a translation vec3 */ +void m4x4_translation_for_vec3(m4x4_t matrix, const vec3_t translation); +/*! create m4x4 as a rotation matrix, for an euler angles (degrees) vec3 */ +void m4x4_rotation_for_vec3(m4x4_t matrix, const vec3_t euler, eulerOrder_t order); +/*! create m4x4 as a scaling matrix, for a scale vec3 */ +void m4x4_scale_for_vec3(m4x4_t matrix, const vec3_t scale); +/*! create m4x4 as a rotation matrix, for a quaternion vec4 */ +void m4x4_rotation_for_quat(m4x4_t matrix, const vec4_t rotation); +/*! create m4x4 as a rotation matrix, for an axis vec3 and an angle (radians) */ +void m4x4_rotation_for_axisangle(m4x4_t matrix, const vec3_t axis, double angle); +/*! generate a perspective matrix by specifying the view frustum */ +void m4x4_frustum(m4x4_t matrix, vec_t left, vec_t right, vec_t bottom, vec_t top, vec_t nearval, vec_t farval); + +// a valid m4x4 to access is always first argument +/*! extract translation vec3 from matrix */ +void m4x4_get_translation_vec3(const m4x4_t matrix, vec3_t translation); +/*! extract euler rotation angles from a rotation-only matrix */ +void m4x4_get_rotation_vec3(const m4x4_t matrix, vec3_t euler, eulerOrder_t order); +/*! extract scale vec3 from matrix */ +void m4x4_get_scale_vec3(const m4x4_t matrix, vec3_t scale); +/*! extract translation/euler/scale from an orthogonal matrix. NOTE: requires right-handed axis-base */ +void m4x4_get_transform_vec3(const m4x4_t matrix, vec3_t translation, vec3_t euler, eulerOrder_t order, vec3_t scale); + +// a valid m4x4 to be modified is always first argument +/*! translate m4x4 by a translation vec3 */ +void m4x4_translate_by_vec3(m4x4_t matrix, const vec3_t translation); +/*! rotate m4x4 by a euler (degrees) vec3 */ +void m4x4_rotate_by_vec3(m4x4_t matrix, const vec3_t euler, eulerOrder_t order); +/*! scale m4x4 by a scaling vec3 */ +void m4x4_scale_by_vec3(m4x4_t matrix, const vec3_t scale); +/*! rotate m4x4 by a quaternion vec4 */ +void m4x4_rotate_by_quat(m4x4_t matrix, const vec4_t rotation); +/*! rotate m4x4 by an axis vec3 and an angle (radians) */ +void m4x4_rotate_by_axisangle(m4x4_t matrix, const vec3_t axis, double angle); +/*! transform m4x4 by translation/eulerZYX/scaling vec3 (transform = scale * eulerZ * eulerY * eulerX * translation) */ +void m4x4_transform_by_vec3(m4x4_t matrix, const vec3_t translation, const vec3_t euler, eulerOrder_t order, const vec3_t scale); +/*! rotate m4x4 around a pivot point by eulerZYX vec3 */ +void m4x4_pivoted_rotate_by_vec3(m4x4_t matrix, const vec3_t euler, eulerOrder_t order, const vec3_t pivotpoint); +/*! scale m4x4 around a pivot point by scaling vec3 */ +void m4x4_pivoted_scale_by_vec3(m4x4_t matrix, const vec3_t scale, const vec3_t pivotpoint); +/*! transform m4x4 around a pivot point by translation/eulerZYX/scaling vec3 */ +void m4x4_pivoted_transform_by_vec3(m4x4_t matrix, const vec3_t translation, const vec3_t euler, eulerOrder_t order, const vec3_t scale, const vec3_t pivotpoint); +/*! transform m4x4 around a pivot point by translation/rotation/scaling vec3 */ +void m4x4_pivoted_transform_by_rotation(m4x4_t matrix, const vec3_t translation, const m4x4_t rotation, const vec3_t scale, const vec3_t pivotpoint); +/*! rotate m4x4 around a pivot point by quaternion vec4 */ +void m4x4_pivoted_rotate_by_quat(m4x4_t matrix, const vec4_t quat, const vec3_t pivotpoint); +/*! rotate m4x4 around a pivot point by axis vec3 and angle (radians) */ +void m4x4_pivoted_rotate_by_axisangle(m4x4_t matrix, const vec3_t axis, double angle, const vec3_t pivotpoint); + +/*! postmultiply m4x4 by another m4x4 */ +void m4x4_multiply_by_m4x4(m4x4_t matrix, const m4x4_t matrix_src); +/*! premultiply m4x4 by another m4x4 */ +void m4x4_premultiply_by_m4x4(m4x4_t matrix, const m4x4_t matrix_src); +/*! postmultiply orthogonal m4x4 by another orthogonal m4x4 */ +void m4x4_orthogonal_multiply_by_m4x4(m4x4_t matrix, const m4x4_t matrix_src); +/*! premultiply orthogonal m4x4 by another orthogonal m4x4 */ +void m4x4_orthogonal_premultiply_by_m4x4(m4x4_t matrix, const m4x4_t matrix_src); + +/*! multiply a point (x,y,z,1) by matrix */ +void m4x4_transform_point(const m4x4_t matrix, vec3_t point); +/*! multiply a normal (x,y,z,0) by matrix */ +void m4x4_transform_normal(const m4x4_t matrix, vec3_t normal); +/*! multiply a vec4 (x,y,z,w) by matrix */ +void m4x4_transform_vec4(const m4x4_t matrix, vec4_t vector); + +/*! multiply a point (x,y,z,1) by matrix */ +void m4x4_transform_point(const m4x4_t matrix, vec3_t point); +/*! multiply a normal (x,y,z,0) by matrix */ +void m4x4_transform_normal(const m4x4_t matrix, vec3_t normal); + +/*! transpose a m4x4 */ +void m4x4_transpose(m4x4_t matrix); +/*! invert an orthogonal 4x3 subset of a 4x4 matrix */ +int m4x4_orthogonal_invert(m4x4_t matrix); +/*! invert any m4x4 using Kramer's rule.. return 1 if matrix is singular, else return 0 */ +int m4x4_invert(m4x4_t matrix); + +/*! clip a point (x,y,z,1) by canonical matrix */ +clipmask_t m4x4_clip_point(const m4x4_t matrix, const vec3_t point, vec4_t clipped); +/*! device-space polygon for clipped triangle */ +unsigned int m4x4_clip_triangle(const m4x4_t matrix, const vec3_t p0, const vec3_t p1, const vec3_t p2, vec4_t clipped[9]); +/*! device-space line for clipped line */ +unsigned int m4x4_clip_line(const m4x4_t matrix, const vec3_t p0, const vec3_t p1, vec4_t clipped[2]); + + +//! quaternion identity +void quat_identity(vec4_t quat); +//! quaternion from two unit vectors +void quat_for_unit_vectors(vec4_t quat, const vec3_t from, const vec3_t to); +//! quaternion from axis and angle (radians) +void quat_for_axisangle(vec4_t quat, const vec3_t axis, double angle); +//! concatenates two rotations.. equivalent to m4x4_multiply_by_m4x4 .. postmultiply.. the right-hand side is the first rotation performed +void quat_multiply_by_quat(vec4_t quat, const vec4_t other); +//! negate a quaternion +void quat_conjugate(vec4_t quat); +//! normalise a quaternion +void quat_normalise(vec4_t quat); + + + +/*! +\todo object/ray intersection functions should maybe return a point rather than a distance? +*/ + +/*! +aabb_t - "axis-aligned" bounding box... + origin: centre of bounding box... + extents: +/- extents of box from origin... +*/ +typedef struct aabb_s +{ + vec3_t origin; + vec3_t extents; +} aabb_t; + +extern const aabb_t g_aabb_null; + +/*! +bbox_t - oriented bounding box... + aabb: axis-aligned bounding box... + axes: orientation axes... +*/ +typedef struct bbox_s +{ + aabb_t aabb; + vec3_t axes[3]; + vec_t radius; +} bbox_t; + +/*! +ray_t - origin point and direction unit-vector +*/ +typedef struct ray_s +{ + vec3_t origin; + vec3_t direction; +} ray_t; + +/*! +line_t - centre point and displacement of end point from centre +*/ +typedef struct line_s +{ + vec3_t origin; + vec3_t extents; +} line_t; + + +/*! Generate line from start/end points. */ +void line_construct_for_vec3(line_t* line, const vec3_t start, const vec3_t end); +/*! Return 2 if line is behind plane, else return 1 if line intersects plane, else return 0. */ +int line_test_plane(const line_t* line, const vec4_t plane); + +/*! Generate AABB from min/max. */ +void aabb_construct_for_vec3(aabb_t* aabb, const vec3_t min, const vec3_t max); +/*! Initialise AABB to negative size. */ +void aabb_clear(aabb_t* aabb); + +/*! Extend AABB to include point. */ +void aabb_extend_by_point(aabb_t* aabb, const vec3_t point); +/*! Extend AABB to include aabb_src. */ +void aabb_extend_by_aabb(aabb_t* aabb, const aabb_t* aabb_src); +/*! Extend AABB by +/- extension vector. */ +void aabb_extend_by_vec3(aabb_t* aabb, vec3_t extension); + +/*! Return 2 if point is inside, else 1 if point is on surface, else 0. */ +int aabb_test_point(const aabb_t* aabb, const vec3_t point); +/*! Return 2 if aabb_src intersects, else 1 if aabb_src touches exactly, else 0. */ +int aabb_test_aabb(const aabb_t* aabb, const aabb_t* aabb_src); +/*! Return 2 if aabb is behind plane, else 1 if aabb intersects plane, else 0. */ +int aabb_test_plane(const aabb_t* aabb, const float* plane); +/*! Return 1 if aabb intersects ray, else 0... dist = closest intersection. */ +int aabb_intersect_ray(const aabb_t* aabb, const ray_t* ray, vec3_t intersection); +/*! Return 1 if aabb intersects ray, else 0. Faster, but does not provide point of intersection */ +int aabb_test_ray(const aabb_t* aabb, const ray_t* ray); + +/*! Return 2 if oriented aabb is behind plane, else 1 if aabb intersects plane, else 0. */ +int aabb_oriented_intersect_plane(const aabb_t* aabb, const m4x4_t transform, const vec_t* plane); + +/*! Calculate the corners of the aabb. */ +void aabb_corners(const aabb_t* aabb, vec3_t corners[8]); + +/*! (deprecated) Generate AABB from oriented bounding box. */ +void aabb_for_bbox(aabb_t* aabb, const bbox_t* bbox); +/*! (deprecated) Generate AABB from 2-dimensions of min/max, specified by axis. */ +void aabb_for_area(aabb_t* aabb, vec3_t area_tl, vec3_t area_br, int axis); +/*! Generate AABB to contain src* transform. NOTE: transform must be orthogonal */ +void aabb_for_transformed_aabb(aabb_t* dst, const aabb_t* src, const m4x4_t transform); + +/*! Update bounding-sphere radius. */ +void bbox_update_radius(bbox_t* bbox); +/*! Generate oriented bounding box from AABB and transformation matrix. */ +/*!\todo Remove need to specify eulerZYX/scale. */ +void bbox_for_oriented_aabb(bbox_t* bbox, const aabb_t* aabb, + const m4x4_t matrix, const vec3_t eulerZYX, const vec3_t scale); +/*! Return 2 if bbox is behind plane, else return 1 if bbox intersects plane, else return 0. */ +int bbox_intersect_plane(const bbox_t* bbox, const vec_t* plane); + + +/*! Generate a ray from an origin point and a direction unit-vector */ +void ray_construct_for_vec3(ray_t* ray, const vec3_t origin, const vec3_t direction); + +/*! Transform a ray */ +void ray_transform(ray_t* ray, const m4x4_t matrix); + +/*! distance from ray origin in ray direction to point. FLT_MAX if no intersection. */ +vec_t ray_intersect_point(const ray_t* ray, const vec3_t point, vec_t epsilon, vec_t divergence); +/*! distance from ray origin in ray direction to triangle. FLT_MAX if no intersection. */ +vec_t ray_intersect_triangle(const ray_t* ray, qboolean bCullBack, const vec3_t vert0, const vec3_t vert1, const vec3_t vert2); +/*! distance from ray origin in ray direction to plane. */ +vec_t ray_intersect_plane(const ray_t* ray, const vec3_t normal, vec_t dist); + + +int plane_intersect_planes(const vec4_t plane1, const vec4_t plane2, const vec4_t plane3, vec3_t intersection); + + +#ifdef __cplusplus +} +#endif + +#endif /* __MATHLIB__ */ diff --git a/tools/urt/libs/mathlib/bbox.c b/tools/urt/libs/mathlib/bbox.c new file mode 100644 index 00000000..4b5a9fbc --- /dev/null +++ b/tools/urt/libs/mathlib/bbox.c @@ -0,0 +1,465 @@ +/* +This code provided under the terms of the Id Software +LIMITED USE SOFTWARE LICENSE AGREEMENT, a copy of which is included with the +GtkRadiant sources (see LICENSE_ID). If you did not receive a copy of +LICENSE_ID, please contact Id Software immediately at info@idsoftware.com. + +All changes and additions to the original source which have been developed by +other contributors (see CONTRIBUTORS) are provided under the terms of the +license the contributors choose (see LICENSE), to the extent permitted by the +LICENSE_ID. If you did not receive a copy of the contributor license, +please contact the GtkRadiant maintainers at info@gtkradiant.com immediately. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include + +#include "mathlib.h" + +const aabb_t g_aabb_null = { + { 0, 0, 0, }, + { -FLT_MAX, -FLT_MAX, -FLT_MAX, }, +}; + +void aabb_construct_for_vec3(aabb_t *aabb, const vec3_t min, const vec3_t max) +{ + VectorMid(min, max, aabb->origin); + VectorSubtract(max, aabb->origin, aabb->extents); +} + +void aabb_clear(aabb_t *aabb) +{ + VectorCopy(g_aabb_null.origin, aabb->origin); + VectorCopy(g_aabb_null.extents, aabb->extents); +} + +void aabb_extend_by_point(aabb_t *aabb, const vec3_t point) +{ +#if 1 + int i; + vec_t min, max, displacement; + for(i=0; i<3; i++) + { + displacement = point[i] - aabb->origin[i]; + if(fabs(displacement) > aabb->extents[i]) + { + if(aabb->extents[i] < 0) // degenerate + { + min = max = point[i]; + } + else if(displacement > 0) + { + min = aabb->origin[i] - aabb->extents[i]; + max = aabb->origin[i] + displacement; + } + else + { + max = aabb->origin[i] + aabb->extents[i]; + min = aabb->origin[i] + displacement; + } + aabb->origin[i] = (min + max) * 0.5f; + aabb->extents[i] = max - aabb->origin[i]; + } + } +#else + unsigned int i; + for(i=0; i<3; ++i) + { + if(aabb->extents[i] < 0) // degenerate + { + aabb->origin[i] = point[i]; + aabb->extents[i] = 0; + } + else + { + vec_t displacement = point[i] - aabb->origin[i]; + vec_t increment = (vec_t)fabs(displacement) - aabb->extents[i]; + if(increment > 0) + { + increment *= (vec_t)((displacement > 0) ? 0.5 : -0.5); + aabb->extents[i] += increment; + aabb->origin[i] += increment; + } + } + } +#endif +} + +void aabb_extend_by_aabb(aabb_t *aabb, const aabb_t *aabb_src) +{ + int i; + vec_t min, max, displacement, difference; + for(i=0; i<3; i++) + { + displacement = aabb_src->origin[i] - aabb->origin[i]; + difference = aabb_src->extents[i] - aabb->extents[i]; + if(aabb->extents[i] < 0 + || difference >= fabs(displacement)) + { + // 2nd contains 1st + aabb->extents[i] = aabb_src->extents[i]; + aabb->origin[i] = aabb_src->origin[i]; + } + else if(aabb_src->extents[i] < 0 + || -difference >= fabs(displacement)) + { + // 1st contains 2nd + continue; + } + else + { + // not contained + if(displacement > 0) + { + min = aabb->origin[i] - aabb->extents[i]; + max = aabb_src->origin[i] + aabb_src->extents[i]; + } + else + { + min = aabb_src->origin[i] - aabb_src->extents[i]; + max = aabb->origin[i] + aabb->extents[i]; + } + aabb->origin[i] = (min + max) * 0.5f; + aabb->extents[i] = max - aabb->origin[i]; + } + } +} + +void aabb_extend_by_vec3(aabb_t *aabb, vec3_t extension) +{ + VectorAdd(aabb->extents, extension, aabb->extents); +} + +int aabb_test_point(const aabb_t *aabb, const vec3_t point) +{ + int i; + for(i=0; i<3; i++) + if(fabs(point[i] - aabb->origin[i]) >= aabb->extents[i]) + return 0; + return 1; +} + +int aabb_test_aabb(const aabb_t *aabb, const aabb_t *aabb_src) +{ + int i; + for (i=0; i<3; i++) + if ( fabs(aabb_src->origin[i] - aabb->origin[i]) > (fabs(aabb->extents[i]) + fabs(aabb_src->extents[i])) ) + return 0; + return 1; +} + +int aabb_test_plane(const aabb_t *aabb, const float *plane) +{ + float fDist, fIntersect; + + // calc distance of origin from plane + fDist = DotProduct(plane, aabb->origin) + plane[3]; + + // calc extents distance relative to plane normal + fIntersect = (vec_t)(fabs(plane[0] * aabb->extents[0]) + fabs(plane[1] * aabb->extents[1]) + fabs(plane[2] * aabb->extents[2])); + // accept if origin is less than or equal to this distance + if (fabs(fDist) < fIntersect) return 1; // partially inside + else if (fDist < 0) return 2; // totally inside + return 0; // totally outside +} + +/* +Fast Ray-Box Intersection +by Andrew Woo +from "Graphics Gems", Academic Press, 1990 +*/ + +#define NUMDIM 3 +#define RIGHT 0 +#define LEFT 1 +#define MIDDLE 2 + +int aabb_intersect_ray(const aabb_t *aabb, const ray_t *ray, vec3_t intersection) +{ + int inside = 1; + char quadrant[NUMDIM]; + register int i; + int whichPlane; + double maxT[NUMDIM]; + double candidatePlane[NUMDIM]; + + const float *origin = ray->origin; + const float *direction = ray->direction; + + /* Find candidate planes; this loop can be avoided if + rays cast all from the eye(assume perpsective view) */ + for (i=0; iorigin[i] - aabb->extents[i])) + { + quadrant[i] = LEFT; + candidatePlane[i] = (aabb->origin[i] - aabb->extents[i]); + inside = 0; + } + else if (origin[i] > (aabb->origin[i] + aabb->extents[i])) + { + quadrant[i] = RIGHT; + candidatePlane[i] = (aabb->origin[i] + aabb->extents[i]); + inside = 0; + } + else + { + quadrant[i] = MIDDLE; + } + } + + /* Ray origin inside bounding box */ + if(inside == 1) + { + VectorCopy(ray->origin, intersection); + return 1; + } + + + /* Calculate T distances to candidate planes */ + for (i = 0; i < NUMDIM; i++) + { + if (quadrant[i] != MIDDLE && direction[i] !=0.) + maxT[i] = (candidatePlane[i] - origin[i]) / direction[i]; + else + maxT[i] = -1.; + } + + /* Get largest of the maxT's for final choice of intersection */ + whichPlane = 0; + for (i = 1; i < NUMDIM; i++) + if (maxT[whichPlane] < maxT[i]) + whichPlane = i; + + /* Check final candidate actually inside box */ + if (maxT[whichPlane] < 0.) + return 0; + for (i = 0; i < NUMDIM; i++) + { + if (whichPlane != i) + { + intersection[i] = (vec_t)(origin[i] + maxT[whichPlane] * direction[i]); + if (fabs(intersection[i] - aabb->origin[i]) > aabb->extents[i]) + return 0; + } + else + { + intersection[i] = (vec_t)candidatePlane[i]; + } + } + + return 1; /* ray hits box */ +} + +int aabb_test_ray(const aabb_t* aabb, const ray_t* ray) +{ + vec3_t displacement, ray_absolute; + vec_t f; + + displacement[0] = ray->origin[0] - aabb->origin[0]; + if(fabs(displacement[0]) > aabb->extents[0] && displacement[0] * ray->direction[0] >= 0.0f) + return 0; + + displacement[1] = ray->origin[1] - aabb->origin[1]; + if(fabs(displacement[1]) > aabb->extents[1] && displacement[1] * ray->direction[1] >= 0.0f) + return 0; + + displacement[2] = ray->origin[2] - aabb->origin[2]; + if(fabs(displacement[2]) > aabb->extents[2] && displacement[2] * ray->direction[2] >= 0.0f) + return 0; + + ray_absolute[0] = (float)fabs(ray->direction[0]); + ray_absolute[1] = (float)fabs(ray->direction[1]); + ray_absolute[2] = (float)fabs(ray->direction[2]); + + f = ray->direction[1] * displacement[2] - ray->direction[2] * displacement[1]; + if((float)fabs(f) > aabb->extents[1] * ray_absolute[2] + aabb->extents[2] * ray_absolute[1]) + return 0; + + f = ray->direction[2] * displacement[0] - ray->direction[0] * displacement[2]; + if((float)fabs(f) > aabb->extents[0] * ray_absolute[2] + aabb->extents[2] * ray_absolute[0]) + return 0; + + f = ray->direction[0] * displacement[1] - ray->direction[1] * displacement[0]; + if((float)fabs(f) > aabb->extents[0] * ray_absolute[1] + aabb->extents[1] * ray_absolute[0]) + return 0; + + return 1; +} + +void aabb_orthogonal_transform(aabb_t* dst, const aabb_t* src, const m4x4_t transform) +{ + VectorCopy(src->origin, dst->origin); + m4x4_transform_point(transform, dst->origin); + + dst->extents[0] = (vec_t)(fabs(transform[0] * src->extents[0]) + + fabs(transform[4] * src->extents[1]) + + fabs(transform[8] * src->extents[2])); + dst->extents[1] = (vec_t)(fabs(transform[1] * src->extents[0]) + + fabs(transform[5] * src->extents[1]) + + fabs(transform[9] * src->extents[2])); + dst->extents[2] = (vec_t)(fabs(transform[2] * src->extents[0]) + + fabs(transform[6] * src->extents[1]) + + fabs(transform[10] * src->extents[2])); +} + +void aabb_for_bbox(aabb_t *aabb, const bbox_t *bbox) +{ + int i; + vec3_t temp[3]; + + VectorCopy(bbox->aabb.origin, aabb->origin); + + // calculate the AABB extents in local coord space from the OBB extents and axes + VectorScale(bbox->axes[0], bbox->aabb.extents[0], temp[0]); + VectorScale(bbox->axes[1], bbox->aabb.extents[1], temp[1]); + VectorScale(bbox->axes[2], bbox->aabb.extents[2], temp[2]); + for(i=0;i<3;i++) aabb->extents[i] = (vec_t)(fabs(temp[0][i]) + fabs(temp[1][i]) + fabs(temp[2][i])); +} + +void aabb_for_area(aabb_t *aabb, vec3_t area_tl, vec3_t area_br, int axis) +{ + aabb_clear(aabb); + aabb->extents[axis] = FLT_MAX; + aabb_extend_by_point(aabb, area_tl); + aabb_extend_by_point(aabb, area_br); +} + +int aabb_oriented_intersect_plane(const aabb_t *aabb, const m4x4_t transform, const vec_t* plane) +{ + vec_t fDist, fIntersect; + + // calc distance of origin from plane + fDist = DotProduct(plane, aabb->origin) + plane[3]; + + // calc extents distance relative to plane normal + fIntersect = (vec_t)(fabs(aabb->extents[0] * DotProduct(plane, transform)) + + fabs(aabb->extents[1] * DotProduct(plane, transform+4)) + + fabs(aabb->extents[2] * DotProduct(plane, transform+8))); + // accept if origin is less than this distance + if (fabs(fDist) < fIntersect) return 1; // partially inside + else if (fDist < 0) return 2; // totally inside + return 0; // totally outside +} + +void aabb_corners(const aabb_t* aabb, vec3_t corners[8]) +{ + vec3_t min, max; + VectorSubtract(aabb->origin, aabb->extents, min); + VectorAdd(aabb->origin, aabb->extents, max); + VectorSet(corners[0], min[0], max[1], max[2]); + VectorSet(corners[1], max[0], max[1], max[2]); + VectorSet(corners[2], max[0], min[1], max[2]); + VectorSet(corners[3], min[0], min[1], max[2]); + VectorSet(corners[4], min[0], max[1], min[2]); + VectorSet(corners[5], max[0], max[1], min[2]); + VectorSet(corners[6], max[0], min[1], min[2]); + VectorSet(corners[7], min[0], min[1], min[2]); +} + + +void bbox_update_radius(bbox_t *bbox) +{ + bbox->radius = VectorLength(bbox->aabb.extents); +} + +void aabb_for_transformed_aabb(aabb_t* dst, const aabb_t* src, const m4x4_t transform) +{ + if(src->extents[0] < 0 + || src->extents[1] < 0 + || src->extents[2] < 0) + { + aabb_clear(dst); + return; + } + + VectorCopy(src->origin, dst->origin); + m4x4_transform_point(transform, dst->origin); + + dst->extents[0] = (vec_t)(fabs(transform[0] * src->extents[0]) + + fabs(transform[4] * src->extents[1]) + + fabs(transform[8] * src->extents[2])); + dst->extents[1] = (vec_t)(fabs(transform[1] * src->extents[0]) + + fabs(transform[5] * src->extents[1]) + + fabs(transform[9] * src->extents[2])); + dst->extents[2] = (vec_t)(fabs(transform[2] * src->extents[0]) + + fabs(transform[6] * src->extents[1]) + + fabs(transform[10] * src->extents[2])); +} + +void bbox_for_oriented_aabb(bbox_t *bbox, const aabb_t *aabb, const m4x4_t matrix, const vec3_t euler, const vec3_t scale) +{ + double rad[3]; + double pi_180 = Q_PI / 180; + double A, B, C, D, E, F, AD, BD; + + VectorCopy(aabb->origin, bbox->aabb.origin); + + m4x4_transform_point(matrix, bbox->aabb.origin); + + bbox->aabb.extents[0] = aabb->extents[0] * scale[0]; + bbox->aabb.extents[1] = aabb->extents[1] * scale[1]; + bbox->aabb.extents[2] = aabb->extents[2] * scale[2]; + + rad[0] = euler[0] * pi_180; + rad[1] = euler[1] * pi_180; + rad[2] = euler[2] * pi_180; + + A = cos(rad[0]); + B = sin(rad[0]); + C = cos(rad[1]); + D = sin(rad[1]); + E = cos(rad[2]); + F = sin(rad[2]); + + AD = A * -D; + BD = B * -D; + + bbox->axes[0][0] = (vec_t)(C*E); + bbox->axes[0][1] = (vec_t)(-BD*E + A*F); + bbox->axes[0][2] = (vec_t)(AD*E + B*F); + bbox->axes[1][0] = (vec_t)(-C*F); + bbox->axes[1][1] = (vec_t)(BD*F + A*E); + bbox->axes[1][2] = (vec_t)(-AD*F + B*E); + bbox->axes[2][0] = (vec_t)D; + bbox->axes[2][1] = (vec_t)(-B*C); + bbox->axes[2][2] = (vec_t)(A*C); + + bbox_update_radius(bbox); +} + +int bbox_intersect_plane(const bbox_t *bbox, const vec_t* plane) +{ + vec_t fDist, fIntersect; + + // calc distance of origin from plane + fDist = DotProduct(plane, bbox->aabb.origin) + plane[3]; + + // trivial accept/reject using bounding sphere + if (fabs(fDist) > bbox->radius) + { + if (fDist < 0) + return 2; // totally inside + else + return 0; // totally outside + } + + // calc extents distance relative to plane normal + fIntersect = (vec_t)(fabs(bbox->aabb.extents[0] * DotProduct(plane, bbox->axes[0])) + + fabs(bbox->aabb.extents[1] * DotProduct(plane, bbox->axes[1])) + + fabs(bbox->aabb.extents[2] * DotProduct(plane, bbox->axes[2]))); + // accept if origin is less than this distance + if (fabs(fDist) < fIntersect) return 1; // partially inside + else if (fDist < 0) return 2; // totally inside + return 0; // totally outside +} diff --git a/tools/urt/libs/mathlib/line.c b/tools/urt/libs/mathlib/line.c new file mode 100644 index 00000000..8838b6da --- /dev/null +++ b/tools/urt/libs/mathlib/line.c @@ -0,0 +1,44 @@ +/* +This code provided under the terms of the Id Software +LIMITED USE SOFTWARE LICENSE AGREEMENT, a copy of which is included with the +GtkRadiant sources (see LICENSE_ID). If you did not receive a copy of +LICENSE_ID, please contact Id Software immediately at info@idsoftware.com. + +All changes and additions to the original source which have been developed by +other contributors (see CONTRIBUTORS) are provided under the terms of the +license the contributors choose (see LICENSE), to the extent permitted by the +LICENSE_ID. If you did not receive a copy of the contributor license, +please contact the GtkRadiant maintainers at info@gtkradiant.com immediately. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "mathlib.h" + +void line_construct_for_vec3(line_t *line, const vec3_t start, const vec3_t end) +{ + VectorMid(start, end, line->origin); + VectorSubtract(end, line->origin, line->extents); +} + +int line_test_plane(const line_t* line, const vec4_t plane) +{ + float fDist; + + // calc distance of origin from plane + fDist = DotProduct(plane, line->origin) + plane[3]; + + // accept if origin is less than or equal to this distance + if (fabs(fDist) < fabs(DotProduct(plane, line->extents))) return 1; // partially inside + else if (fDist < 0) return 2; // totally inside + return 0; // totally outside +} diff --git a/tools/urt/libs/mathlib/linear.c b/tools/urt/libs/mathlib/linear.c new file mode 100644 index 00000000..aa36b49c --- /dev/null +++ b/tools/urt/libs/mathlib/linear.c @@ -0,0 +1,207 @@ + +#include +#include +#include + +#include "mathlib.h" + +#define TINY FLT_MIN + +void lubksb(float **a, int n, int *indx, float b[]) +// Solves the set of n linear equations A.X=B. Here a[n][n] is input, not as the matrix +// A but rather as its LU decomposition determined by the routine ludcmp. indx[n] is input +// as the permutation vector returned by ludcmp. b[n] is input as the right-hand side vector +// B, and returns with the solution vector X. a, n and indx are not modified by this routine +// and can be left in place for successive calls with different right-hand sides b. This routine takes +// into account the possibility that b will begin with many zero elements, so it is efficient for use +// in matrix inversion +{ + int i,ii=-1,ip,j; + float sum; + + for (i=0;i=0) + for (j=ii;j=0;i--) { + sum=b[i]; + for (j=i+1;j big) big=temp; + if (big == 0.0) return 1; + vv[i]=1.0f/big; + } + for (j=0;j= big) { + big=dum; + imax=i; + } + } + if (j != imax) { + for (k=0;k big) big=temp; + if (big == 0.0) nrerror("Singular matrix in routine ludcmp"); + //No nonzero largest element. + vv[i]=1.0/big; //Save the scaling. + } + for (j=1;j<=n;j++) { //This is the loop over columns of Crout's method. + for (i=1;i= big) { + //Is the figure of merit for the pivot better than the best so far? + big=dum; + imax=i; + } + } + if (j != imax) { //Do we need to interchange rows? + for (k=1;k<=n;k++) { Yes, do so... + dum=a[imax][k]; + a[imax][k]=a[j][k]; + a[j][k]=dum; + } + *d = -(*d); //...and change the parity of d. + vv[imax]=vv[j]; //Also interchange the scale factor. + } + indx[j]=imax; + if (a[j][j] == 0.0) a[j][j]=TINY; + //If the pivot element is zero the matrix is singular (at least to the precision of the + // algorithm). For some applications on singular matrices, it is desirable to substitute + // TINY for zero. + if (j != n) { //Now, finally, divide by the pivot element. + dum=1.0/(a[j][j]); + for (i=j+1;i<=n;i++) a[i][j] *= dum; + } + } //Go back for the next column in the reduction. + free_vector(vv,1,n); +} + +void lubksb(float **a, int n, int *indx, float b[]) +//Solves the set of n linear equations A.X = B. Here a[1..n][1..n] is input, not as the matrix +//A but rather as its LU decomposition, determined by the routine ludcmp. indx[1..n] is input +//as the permutation vector returned by ludcmp. b[1..n] is input as the right-hand side vector +//B, and returns with the solution vector X. a, n, and indx are not modi ed by this routine +//and can be left in place for successive calls with di erent right-hand sides b. This routine takes +//into account the possibility that b will begin with many zero elements, so it is e.cient for use +//in matrix inversion. +{ + int i,ii=0,ip,j; + float sum; + for (i=1;i<=n;i++) { //When ii is set to a positive value, it will become the + //index of the first nonvanishing element of b. Wenow + //do the forward substitution, equation (2.3.6). The + //only new wrinkle is to unscramble the permutation + //as we go. + ip=indx[i]; + sum=b[ip]; + b[ip]=b[i]; + if (ii) + for (j=ii;j<=i-1;j++) sum -= a[i][j]*b[j]; + else if (sum) ii=i; //A nonzero element was encountered, so from now on we + //will have to do the sums in the loop above. b[i]=sum; + } + for (i=n;i>=1;i--) { //Now we do the backsubstitution, equation (2.3.7). + sum=b[i]; + for (j=i+1;j<=n;j++) sum -= a[i][j]*b[j]; + b[i]=sum/a[i][i]; //Store a component of the solution vector X. + } //All done! +} + +void bleh() +{ + #define N ... + float **a,**y,d,*col; + int i,j,*indx; + ... + ludcmp(a,N,indx,&d); //Decompose the matrix just once. + for(j=1;j<=N;j++) { //Find inverse by columns. + for(i=1;i<=N;i++) col[i]=0.0; + col[j]=1.0; + lubksb(a,N,indx,col); + for(i=1;i<=N;i++) y[i][j]=col[i]; + } +} +*/ diff --git a/tools/urt/libs/mathlib/m4x4.c b/tools/urt/libs/mathlib/m4x4.c new file mode 100644 index 00000000..5a67a917 --- /dev/null +++ b/tools/urt/libs/mathlib/m4x4.c @@ -0,0 +1,1880 @@ +/* +This code provided under the terms of the Id Software +LIMITED USE SOFTWARE LICENSE AGREEMENT, a copy of which is included with the +GtkRadiant sources (see LICENSE_ID). If you did not receive a copy of +LICENSE_ID, please contact Id Software immediately at info@idsoftware.com. + +All changes and additions to the original source which have been developed by +other contributors (see CONTRIBUTORS) are provided under the terms of the +license the contributors choose (see LICENSE), to the extent permitted by the +LICENSE_ID. If you did not receive a copy of the contributor license, +please contact the GtkRadiant maintainers at info@gtkradiant.com immediately. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "mathlib.h" + +const m4x4_t g_m4x4_identity = { + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1, +}; + +void m4x4_identity(m4x4_t matrix) +{ + matrix[1] = matrix[2] = matrix[3] = + matrix[4] = matrix[6] = matrix[7] = + matrix[8] = matrix[9] = matrix[11] = + matrix[12] = matrix[13] = matrix[14] = 0; + + matrix[0] = matrix[5] = matrix[10] = matrix[15] = 1; +} + +m4x4Handedness_t m4x4_handedness(const m4x4_t matrix) +{ + vec3_t cross; + CrossProduct(matrix+0, matrix+4, cross); + return (DotProduct(matrix+8, cross) < 0) ? eLeftHanded : eRightHanded; +} + +void m4x4_assign(m4x4_t matrix, const m4x4_t other) +{ + M4X4_COPY(matrix, other); +} + +void m4x4_translation_for_vec3(m4x4_t matrix, const vec3_t translation) +{ + matrix[1] = matrix[2] = matrix[3] = + matrix[4] = matrix[6] = matrix[7] = + matrix[8] = matrix[9] = matrix[11] = 0; + + matrix[0] = matrix[5] = matrix[10] = matrix[15] = 1; + + matrix[12] = translation[0]; + matrix[13] = translation[1]; + matrix[14] = translation[2]; +} + +/* +clockwise rotation around X, Y, Z, facing along axis + 1 0 0 cy 0 sy cz sz 0 + 0 cx sx 0 1 0 -sz cz 0 + 0 -sx cx -sy 0 cy 0 0 1 + +rows of Z by cols of Y + cy*cz -sy*cz+sz -sy*sz+cz +-sz*cy -sz*sy+cz + + .. or something like that.. + +final rotation is Z * Y * X + cy*cz -sx*-sy*cz+cx*sz cx*-sy*sz+sx*cz +-cy*sz sx*sy*sz+cx*cz -cx*-sy*sz+sx*cz + sy -sx*cy cx*cy +*/ + +/* transposed +| cy.cz + 0.sz + sy.0 cy.-sz + 0 .cz + sy.0 cy.0 + 0 .0 + sy.1 | +| sx.sy.cz + cx.sz + -sx.cy.0 sx.sy.-sz + cx.cz + -sx.cy.0 sx.sy.0 + cx.0 + -sx.cy.1 | +| -cx.sy.cz + sx.sz + cx.cy.0 -cx.sy.-sz + sx.cz + cx.cy.0 -cx.sy.0 + 0 .0 + cx.cy.1 | +*/ +void m4x4_rotation_for_vec3(m4x4_t matrix, const vec3_t euler, eulerOrder_t order) +{ + double cx, sx, cy, sy, cz, sz; + + cx = cos(DEG2RAD(euler[0])); + sx = sin(DEG2RAD(euler[0])); + cy = cos(DEG2RAD(euler[1])); + sy = sin(DEG2RAD(euler[1])); + cz = cos(DEG2RAD(euler[2])); + sz = sin(DEG2RAD(euler[2])); + + switch(order) + { + case eXYZ: + +#if 1 + + { + matrix[0] = (vec_t)(cy*cz); + matrix[1] = (vec_t)(cy*sz); + matrix[2] = (vec_t)-sy; + matrix[4] = (vec_t)(sx*sy*cz + cx*-sz); + matrix[5] = (vec_t)(sx*sy*sz + cx*cz); + matrix[6] = (vec_t)(sx*cy); + matrix[8] = (vec_t)(cx*sy*cz + sx*sz); + matrix[9] = (vec_t)(cx*sy*sz + -sx*cz); + matrix[10] = (vec_t)(cx*cy); + } + + matrix[12] = matrix[13] = matrix[14] = matrix[3] = matrix[7] = matrix[11] = 0; + matrix[15] = 1; + +#else + + m4x4_identity(matrix); + matrix[5] =(vec_t) cx; matrix[6] =(vec_t) sx; + matrix[9] =(vec_t)-sx; matrix[10]=(vec_t) cx; + + { + m4x4_t temp; + m4x4_identity(temp); + temp[0] =(vec_t) cy; temp[2] =(vec_t)-sy; + temp[8] =(vec_t) sy; temp[10]=(vec_t) cy; + m4x4_premultiply_by_m4x4(matrix, temp); + m4x4_identity(temp); + temp[0] =(vec_t) cz; temp[1] =(vec_t) sz; + temp[4] =(vec_t)-sz; temp[5] =(vec_t) cz; + m4x4_premultiply_by_m4x4(matrix, temp); + } +#endif + + break; + + case eYZX: + m4x4_identity(matrix); + matrix[0] =(vec_t) cy; matrix[2] =(vec_t)-sy; + matrix[8] =(vec_t) sy; matrix[10]=(vec_t) cy; + + { + m4x4_t temp; + m4x4_identity(temp); + temp[5] =(vec_t) cx; temp[6] =(vec_t) sx; + temp[9] =(vec_t)-sx; temp[10]=(vec_t) cx; + m4x4_premultiply_by_m4x4(matrix, temp); + m4x4_identity(temp); + temp[0] =(vec_t) cz; temp[1] =(vec_t) sz; + temp[4] =(vec_t)-sz; temp[5] =(vec_t) cz; + m4x4_premultiply_by_m4x4(matrix, temp); + } + break; + + case eZXY: + m4x4_identity(matrix); + matrix[0] =(vec_t) cz; matrix[1] =(vec_t) sz; + matrix[4] =(vec_t)-sz; matrix[5] =(vec_t) cz; + + { + m4x4_t temp; + m4x4_identity(temp); + temp[5] =(vec_t) cx; temp[6] =(vec_t) sx; + temp[9] =(vec_t)-sx; temp[10]=(vec_t) cx; + m4x4_premultiply_by_m4x4(matrix, temp); + m4x4_identity(temp); + temp[0] =(vec_t) cy; temp[2] =(vec_t)-sy; + temp[8] =(vec_t) sy; temp[10]=(vec_t) cy; + m4x4_premultiply_by_m4x4(matrix, temp); + } + break; + + case eXZY: + m4x4_identity(matrix); + matrix[5] =(vec_t) cx; matrix[6] =(vec_t) sx; + matrix[9] =(vec_t)-sx; matrix[10]=(vec_t) cx; + + { + m4x4_t temp; + m4x4_identity(temp); + temp[0] =(vec_t) cz; temp[1] =(vec_t) sz; + temp[4] =(vec_t)-sz; temp[5] =(vec_t) cz; + m4x4_premultiply_by_m4x4(matrix, temp); + m4x4_identity(temp); + temp[0] =(vec_t) cy; temp[2] =(vec_t)-sy; + temp[8] =(vec_t) sy; temp[10]=(vec_t) cy; + m4x4_premultiply_by_m4x4(matrix, temp); + } + break; + + case eYXZ: + +/* transposed +| cy.cz + sx.sy.-sz + -cx.sy.0 0.cz + cx.-sz + sx.0 sy.cz + -sx.cy.-sz + cx.cy.0 | +| cy.sz + sx.sy.cz + -cx.sy.0 0.sz + cx.cz + sx.0 sy.sz + -sx.cy.cz + cx.cy.0 | +| cy.0 + sx.sy.0 + -cx.sy.1 0.0 + cx.0 + sx.1 sy.0 + -sx.cy.0 + cx.cy.1 | +*/ + +#if 1 + + { + matrix[0] = (vec_t)(cy*cz + sx*sy*-sz); + matrix[1] = (vec_t)(cy*sz + sx*sy*cz); + matrix[2] = (vec_t)(-cx*sy); + matrix[4] = (vec_t)(cx*-sz); + matrix[5] = (vec_t)(cx*cz); + matrix[6] = (vec_t)(sx); + matrix[8] = (vec_t)(sy*cz + -sx*cy*-sz); + matrix[9] = (vec_t)(sy*sz + -sx*cy*cz); + matrix[10] = (vec_t)(cx*cy); + } + + matrix[12] = matrix[13] = matrix[14] = matrix[3] = matrix[7] = matrix[11] = 0; + matrix[15] = 1; + +#else + + m4x4_identity(matrix); + matrix[0] =(vec_t) cy; matrix[2] =(vec_t)-sy; + matrix[8] =(vec_t) sy; matrix[10]=(vec_t) cy; + + { + m4x4_t temp; + m4x4_identity(temp); + temp[5] =(vec_t) cx; temp[6] =(vec_t) sx; + temp[9] =(vec_t)-sx; temp[10]=(vec_t) cx; + m4x4_premultiply_by_m4x4(matrix, temp); + m4x4_identity(temp); + temp[0] =(vec_t) cz; temp[1] =(vec_t) sz; + temp[4] =(vec_t)-sz; temp[5] =(vec_t) cz; + m4x4_premultiply_by_m4x4(matrix, temp); + } +#endif + break; + + case eZYX: +#if 1 + + { + matrix[0] = (vec_t)(cy*cz); + matrix[4] = (vec_t)(cy*-sz); + matrix[8] = (vec_t)sy; + matrix[1] = (vec_t)(sx*sy*cz + cx*sz); + matrix[5] = (vec_t)(sx*sy*-sz + cx*cz); + matrix[9] = (vec_t)(-sx*cy); + matrix[2] = (vec_t)(cx*-sy*cz + sx*sz); + matrix[6] = (vec_t)(cx*-sy*-sz + sx*cz); + matrix[10] = (vec_t)(cx*cy); + } + + matrix[12] = matrix[13] = matrix[14] = matrix[3] = matrix[7] = matrix[11] = 0; + matrix[15] = 1; + +#else + + m4x4_identity(matrix); + matrix[0] =(vec_t) cz; matrix[1] =(vec_t) sz; + matrix[4] =(vec_t)-sz; matrix[5] =(vec_t) cz; + { + m4x4_t temp; + m4x4_identity(temp); + temp[0] =(vec_t) cy; temp[2] =(vec_t)-sy; + temp[8] =(vec_t) sy; temp[10]=(vec_t) cy; + m4x4_premultiply_by_m4x4(matrix, temp); + m4x4_identity(temp); + temp[5] =(vec_t) cx; temp[6] =(vec_t) sx; + temp[9] =(vec_t)-sx; temp[10]=(vec_t) cx; + m4x4_premultiply_by_m4x4(matrix, temp); + } + +#endif + break; + + } +} + +void m4x4_scale_for_vec3(m4x4_t matrix, const vec3_t scale) +{ + matrix[1] = matrix[2] = matrix[3] = + matrix[4] = matrix[6] = matrix[7] = + matrix[8] = matrix[9] = matrix[11] = + matrix[12] = matrix[13] = matrix[14] = 0; + + matrix[15] = 1; + + matrix[0] = scale[0]; + matrix[5] = scale[1]; + matrix[10] = scale[2]; +} + +void m4x4_rotation_for_quat(m4x4_t matrix, const vec4_t quat) +{ +#if 0 + const double xx = quat[0] * quat[0]; + const double xy = quat[0] * quat[1]; + const double xz = quat[0] * quat[2]; + const double xw = quat[0] * quat[3]; + + const double yy = quat[1] * quat[1]; + const double yz = quat[1] * quat[2]; + const double yw = quat[1] * quat[3]; + + const double zz = quat[2] * quat[2]; + const double zw = quat[2] * quat[3]; + + matrix[0] = 1 - 2 * ( yy + zz ); + matrix[4] = 2 * ( xy - zw ); + matrix[8] = 2 * ( xz + yw ); + + matrix[1] = 2 * ( xy + zw ); + matrix[5] = 1 - 2 * ( xx + zz ); + matrix[9] = 2 * ( yz - xw ); + + matrix[2] = 2 * ( xz - yw ); + matrix[6] = 2 * ( yz + xw ); + matrix[10] = 1 - 2 * ( xx + yy ); +#else + const double x2 = quat[0] + quat[0]; + const double y2 = quat[1] + quat[1]; + const double z2 = quat[2] + quat[2]; + const double xx = quat[0] * x2; + const double xy = quat[0] * y2; + const double xz = quat[0] * z2; + const double yy = quat[1] * y2; + const double yz = quat[1] * z2; + const double zz = quat[2] * z2; + const double wx = quat[3] * x2; + const double wy = quat[3] * y2; + const double wz = quat[3] * z2; + + matrix[0] = (vec_t)( 1.0 - (yy + zz) ); + matrix[4] = (vec_t)(xy - wz); + matrix[8] = (vec_t)(xz + wy); + + matrix[1] = (vec_t)(xy + wz); + matrix[5] = (vec_t)( 1.0 - (xx + zz) ); + matrix[9] = (vec_t)(yz - wx); + + matrix[2] = (vec_t)(xz - wy); + matrix[6] = (vec_t)(yz + wx); + matrix[10] = (vec_t)( 1.0 - (xx + yy) ); +#endif + + matrix[3] = matrix[7] = matrix[11] = matrix[12] = matrix[13] = matrix[14] = 0; + matrix[15] = 1; +} + +void m4x4_rotation_for_axisangle(m4x4_t matrix, const vec3_t axis, double angle) +{ + vec4_t quat; + quat_for_axisangle(quat, axis, angle); + m4x4_rotation_for_quat(matrix, quat); +} + +void m4x4_frustum(m4x4_t matrix, + vec_t left, vec_t right, + vec_t bottom, vec_t top, + vec_t nearval, vec_t farval) +{ + matrix[0] = (vec_t)( (2*nearval) / (right-left) ); + matrix[1] = 0; + matrix[2] = 0; + matrix[3] = 0; + + matrix[4] = 0; + matrix[5] = (vec_t)( (2*nearval) / (top-bottom) ); + matrix[6] = 0; + matrix[7] = 0; + + matrix[8] = (vec_t)( (right+left) / (right-left) ); + matrix[9] = (vec_t)( (top+bottom) / (top-bottom) ); + matrix[10] = (vec_t)( -(farval+nearval) / (farval-nearval) ); + matrix[11] =-1; + + matrix[12] = 0; + matrix[13] = 0; + matrix[14] = (vec_t)( -(2*farval*nearval) / (farval-nearval) ); + matrix[15] = 0; +} + + +void m4x4_get_translation_vec3(const m4x4_t matrix, vec3_t translation) +{ + translation[0] = matrix[12]; + translation[1] = matrix[13]; + translation[2] = matrix[14]; +} + +void m4x4_get_rotation_vec3(const m4x4_t matrix, vec3_t euler, eulerOrder_t order) +{ + double a, ca; + + switch(order) + { + case eXYZ: + a = asin(-matrix[2]); + ca = cos(a); + euler[1] = (vec_t)RAD2DEG(a); /* Calculate Y-axis angle */ + + if (fabs(ca) > 0.005) /* Gimbal lock? */ + { + /* No, so get Z-axis angle */ + euler[2] = (vec_t)RAD2DEG(atan2(matrix[1] / ca, matrix[0]/ ca)); + + /* Get X-axis angle */ + euler[0] = (vec_t)RAD2DEG(atan2(matrix[6] / ca, matrix[10] / ca)); + } + else /* Gimbal lock has occurred */ + { + /* Set Z-axis angle to zero */ + euler[2] = 0; + + /* And calculate X-axis angle */ + euler[0] = (vec_t)RAD2DEG(atan2(-matrix[9], matrix[5])); + } + break; + case eYZX: + /* NOT IMPLEMENTED */ + break; + case eZXY: + /* NOT IMPLEMENTED */ + break; + case eXZY: + /* NOT IMPLEMENTED */ + break; + case eYXZ: + a = asin(matrix[6]); + ca = cos(a); + euler[0] = (vec_t)RAD2DEG(a); /* Calculate X-axis angle */ + + if (fabs(ca) > 0.005) /* Gimbal lock? */ + { + /* No, so get Y-axis angle */ + euler[1] = (vec_t)RAD2DEG(atan2(-matrix[2] / ca, matrix[10]/ ca)); + + /* Get Z-axis angle */ + euler[2] = (vec_t)RAD2DEG(atan2(-matrix[4] / ca, matrix[5] / ca)); + } + else /* Gimbal lock has occurred */ + { + /* Set Z-axis angle to zero */ + euler[2] = 0; + + /* And calculate Y-axis angle */ + euler[1] = (vec_t)RAD2DEG(atan2(matrix[8], matrix[0])); + } + break; + case eZYX: + a = asin(matrix[8]); + ca = cos(a); + euler[1] = (vec_t)RAD2DEG(a); /* Calculate Y-axis angle */ + + if (fabs(ca) > 0.005) /* Gimbal lock? */ + { + /* No, so get X-axis angle */ + euler[0] = (vec_t)RAD2DEG(atan2(-matrix[9] / ca, matrix[10]/ ca)); + + /* Get Z-axis angle */ + euler[2] = (vec_t)RAD2DEG(atan2(-matrix[4] / ca, matrix[0] / ca)); + } + else /* Gimbal lock has occurred */ + { + /* Set X-axis angle to zero */ + euler[0] = 0; + + /* And calculate Z-axis angle */ + euler[2] = (vec_t)RAD2DEG(atan2(matrix[1], matrix[5])); + } + break; + } + + /* return only positive angles in [0,360] */ + if (euler[0] < 0) euler[0] += 360; + if (euler[1] < 0) euler[1] += 360; + if (euler[2] < 0) euler[2] += 360; +} + +void m4x4_get_scale_vec3(const m4x4_t matrix, vec3_t scale) +{ + scale[0] = VectorLength(matrix+0); + scale[1] = VectorLength(matrix+4); + scale[2] = VectorLength(matrix+8); +} + +void m4x4_get_transform_vec3(const m4x4_t matrix, vec3_t translation, vec3_t euler, eulerOrder_t order, vec3_t scale) +{ + m4x4_t normalised; + m4x4_assign(normalised, matrix); + scale[0] = VectorNormalize(normalised+0, normalised+0); + scale[1] = VectorNormalize(normalised+4, normalised+4); + scale[2] = VectorNormalize(normalised+8, normalised+8); + if(m4x4_handedness(normalised) == eLeftHanded) + { + VectorNegate(normalised+0, normalised+0); + VectorNegate(normalised+4, normalised+4); + VectorNegate(normalised+8, normalised+8); + scale[0] = -scale[0]; + scale[1] = -scale[1]; + scale[2] = -scale[2]; + } + m4x4_get_rotation_vec3(normalised, euler, order); + m4x4_get_translation_vec3(matrix, translation); +} + +void m4x4_translate_by_vec3(m4x4_t matrix, const vec3_t translation) +{ + m4x4_t temp; + m4x4_translation_for_vec3(temp, translation); + m4x4_multiply_by_m4x4(matrix, temp); +} + +void m4x4_rotate_by_vec3(m4x4_t matrix, const vec3_t euler, eulerOrder_t order) +{ + m4x4_t temp; + m4x4_rotation_for_vec3(temp, euler, order); + m4x4_multiply_by_m4x4(matrix, temp); +} + +void m4x4_scale_by_vec3(m4x4_t matrix, const vec3_t scale) +{ + m4x4_t temp; + m4x4_scale_for_vec3(temp, scale); + m4x4_multiply_by_m4x4(matrix, temp); +} + +void m4x4_rotate_by_quat(m4x4_t matrix, const vec4_t rotation) +{ + m4x4_t temp; + m4x4_rotation_for_quat(temp, rotation); + m4x4_multiply_by_m4x4(matrix, temp); +} + +void m4x4_rotate_by_axisangle(m4x4_t matrix, const vec3_t axis, double angle) +{ + m4x4_t temp; + m4x4_rotation_for_axisangle(temp, axis, angle); + m4x4_multiply_by_m4x4(matrix, temp); +} + +void m4x4_transform_by_vec3(m4x4_t matrix, const vec3_t translation, const vec3_t euler, eulerOrder_t order, const vec3_t scale) +{ + m4x4_translate_by_vec3(matrix, translation); + m4x4_rotate_by_vec3(matrix, euler, order); + m4x4_scale_by_vec3(matrix, scale); +} + +void m4x4_pivoted_rotate_by_vec3(m4x4_t matrix, const vec3_t euler, eulerOrder_t order, const vec3_t pivotpoint) +{ + vec3_t vec3_temp; + VectorNegate(pivotpoint, vec3_temp); + + m4x4_translate_by_vec3(matrix, pivotpoint); + m4x4_rotate_by_vec3(matrix, euler, order); + m4x4_translate_by_vec3(matrix, vec3_temp); +} + +void m4x4_pivoted_scale_by_vec3(m4x4_t matrix, const vec3_t scale, const vec3_t pivotpoint) +{ + vec3_t vec3_temp; + VectorNegate(pivotpoint, vec3_temp); + + m4x4_translate_by_vec3(matrix, pivotpoint); + m4x4_scale_by_vec3(matrix, scale); + m4x4_translate_by_vec3(matrix, vec3_temp); +} + +void m4x4_pivoted_transform_by_vec3(m4x4_t matrix, const vec3_t translation, const vec3_t euler, eulerOrder_t order, const vec3_t scale, const vec3_t pivotpoint) +{ + vec3_t vec3_temp; + + VectorAdd(pivotpoint, translation, vec3_temp); + m4x4_translate_by_vec3(matrix, vec3_temp); + m4x4_rotate_by_vec3(matrix, euler, order); + m4x4_scale_by_vec3(matrix, scale); + VectorNegate(pivotpoint, vec3_temp); + m4x4_translate_by_vec3(matrix, vec3_temp); +} + +void m4x4_pivoted_transform_by_rotation(m4x4_t matrix, const vec3_t translation, const m4x4_t rotation, const vec3_t scale, const vec3_t pivotpoint) +{ + vec3_t vec3_temp; + + VectorAdd(pivotpoint, translation, vec3_temp); + m4x4_translate_by_vec3(matrix, vec3_temp); + m4x4_multiply_by_m4x4(matrix, rotation); + m4x4_scale_by_vec3(matrix, scale); + VectorNegate(pivotpoint, vec3_temp); + m4x4_translate_by_vec3(matrix, vec3_temp); +} + +void m4x4_pivoted_rotate_by_quat(m4x4_t matrix, const vec4_t rotation, const vec3_t pivotpoint) +{ + vec3_t vec3_temp; + VectorNegate(pivotpoint, vec3_temp); + + m4x4_translate_by_vec3(matrix, pivotpoint); + m4x4_rotate_by_quat(matrix, rotation); + m4x4_translate_by_vec3(matrix, vec3_temp); +} + +void m4x4_pivoted_rotate_by_axisangle(m4x4_t matrix, const vec3_t axis, double angle, const vec3_t pivotpoint) +{ + vec3_t vec3_temp; + VectorNegate(pivotpoint, vec3_temp); + + m4x4_translate_by_vec3(matrix, pivotpoint); + m4x4_rotate_by_axisangle(matrix, axis, angle); + m4x4_translate_by_vec3(matrix, vec3_temp); +} + +/* +A = A.B + +A0 = B0 * A0 + B1 * A4 + B2 * A8 + B3 * A12 +A4 = B4 * A0 + B5 * A4 + B6 * A8 + B7 * A12 +A8 = B8 * A0 + B9 * A4 + B10* A8 + B11* A12 +A12= B12* A0 + B13* A4 + B14* A8 + B15* A12 + +A1 = B0 * A1 + B1 * A5 + B2 * A9 + B3 * A13 +A5 = B4 * A1 + B5 * A5 + B6 * A9 + B7 * A13 +A9 = B8 * A1 + B9 * A5 + B10* A9 + B11* A13 +A13= B12* A1 + B13* A5 + B14* A9 + B15* A13 + +A2 = B0 * A2 + B1 * A6 + B2 * A10+ B3 * A14 +A6 = B4 * A2 + B5 * A6 + B6 * A10+ B7 * A14 +A10= B8 * A2 + B9 * A6 + B10* A10+ B11* A14 +A14= B12* A2 + B13* A6 + B14* A10+ B15* A14 + +A3 = B0 * A3 + B1 * A7 + B2 * A11+ B3 * A15 +A7 = B4 * A3 + B5 * A7 + B6 * A11+ B7 * A15 +A11= B8 * A3 + B9 * A7 + B10* A11+ B11* A15 +A15= B12* A3 + B13* A7 + B14* A11+ B15* A15 +*/ + +void m4x4_multiply_by_m4x4(m4x4_t dst, const m4x4_t src) +{ + vec_t dst0, dst1, dst2, dst3; + +#if 1 + + dst0 = src[0] * dst[0] + src[1] * dst[4] + src[2] * dst[8] + src[3] * dst[12]; + dst1 = src[4] * dst[0] + src[5] * dst[4] + src[6] * dst[8] + src[7] * dst[12]; + dst2 = src[8] * dst[0] + src[9] * dst[4] + src[10]* dst[8] + src[11]* dst[12]; + dst3 = src[12]* dst[0] + src[13]* dst[4] + src[14]* dst[8] + src[15]* dst[12]; + dst[0] = dst0; dst[4] = dst1; dst[8] = dst2; dst[12]= dst3; + + dst0 = src[0] * dst[1] + src[1] * dst[5] + src[2] * dst[9] + src[3] * dst[13]; + dst1 = src[4] * dst[1] + src[5] * dst[5] + src[6] * dst[9] + src[7] * dst[13]; + dst2 = src[8] * dst[1] + src[9] * dst[5] + src[10]* dst[9] + src[11]* dst[13]; + dst3 = src[12]* dst[1] + src[13]* dst[5] + src[14]* dst[9] + src[15]* dst[13]; + dst[1] = dst0; dst[5] = dst1; dst[9] = dst2; dst[13]= dst3; + + dst0 = src[0] * dst[2] + src[1] * dst[6] + src[2] * dst[10]+ src[3] * dst[14]; + dst1 = src[4] * dst[2] + src[5] * dst[6] + src[6] * dst[10]+ src[7] * dst[14]; + dst2 = src[8] * dst[2] + src[9] * dst[6] + src[10]* dst[10]+ src[11]* dst[14]; + dst3 = src[12]* dst[2] + src[13]* dst[6] + src[14]* dst[10]+ src[15]* dst[14]; + dst[2] = dst0; dst[6] = dst1; dst[10]= dst2; dst[14]= dst3; + + dst0 = src[0] * dst[3] + src[1] * dst[7] + src[2] * dst[11]+ src[3] * dst[15]; + dst1 = src[4] * dst[3] + src[5] * dst[7] + src[6] * dst[11]+ src[7] * dst[15]; + dst2 = src[8] * dst[3] + src[9] * dst[7] + src[10]* dst[11]+ src[11]* dst[15]; + dst3 = src[12]* dst[3] + src[13]* dst[7] + src[14]* dst[11]+ src[15]* dst[15]; + dst[3] = dst0; dst[7] = dst1; dst[11]= dst2; dst[15]= dst3; + +#else + + vec_t * p = dst; + for(int i=0;i<4;i++) + { + dst1 = src[0] * p[0]; + dst1 += src[1] * p[4]; + dst1 += src[2] * p[8]; + dst1 += src[3] * p[12]; + dst2 = src[4] * p[0]; + dst2 += src[5] * p[4]; + dst2 += src[6] * p[8]; + dst2 += src[7] * p[12]; + dst3 = src[8] * p[0]; + dst3 += src[9] * p[4]; + dst3 += src[10] * p[8]; + dst3 += src[11] * p[12]; + dst4 = src[12] * p[0]; + dst4 += src[13] * p[4]; + dst4 += src[14] * p[8]; + dst4 += src[15] * p[12]; + + p[0] = dst1; + p[4] = dst2; + p[8] = dst3; + p[12] = dst4; + p++; + } + +#endif +} + +/* +A = B.A + +A0 = A0 * B0 + A1 * B4 + A2 * B8 + A3 * B12 +A1 = A0 * B1 + A1 * B5 + A2 * B9 + A3 * B13 +A2 = A0 * B2 + A1 * B6 + A2 * B10+ A3 * B14 +A3 = A0 * B3 + A1 * B7 + A2 * B11+ A3 * B15 + +A4 = A4 * B0 + A5 * B4 + A6 * B8 + A7 * B12 +A5 = A4 * B1 + A5 * B5 + A6 * B9 + A7 * B13 +A6 = A4 * B2 + A5 * B6 + A6 * B10+ A7 * B14 +A7 = A4 * B3 + A5 * B7 + A6 * B11+ A7 * B15 + +A8 = A8 * B0 + A9 * B4 + A10* B8 + A11* B12 +A9 = A8 * B1 + A9 * B5 + A10* B9 + A11* B13 +A10= A8 * B2 + A9 * B6 + A10* B10+ A11* B14 +A11= A8 * B3 + A9 * B7 + A10* B11+ A11* B15 + +A12= A12* B0 + A13* B4 + A14* B8 + A15* B12 +A13= A12* B1 + A13* B5 + A14* B9 + A15* B13 +A14= A12* B2 + A13* B6 + A14* B10+ A15* B14 +A15= A12* B3 + A13* B7 + A14* B11+ A15* B15 +*/ + +void m4x4_premultiply_by_m4x4(m4x4_t dst, const m4x4_t src) +{ + vec_t dst0, dst1, dst2, dst3; + +#if 1 + + dst0 = dst[0] * src[0] + dst[1] * src[4] + dst[2] * src[8] + dst[3] * src[12]; + dst1 = dst[0] * src[1] + dst[1] * src[5] + dst[2] * src[9] + dst[3] * src[13]; + dst2 = dst[0] * src[2] + dst[1] * src[6] + dst[2] * src[10]+ dst[3] * src[14]; + dst3 = dst[0] * src[3] + dst[1] * src[7] + dst[2] * src[11]+ dst[3] * src[15]; + dst[0] = dst0; dst[1] = dst1; dst[2] = dst2; dst[3]= dst3; + + dst0 = dst[4] * src[0] + dst[5] * src[4] + dst[6] * src[8] + dst[7] * src[12]; + dst1 = dst[4] * src[1] + dst[5] * src[5] + dst[6] * src[9] + dst[7] * src[13]; + dst2 = dst[4] * src[2] + dst[5] * src[6] + dst[6] * src[10]+ dst[7] * src[14]; + dst3 = dst[4] * src[3] + dst[5] * src[7] + dst[6] * src[11]+ dst[7] * src[15]; + dst[4] = dst0; dst[5] = dst1; dst[6] = dst2; dst[7]= dst3; + + dst0 = dst[8] * src[0] + dst[9] * src[4] + dst[10]* src[8] + dst[11]* src[12]; + dst1 = dst[8] * src[1] + dst[9] * src[5] + dst[10]* src[9] + dst[11]* src[13]; + dst2 = dst[8] * src[2] + dst[9] * src[6] + dst[10]* src[10]+ dst[11]* src[14]; + dst3 = dst[8] * src[3] + dst[9] * src[7] + dst[10]* src[11]+ dst[11]* src[15]; + dst[8] = dst0; dst[9] = dst1; dst[10] = dst2; dst[11]= dst3; + + dst0 = dst[12]* src[0] + dst[13]* src[4] + dst[14]* src[8] + dst[15]* src[12]; + dst1 = dst[12]* src[1] + dst[13]* src[5] + dst[14]* src[9] + dst[15]* src[13]; + dst2 = dst[12]* src[2] + dst[13]* src[6] + dst[14]* src[10]+ dst[15]* src[14]; + dst3 = dst[12]* src[3] + dst[13]* src[7] + dst[14]* src[11]+ dst[15]* src[15]; + dst[12] = dst0; dst[13] = dst1; dst[14] = dst2; dst[15]= dst3; + +#else + + vec_t* p = dst; + for(int i=0;i<4;i++) + { + dst1 = src[0] * p[0]; + dst2 = src[1] * p[0]; + dst3 = src[2] * p[0]; + dst4 = src[3] * p[0]; + dst1 += src[4] * p[1]; + dst2 += src[5] * p[1]; + dst3 += src[6] * p[1]; + dst4 += src[7] * p[1]; + dst1 += src[8] * p[2]; + dst2 += src[9] * p[2]; + dst4 += src[11] * p[2]; + dst3 += src[10] * p[2]; + dst1 += src[12] * p[3]; + dst2 += src[13] * p[3]; + dst3 += src[14] * p[3]; + dst4 += src[15] * p[3]; + + *p++ = dst1; + *p++ = dst2; + *p++ = dst3; + *p++ = dst4; + } + +#endif +} + +void m4x4_orthogonal_multiply_by_m4x4(m4x4_t dst, const m4x4_t src) +{ + vec_t dst0, dst1, dst2, dst3; + + dst0 = src[0] * dst[0] + src[1] * dst[4] + src[2] * dst[8]; + dst1 = src[4] * dst[0] + src[5] * dst[4] + src[6] * dst[8]; + dst2 = src[8] * dst[0] + src[9] * dst[4] + src[10]* dst[8]; + dst3 = src[12]* dst[0] + src[13]* dst[4] + src[14]* dst[8] + dst[12]; + dst[0] = dst0; dst[4] = dst1; dst[8] = dst2; dst[12]= dst3; + + dst0 = src[0] * dst[1] + src[1] * dst[5] + src[2] * dst[9]; + dst1 = src[4] * dst[1] + src[5] * dst[5] + src[6] * dst[9]; + dst2 = src[8] * dst[1] + src[9] * dst[5] + src[10]* dst[9]; + dst3 = src[12]* dst[1] + src[13]* dst[5] + src[14]* dst[9] + dst[13]; + dst[1] = dst0; dst[5] = dst1; dst[9] = dst2; dst[13]= dst3; + + dst0 = src[0] * dst[2] + src[1] * dst[6] + src[2] * dst[10]; + dst1 = src[4] * dst[2] + src[5] * dst[6] + src[6] * dst[10]; + dst2 = src[8] * dst[2] + src[9] * dst[6] + src[10]* dst[10]; + dst3 = src[12]* dst[2] + src[13]* dst[6] + src[14]* dst[10]+ dst[14]; + dst[2] = dst0; dst[6] = dst1; dst[10]= dst2; dst[14]= dst3; +} + +void m4x4_orthogonal_premultiply_by_m4x4(m4x4_t dst, const m4x4_t src) +{ + vec_t dst0, dst1, dst2; + + dst0 = dst[0] * src[0] + dst[1] * src[4] + dst[2] * src[8]; + dst1 = dst[0] * src[1] + dst[1] * src[5] + dst[2] * src[9]; + dst2 = dst[0] * src[2] + dst[1] * src[6] + dst[2] * src[10]; + dst[0] = dst0; dst[1] = dst1; dst[2] = dst2; + + dst0 = dst[4] * src[0] + dst[5] * src[4] + dst[6] * src[8]; + dst1 = dst[4] * src[1] + dst[5] * src[5] + dst[6] * src[9]; + dst2 = dst[4] * src[2] + dst[5] * src[6] + dst[6] * src[10]; + dst[4] = dst0; dst[5] = dst1; dst[6] = dst2; + + dst0 = dst[8] * src[0] + dst[9] * src[4] + dst[10]* src[8]; + dst1 = dst[8] * src[1] + dst[9] * src[5] + dst[10]* src[9]; + dst2 = dst[8] * src[2] + dst[9] * src[6] + dst[10]* src[10]; + dst[8] = dst0; dst[9] = dst1; dst[10] = dst2; + + dst0 = dst[12]* src[0] + dst[13]* src[4] + dst[14]* src[8] + dst[15]* src[12]; + dst1 = dst[12]* src[1] + dst[13]* src[5] + dst[14]* src[9] + dst[15]* src[13]; + dst2 = dst[12]* src[2] + dst[13]* src[6] + dst[14]* src[10]+ dst[15]* src[14]; + dst[12] = dst0; dst[13] = dst1; dst[14] = dst2; +} + +void m4x4_transform_point(const m4x4_t matrix, vec3_t point) +{ + float out1, out2, out3; + + out1 = matrix[0] * point[0] + matrix[4] * point[1] + matrix[8] * point[2] + matrix[12]; + out2 = matrix[1] * point[0] + matrix[5] * point[1] + matrix[9] * point[2] + matrix[13]; + out3 = matrix[2] * point[0] + matrix[6] * point[1] + matrix[10] * point[2] + matrix[14]; + + point[0] = out1; + point[1] = out2; + point[2] = out3; +} + +void m4x4_transform_normal(const m4x4_t matrix, vec3_t normal) +{ + float out1, out2, out3; + + out1 = matrix[0] * normal[0] + matrix[4] * normal[1] + matrix[8] * normal[2]; + out2 = matrix[1] * normal[0] + matrix[5] * normal[1] + matrix[9] * normal[2]; + out3 = matrix[2] * normal[0] + matrix[6] * normal[1] + matrix[10] * normal[2]; + + normal[0] = out1; + normal[1] = out2; + normal[2] = out3; +} + +void m4x4_transform_vec4(const m4x4_t matrix, vec4_t vector) +{ + float out1, out2, out3, out4; + + out1 = matrix[0] * vector[0] + matrix[4] * vector[1] + matrix[8] * vector[2] + matrix[12] * vector[3]; + out2 = matrix[1] * vector[0] + matrix[5] * vector[1] + matrix[9] * vector[2] + matrix[13] * vector[3]; + out3 = matrix[2] * vector[0] + matrix[6] * vector[1] + matrix[10] * vector[2] + matrix[14] * vector[3]; + out4 = matrix[3] * vector[0] + matrix[7] * vector[1] + matrix[11] * vector[2] + matrix[15] * vector[3]; + + vector[0] = out1; + vector[1] = out2; + vector[2] = out3; + vector[3] = out4; +} + +#define CLIP_X_LT_W(p) ((p)[0] < (p)[3]) +#define CLIP_X_GT_W(p) ((p)[0] > -(p)[3]) +#define CLIP_Y_LT_W(p) ((p)[1] < (p)[3]) +#define CLIP_Y_GT_W(p) ((p)[1] > -(p)[3]) +#define CLIP_Z_LT_W(p) ((p)[2] < (p)[3]) +#define CLIP_Z_GT_W(p) ((p)[2] > -(p)[3]) + +clipmask_t homogenous_clip_point(const vec4_t clipped) +{ + clipmask_t result = CLIP_FAIL; + if(CLIP_X_LT_W(clipped)) result &= ~CLIP_LT_X; // X < W + if(CLIP_X_GT_W(clipped)) result &= ~CLIP_GT_X; // X > -W + if(CLIP_Y_LT_W(clipped)) result &= ~CLIP_LT_Y; // Y < W + if(CLIP_Y_GT_W(clipped)) result &= ~CLIP_GT_Y; // Y > -W + if(CLIP_Z_LT_W(clipped)) result &= ~CLIP_LT_Z; // Z < W + if(CLIP_Z_GT_W(clipped)) result &= ~CLIP_GT_Z; // Z > -W + return result; +} + +clipmask_t m4x4_clip_point(const m4x4_t matrix, const vec3_t point, vec4_t clipped) +{ + clipped[0] = point[0]; + clipped[1] = point[1]; + clipped[2] = point[2]; + clipped[3] = 1; + m4x4_transform_vec4(matrix, clipped); + return homogenous_clip_point(clipped); +} + + +unsigned int homogenous_clip_triangle(vec4_t clipped[9]) +{ + vec4_t buffer[9]; + unsigned int rcount = 3; + unsigned int wcount = 0; + vec_t const* rptr = clipped[0]; + vec_t* wptr = buffer[0]; + const vec_t* p0; + const vec_t* p1; + unsigned char b0, b1; + + unsigned int i; + double scale; + + p0 = rptr; + b0 = CLIP_X_LT_W(p0); + for(i=0; i= 0.0) pos += det; else neg += det; + + det = src[1] * src[6] * src[8]; + if (det >= 0.0) pos += det; else neg += det; + + det = src[2] * src[4] * src[9]; + if (det >= 0.0) pos += det; else neg += det; + + det = -src[2] * src[5] * src[8]; + if (det >= 0.0) pos += det; else neg += det; + + det = -src[1] * src[4] * src[10]; + if (det >= 0.0) pos += det; else neg += det; + + det = -src[0] * src[6] * src[9]; + if (det >= 0.0) pos += det; else neg += det; + + det = pos + neg; +#elif 0 + float det + = (src[0] * src[5] * src[10]) + + (src[1] * src[6] * src[8]) + + (src[2] * src[4] * src[9]) + - (src[2] * src[5] * src[8]) + - (src[1] * src[4] * src[10]) + - (src[0] * src[6] * src[9]); +#else + float det + = src[0] * ( src[5]*src[10] - src[9]*src[6] ) + - src[1] * ( src[4]*src[10] - src[8]*src[6] ) + + src[2] * ( src[4]*src[9] - src[8]*src[5] ); + +#endif + + if (det*det < 1e-25) + return 1; + + det = 1.0f / det; + matrix[0] = ( (src[5]*src[10]- src[6]*src[9] )*det); + matrix[1] = (- (src[1]*src[10]- src[2]*src[9] )*det); + matrix[2] = ( (src[1]*src[6] - src[2]*src[5] )*det); + matrix[4] = (- (src[4]*src[10]- src[6]*src[8] )*det); + matrix[5] = ( (src[0]*src[10]- src[2]*src[8] )*det); + matrix[6] = (- (src[0]*src[6] - src[2]*src[4] )*det); + matrix[8] = ( (src[4]*src[9] - src[5]*src[8] )*det); + matrix[9] = (- (src[0]*src[9] - src[1]*src[8] )*det); + matrix[10]= ( (src[0]*src[5] - src[1]*src[4] )*det); + } + + /* Do the translation part */ + matrix[12] = - (src[12] * matrix[0] + + src[13] * matrix[4] + + src[14] * matrix[8]); + matrix[13] = - (src[12] * matrix[1] + + src[13] * matrix[5] + + src[14] * matrix[9]); + matrix[14] = - (src[12] * matrix[2] + + src[13] * matrix[6] + + src[14] * matrix[10]); + + return 0; +} + +void quat_identity(vec4_t quat) +{ + quat[0] = quat[1] = quat[2] = 0; + quat[3] = 1; +} + +void quat_multiply_by_quat(vec4_t quat, const vec4_t other) +{ + const vec_t x = quat[3]*other[0] + quat[0]*other[3] + quat[1]*other[2] - quat[2]*other[1]; + const vec_t y = quat[3]*other[1] + quat[1]*other[3] + quat[2]*other[0] - quat[0]*other[2]; + const vec_t z = quat[3]*other[2] + quat[2]*other[3] + quat[0]*other[1] - quat[1]*other[0]; + const vec_t w = quat[3]*other[3] - quat[0]*other[0] - quat[1]*other[1] - quat[2]*other[2]; + quat[0] = x; + quat[1] = y; + quat[2] = z; + quat[3] = w; +} + +void quat_conjugate(vec4_t quat) +{ + VectorNegate(quat, quat); +} + +//! quaternion from two unit vectors +void quat_for_unit_vectors(vec4_t quat, const vec3_t from, const vec3_t to) +{ + CrossProduct(from, to, quat); + quat[3] = DotProduct(from, to); +} + +void quat_normalise(vec4_t quat) +{ + const vec_t n = 1 / ( quat[0] * quat[0] + quat[1] * quat[1] + quat[2] * quat[2] + quat[3] * quat[3] ); + quat[0] *= n; + quat[1] *= n; + quat[2] *= n; + quat[3] *= n; +} + +void quat_for_axisangle(vec4_t quat, const vec3_t axis, double angle) +{ + angle *= 0.5; + + quat[3] = (float)sin(angle); + + quat[0] = axis[0] * quat[3]; + quat[1] = axis[1] * quat[3]; + quat[2] = axis[2] * quat[3]; + quat[3] = (float)cos(angle); +} + +void m3x3_multiply_by_m3x3(m3x3_t matrix, const m3x3_t matrix_src) +{ + float *pDest = matrix; + float out1, out2, out3; + int i; + + for(i=0;i<3;i++) + { + out1 = matrix_src[0] * pDest[0]; + out1 += matrix_src[1] * pDest[3]; + out1 += matrix_src[2] * pDest[6]; + out2 = matrix_src[3] * pDest[0]; + out2 += matrix_src[4] * pDest[3]; + out2 += matrix_src[5] * pDest[6]; + out3 = matrix_src[6] * pDest[0]; + out3 += matrix_src[7] * pDest[3]; + out3 += matrix_src[8] * pDest[6]; + + pDest[0] = out1; + pDest[3] = out2; + pDest[6] = out3; + + pDest++; + } +} + +void m3x3_transform_vec3(const m3x3_t matrix, vec3_t vector) +{ + float out1, out2, out3; + + out1 = matrix[0] * vector[0]; + out1 += matrix[3] * vector[1]; + out1 += matrix[6] * vector[2]; + out2 = matrix[1] * vector[0]; + out2 += matrix[4] * vector[1]; + out2 += matrix[7] * vector[2]; + out3 = matrix[2] * vector[0]; + out3 += matrix[5] * vector[1]; + out3 += matrix[8] * vector[2]; + + vector[0] = out1; + vector[1] = out2; + vector[2] = out3; +} + +float m3_det( m3x3_t mat ) +{ + float det; + + det = mat[0] * ( mat[4]*mat[8] - mat[7]*mat[5] ) + - mat[1] * ( mat[3]*mat[8] - mat[6]*mat[5] ) + + mat[2] * ( mat[3]*mat[7] - mat[6]*mat[4] ); + + return( det ); +} + +int m3_inverse( m3x3_t mr, m3x3_t ma ) +{ + float det = m3_det( ma ); + + if (det == 0 ) + { + return 1; + } + + + mr[0] = ma[4]*ma[8] - ma[5]*ma[7] / det; + mr[1] = -( ma[1]*ma[8] - ma[7]*ma[2] ) / det; + mr[2] = ma[1]*ma[5] - ma[4]*ma[2] / det; + + mr[3] = -( ma[3]*ma[8] - ma[5]*ma[6] ) / det; + mr[4] = ma[0]*ma[8] - ma[6]*ma[2] / det; + mr[5] = -( ma[0]*ma[5] - ma[3]*ma[2] ) / det; + + mr[6] = ma[3]*ma[7] - ma[6]*ma[4] / det; + mr[7] = -( ma[0]*ma[7] - ma[6]*ma[1] ) / det; + mr[8] = ma[0]*ma[4] - ma[1]*ma[3] / det; + + return 0; +} + +void m4_submat( m4x4_t mr, m3x3_t mb, int i, int j ) +{ + int ti, tj, idst, jdst; + + for ( ti = 0; ti < 4; ti++ ) + { + if ( ti < i ) + idst = ti; + else + if ( ti > i ) + idst = ti-1; + + for ( tj = 0; tj < 4; tj++ ) + { + if ( tj < j ) + jdst = tj; + else + if ( tj > j ) + jdst = tj-1; + + if ( ti != i && tj != j ) + mb[idst*3 + jdst] = mr[ti*4 + tj ]; + } + } +} + +float m4_det( m4x4_t mr ) +{ + float det, result = 0, i = 1; + m3x3_t msub3; + int n; + + for ( n = 0; n < 4; n++, i *= -1 ) + { + m4_submat( mr, msub3, 0, n ); + + det = m3_det( msub3 ); + result += mr[n] * det * i; + } + + return result; +} + +int m4x4_invert(m4x4_t matrix) +{ + float mdet = m4_det( matrix ); + m3x3_t mtemp; + int i, j, sign; + m4x4_t m4x4_temp; + +#if 0 + if ( fabs( mdet ) < 0.0000000001 ) + return 1; +#endif + + m4x4_assign(m4x4_temp, matrix); + + for ( i = 0; i < 4; i++ ) + for ( j = 0; j < 4; j++ ) + { + sign = 1 - ( (i +j) % 2 ) * 2; + + m4_submat( m4x4_temp, mtemp, i, j ); + + matrix[i+j*4] = ( m3_det( mtemp ) * sign ) / mdet; /* FIXME: try using * inverse det and see if speed/accuracy are good enough */ + } + + return 0; +} +#if 0 +void m4x4_solve_ge(m4x4_t matrix, vec4_t x) +{ + int indx[4]; + int c,r; + int i; + int best; + float scale[4]; + float f, pivot; + float aug[4]; + float recip, ratio; + float* p; + + for(r=0; r<4; r++) + { + aug[r] = 0; + indx[r] = r; + } + + for (r=0; r<4; r++) + { + scale[r] = 0; + for (c=0; c<4; c++, p++) + { + if (fabs(*p) > scale[r]) + { + scale[r] = (float)fabs(*p); + } + } + } + + for (c=0; c<3; c++) + { + pivot = 0; + for (r=c; r<4; r++) + { + f = (float)fabs(matrix[(indx[r]<<2)+c]) / scale[indx[r]]; + if (f > pivot) + { + pivot = f; + best = r; + } + } + + i = indx[c]; + indx[c] = indx[best]; + indx[best] = i; + + recip = 1 / matrix[(indx[c]<<2)+c]; + + for (r=c+1; r<4; r++) + { + p = matrix + (indx[r]<<2); + ratio = p[c] * recip; + + for (i=c+1; i<4; i++) + p[i] -= ratio * matrix[(indx[c]<<2)+i]; + aug[indx[r]] -= ratio * aug[indx[c]]; + } + } + + x[indx[3]] = aug[indx[3]] / matrix[(indx[3]<<2)+3]; + for(r=2; r>=0; r--) + { + f = aug[indx[r]]; + p = matrix + (indx[r]<<2); + recip = 1 / p[r]; + for(c=(r+1); c<4; c++) + { + f -= (p[c] * x[indx[c]]); + } + x[indx[r]] = f * recip; + } +} +#endif + +#define N 3 + +int matrix_solve_ge(vec_t* matrix, vec_t* aug, vec3_t x) +{ + int indx[N]; + int c,r; + int i; + int best; + float scale[N]; + float f, pivot; + float ratio; + float* p; + + for(r=0; r scale[r]) + { + scale[r] = (float)fabs(*p); + } + } + } + + for (c=0; c pivot) + { + pivot = f; + best = r; + } + } + + if(best == -1) return 1; + + i = indx[c]; + indx[c] = indx[best]; + indx[best] = i; + + for (r=c+1; r=0; r--) + { + f = aug[indx[r]]; + p = matrix + (indx[r]*N); + for(c=(r+1); c=0;i--) + { + temp = b[i]; + for(j=(i+1);j + +const vec3_t vec3_origin = {0.0f,0.0f,0.0f}; + +const vec3_t g_vec3_axis_x = { 1, 0, 0, }; +const vec3_t g_vec3_axis_y = { 0, 1, 0, }; +const vec3_t g_vec3_axis_z = { 0, 0, 1, }; + +/* +================ +MakeNormalVectors + +Given a normalized forward vector, create two +other perpendicular vectors +================ +*/ +void MakeNormalVectors (vec3_t forward, vec3_t right, vec3_t up) +{ + float d; + + // this rotate and negate guarantees a vector + // not colinear with the original + right[1] = -forward[0]; + right[2] = forward[1]; + right[0] = forward[2]; + + d = DotProduct (right, forward); + VectorMA (right, -d, forward, right); + VectorNormalize (right, right); + CrossProduct (right, forward, up); +} + +vec_t VectorLength(const vec3_t v) +{ + int i; + float length; + + length = 0.0f; + for (i=0 ; i< 3 ; i++) + length += v[i]*v[i]; + length = (float)sqrt (length); + + return length; +} + +qboolean VectorCompare (const vec3_t v1, const vec3_t v2) +{ + int i; + + for (i=0 ; i<3 ; i++) + if (fabs(v1[i]-v2[i]) > EQUAL_EPSILON) + return qfalse; + + return qtrue; +} + +void VectorMA( const vec3_t va, vec_t scale, const vec3_t vb, vec3_t vc ) +{ + vc[0] = va[0] + scale*vb[0]; + vc[1] = va[1] + scale*vb[1]; + vc[2] = va[2] + scale*vb[2]; +} + +void _CrossProduct (vec3_t v1, vec3_t v2, vec3_t cross) +{ + cross[0] = v1[1]*v2[2] - v1[2]*v2[1]; + cross[1] = v1[2]*v2[0] - v1[0]*v2[2]; + cross[2] = v1[0]*v2[1] - v1[1]*v2[0]; +} + +vec_t _DotProduct (vec3_t v1, vec3_t v2) +{ + return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2]; +} + +void _VectorSubtract (vec3_t va, vec3_t vb, vec3_t out) +{ + out[0] = va[0]-vb[0]; + out[1] = va[1]-vb[1]; + out[2] = va[2]-vb[2]; +} + +void _VectorAdd (vec3_t va, vec3_t vb, vec3_t out) +{ + out[0] = va[0]+vb[0]; + out[1] = va[1]+vb[1]; + out[2] = va[2]+vb[2]; +} + +void _VectorCopy (vec3_t in, vec3_t out) +{ + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; +} + +vec_t VectorNormalize( const vec3_t in, vec3_t out ) { + vec_t length, ilength; + + length = (vec_t)sqrt (in[0]*in[0] + in[1]*in[1] + in[2]*in[2]); + if (length == 0) + { + VectorClear (out); + return 0; + } + + ilength = 1.0f/length; + out[0] = in[0]*ilength; + out[1] = in[1]*ilength; + out[2] = in[2]*ilength; + + return length; +} + +vec_t ColorNormalize( const vec3_t in, vec3_t out ) { + float max, scale; + + max = in[0]; + if (in[1] > max) + max = in[1]; + if (in[2] > max) + max = in[2]; + + if (max == 0) { + out[0] = out[1] = out[2] = 1.0; + return 0; + } + + scale = 1.0f / max; + + VectorScale (in, scale, out); + + return max; +} + +void VectorInverse (vec3_t v) +{ + v[0] = -v[0]; + v[1] = -v[1]; + v[2] = -v[2]; +} + +/* +void VectorScale (vec3_t v, vec_t scale, vec3_t out) +{ + out[0] = v[0] * scale; + out[1] = v[1] * scale; + out[2] = v[2] * scale; +} +*/ + +void VectorRotate (vec3_t vIn, vec3_t vRotation, vec3_t out) +{ + vec3_t vWork, va; + int nIndex[3][2]; + int i; + + VectorCopy(vIn, va); + VectorCopy(va, vWork); + nIndex[0][0] = 1; nIndex[0][1] = 2; + nIndex[1][0] = 2; nIndex[1][1] = 0; + nIndex[2][0] = 0; nIndex[2][1] = 1; + + for (i = 0; i < 3; i++) + { + if (vRotation[i] != 0) + { + float dAngle = vRotation[i] * Q_PI / 180.0f; + float c = (vec_t)cos(dAngle); + float s = (vec_t)sin(dAngle); + vWork[nIndex[i][0]] = va[nIndex[i][0]] * c - va[nIndex[i][1]] * s; + vWork[nIndex[i][1]] = va[nIndex[i][0]] * s + va[nIndex[i][1]] * c; + } + VectorCopy(vWork, va); + } + VectorCopy(vWork, out); +} + +void VectorRotateOrigin (vec3_t vIn, vec3_t vRotation, vec3_t vOrigin, vec3_t out) +{ + vec3_t vTemp, vTemp2; + + VectorSubtract(vIn, vOrigin, vTemp); + VectorRotate(vTemp, vRotation, vTemp2); + VectorAdd(vTemp2, vOrigin, out); +} + +void VectorPolar(vec3_t v, float radius, float theta, float phi) +{ + v[0]=(float)(radius * cos(theta) * cos(phi)); + v[1]=(float)(radius * sin(theta) * cos(phi)); + v[2]=(float)(radius * sin(phi)); +} + +void VectorSnap(vec3_t v) +{ + int i; + for (i = 0; i < 3; i++) + { + v[i] = (vec_t)FLOAT_TO_INTEGER(v[i]); + } +} + +void VectorISnap(vec3_t point, int snap) +{ + int i; + for (i = 0 ;i < 3 ; i++) + { + point[i] = (vec_t)FLOAT_SNAP(point[i], snap); + } +} + +void VectorFSnap(vec3_t point, float snap) +{ + int i; + for (i = 0 ;i < 3 ; i++) + { + point[i] = (vec_t)FLOAT_SNAP(point[i], snap); + } +} + +void _Vector5Add (vec5_t va, vec5_t vb, vec5_t out) +{ + out[0] = va[0]+vb[0]; + out[1] = va[1]+vb[1]; + out[2] = va[2]+vb[2]; + out[3] = va[3]+vb[3]; + out[4] = va[4]+vb[4]; +} + +void _Vector5Scale (vec5_t v, vec_t scale, vec5_t out) +{ + out[0] = v[0] * scale; + out[1] = v[1] * scale; + out[2] = v[2] * scale; + out[3] = v[3] * scale; + out[4] = v[4] * scale; +} + +void _Vector53Copy (vec5_t in, vec3_t out) +{ + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; +} + +// NOTE: added these from Ritual's Q3Radiant +void ClearBounds (vec3_t mins, vec3_t maxs) +{ + mins[0] = mins[1] = mins[2] = 99999; + maxs[0] = maxs[1] = maxs[2] = -99999; +} + +void AddPointToBounds (vec3_t v, vec3_t mins, vec3_t maxs) +{ + int i; + vec_t val; + + for (i=0 ; i<3 ; i++) + { + val = v[i]; + if (val < mins[i]) + mins[i] = val; + if (val > maxs[i]) + maxs[i] = val; + } +} + +void AngleVectors (vec3_t angles, vec3_t forward, vec3_t right, vec3_t up) +{ + float angle; + static float sr, sp, sy, cr, cp, cy; + // static to help MS compiler fp bugs + + angle = angles[YAW] * (Q_PI*2.0f / 360.0f); + sy = (vec_t)sin(angle); + cy = (vec_t)cos(angle); + angle = angles[PITCH] * (Q_PI*2.0f / 360.0f); + sp = (vec_t)sin(angle); + cp = (vec_t)cos(angle); + angle = angles[ROLL] * (Q_PI*2.0f / 360.0f); + sr = (vec_t)sin(angle); + cr = (vec_t)cos(angle); + + if (forward) + { + forward[0] = cp*cy; + forward[1] = cp*sy; + forward[2] = -sp; + } + if (right) + { + right[0] = -sr*sp*cy+cr*sy; + right[1] = -sr*sp*sy-cr*cy; + right[2] = -sr*cp; + } + if (up) + { + up[0] = cr*sp*cy+sr*sy; + up[1] = cr*sp*sy-sr*cy; + up[2] = cr*cp; + } +} + +void VectorToAngles( vec3_t vec, vec3_t angles ) +{ + float forward; + float yaw, pitch; + + if ( ( vec[ 0 ] == 0 ) && ( vec[ 1 ] == 0 ) ) + { + yaw = 0; + if ( vec[ 2 ] > 0 ) + { + pitch = 90; + } + else + { + pitch = 270; + } + } + else + { + yaw = (vec_t)atan2( vec[ 1 ], vec[ 0 ] ) * 180 / Q_PI; + if ( yaw < 0 ) + { + yaw += 360; + } + + forward = ( float )sqrt( vec[ 0 ] * vec[ 0 ] + vec[ 1 ] * vec[ 1 ] ); + pitch = (vec_t)atan2( vec[ 2 ], forward ) * 180 / Q_PI; + if ( pitch < 0 ) + { + pitch += 360; + } + } + + angles[ 0 ] = pitch; + angles[ 1 ] = yaw; + angles[ 2 ] = 0; +} + +/* +===================== +PlaneFromPoints + +Returns false if the triangle is degenrate. +The normal will point out of the clock for clockwise ordered points +===================== +*/ +qboolean PlaneFromPoints( vec4_t plane, const vec3_t a, const vec3_t b, const vec3_t c ) { + vec3_t d1, d2; + + VectorSubtract( b, a, d1 ); + VectorSubtract( c, a, d2 ); + CrossProduct( d2, d1, plane ); + if ( VectorNormalize( plane, plane ) == 0 ) { + return qfalse; + } + + plane[3] = DotProduct( a, plane ); + return qtrue; +} + +/* +** NormalToLatLong +** +** We use two byte encoded normals in some space critical applications. +** Lat = 0 at (1,0,0) to 360 (-1,0,0), encoded in 8-bit sine table format +** Lng = 0 at (0,0,1) to 180 (0,0,-1), encoded in 8-bit sine table format +** +*/ +void NormalToLatLong( const vec3_t normal, byte bytes[2] ) { + // check for singularities + if ( normal[0] == 0 && normal[1] == 0 ) { + if ( normal[2] > 0 ) { + bytes[0] = 0; + bytes[1] = 0; // lat = 0, long = 0 + } else { + bytes[0] = 128; + bytes[1] = 0; // lat = 0, long = 128 + } + } else { + int a, b; + + a = (int)( RAD2DEG( atan2( normal[1], normal[0] ) ) * (255.0f / 360.0f ) ); + a &= 0xff; + + b = (int)( RAD2DEG( acos( normal[2] ) ) * ( 255.0f / 360.0f ) ); + b &= 0xff; + + bytes[0] = b; // longitude + bytes[1] = a; // lattitude + } +} + +/* +================= +PlaneTypeForNormal +================= +*/ +int PlaneTypeForNormal (vec3_t normal) { + if (normal[0] == 1.0 || normal[0] == -1.0) + return PLANE_X; + if (normal[1] == 1.0 || normal[1] == -1.0) + return PLANE_Y; + if (normal[2] == 1.0 || normal[2] == -1.0) + return PLANE_Z; + + return PLANE_NON_AXIAL; +} + +/* +================ +MatrixMultiply +================ +*/ +void MatrixMultiply(float in1[3][3], float in2[3][3], float out[3][3]) { + out[0][0] = in1[0][0] * in2[0][0] + in1[0][1] * in2[1][0] + + in1[0][2] * in2[2][0]; + out[0][1] = in1[0][0] * in2[0][1] + in1[0][1] * in2[1][1] + + in1[0][2] * in2[2][1]; + out[0][2] = in1[0][0] * in2[0][2] + in1[0][1] * in2[1][2] + + in1[0][2] * in2[2][2]; + out[1][0] = in1[1][0] * in2[0][0] + in1[1][1] * in2[1][0] + + in1[1][2] * in2[2][0]; + out[1][1] = in1[1][0] * in2[0][1] + in1[1][1] * in2[1][1] + + in1[1][2] * in2[2][1]; + out[1][2] = in1[1][0] * in2[0][2] + in1[1][1] * in2[1][2] + + in1[1][2] * in2[2][2]; + out[2][0] = in1[2][0] * in2[0][0] + in1[2][1] * in2[1][0] + + in1[2][2] * in2[2][0]; + out[2][1] = in1[2][0] * in2[0][1] + in1[2][1] * in2[1][1] + + in1[2][2] * in2[2][1]; + out[2][2] = in1[2][0] * in2[0][2] + in1[2][1] * in2[1][2] + + in1[2][2] * in2[2][2]; +} + +void ProjectPointOnPlane( vec3_t dst, const vec3_t p, const vec3_t normal ) +{ + float d; + vec3_t n; + float inv_denom; + + inv_denom = 1.0F / DotProduct( normal, normal ); + + d = DotProduct( normal, p ) * inv_denom; + + n[0] = normal[0] * inv_denom; + n[1] = normal[1] * inv_denom; + n[2] = normal[2] * inv_denom; + + dst[0] = p[0] - d * n[0]; + dst[1] = p[1] - d * n[1]; + dst[2] = p[2] - d * n[2]; +} + +/* +** assumes "src" is normalized +*/ +void PerpendicularVector( vec3_t dst, const vec3_t src ) +{ + int pos; + int i; + vec_t minelem = 1.0F; + vec3_t tempvec; + + /* + ** find the smallest magnitude axially aligned vector + */ + for ( pos = 0, i = 0; i < 3; i++ ) + { + if ( fabs( src[i] ) < minelem ) + { + pos = i; + minelem = (vec_t)fabs( src[i] ); + } + } + tempvec[0] = tempvec[1] = tempvec[2] = 0.0F; + tempvec[pos] = 1.0F; + + /* + ** project the point onto the plane defined by src + */ + ProjectPointOnPlane( dst, tempvec, src ); + + /* + ** normalize the result + */ + VectorNormalize( dst, dst ); +} + +/* +=============== +RotatePointAroundVector + +This is not implemented very well... +=============== +*/ +void RotatePointAroundVector( vec3_t dst, const vec3_t dir, const vec3_t point, + float degrees ) { + float m[3][3]; + float im[3][3]; + float zrot[3][3]; + float tmpmat[3][3]; + float rot[3][3]; + int i; + vec3_t vr, vup, vf; + float rad; + + vf[0] = dir[0]; + vf[1] = dir[1]; + vf[2] = dir[2]; + + PerpendicularVector( vr, dir ); + CrossProduct( vr, vf, vup ); + + m[0][0] = vr[0]; + m[1][0] = vr[1]; + m[2][0] = vr[2]; + + m[0][1] = vup[0]; + m[1][1] = vup[1]; + m[2][1] = vup[2]; + + m[0][2] = vf[0]; + m[1][2] = vf[1]; + m[2][2] = vf[2]; + + memcpy( im, m, sizeof( im ) ); + + im[0][1] = m[1][0]; + im[0][2] = m[2][0]; + im[1][0] = m[0][1]; + im[1][2] = m[2][1]; + im[2][0] = m[0][2]; + im[2][1] = m[1][2]; + + memset( zrot, 0, sizeof( zrot ) ); + zrot[0][0] = zrot[1][1] = zrot[2][2] = 1.0F; + + rad = (float)DEG2RAD( degrees ); + zrot[0][0] = (vec_t)cos( rad ); + zrot[0][1] = (vec_t)sin( rad ); + zrot[1][0] = (vec_t)-sin( rad ); + zrot[1][1] = (vec_t)cos( rad ); + + MatrixMultiply( m, zrot, tmpmat ); + MatrixMultiply( tmpmat, im, rot ); + + for ( i = 0; i < 3; i++ ) { + dst[i] = rot[i][0] * point[0] + rot[i][1] * point[1] + rot[i][2] * point[2]; + } +} diff --git a/tools/urt/libs/mathlib/mathlib.dsp b/tools/urt/libs/mathlib/mathlib.dsp new file mode 100644 index 00000000..358da218 --- /dev/null +++ b/tools/urt/libs/mathlib/mathlib.dsp @@ -0,0 +1,126 @@ +# Microsoft Developer Studio Project File - Name="mathlib" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=mathlib - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "mathlib.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mathlib.mak" CFG="mathlib - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "mathlib - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "mathlib - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "mathlib" +# PROP Scc_LocalPath ".." +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "mathlib - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +MTL=midl.exe +F90=df.exe +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MD /W3 /O2 /I ".." /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "mathlib - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +MTL=midl.exe +F90=df.exe +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /ZI /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FR /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "mathlib - Win32 Release" +# Name "mathlib - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\bbox.c +# End Source File +# Begin Source File + +SOURCE=.\line.c +# End Source File +# Begin Source File + +SOURCE=.\linear.c +# End Source File +# Begin Source File + +SOURCE=.\m4x4.c +# End Source File +# Begin Source File + +SOURCE=.\mathlib.c +# End Source File +# Begin Source File + +SOURCE=.\ray.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\mathlib.h +# End Source File +# End Group +# End Target +# End Project diff --git a/tools/urt/libs/mathlib/mathlib.vcproj b/tools/urt/libs/mathlib/mathlib.vcproj new file mode 100644 index 00000000..fa2fb7d2 --- /dev/null +++ b/tools/urt/libs/mathlib/mathlib.vcproj @@ -0,0 +1,320 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/urt/libs/mathlib/ray.c b/tools/urt/libs/mathlib/ray.c new file mode 100644 index 00000000..10256800 --- /dev/null +++ b/tools/urt/libs/mathlib/ray.c @@ -0,0 +1,143 @@ +/* +This code provided under the terms of the Id Software +LIMITED USE SOFTWARE LICENSE AGREEMENT, a copy of which is included with the +GtkRadiant sources (see LICENSE_ID). If you did not receive a copy of +LICENSE_ID, please contact Id Software immediately at info@idsoftware.com. + +All changes and additions to the original source which have been developed by +other contributors (see CONTRIBUTORS) are provided under the terms of the +license the contributors choose (see LICENSE), to the extent permitted by the +LICENSE_ID. If you did not receive a copy of the contributor license, +please contact the GtkRadiant maintainers at info@gtkradiant.com immediately. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "mathlib.h" +#include + +vec3_t identity = { 0,0,0 }; + +void ray_construct_for_vec3(ray_t *ray, const vec3_t origin, const vec3_t direction) +{ + VectorCopy(origin, ray->origin); + VectorCopy(direction, ray->direction); +} + +void ray_transform(ray_t *ray, const m4x4_t matrix) +{ + m4x4_transform_point(matrix, ray->origin); + m4x4_transform_normal(matrix, ray->direction); +} + +vec_t ray_intersect_point(const ray_t *ray, const vec3_t point, vec_t epsilon, vec_t divergence) +{ + vec3_t displacement; + vec_t depth; + + // calc displacement of test point from ray origin + VectorSubtract(point, ray->origin, displacement); + // calc length of displacement vector along ray direction + depth = DotProduct(displacement, ray->direction); + if(depth < 0.0f) return (vec_t)FLT_MAX; + // calc position of closest point on ray to test point + VectorMA (ray->origin, depth, ray->direction, displacement); + // calc displacement of test point from closest point + VectorSubtract(point, displacement, displacement); + // calc length of displacement, subtract depth-dependant epsilon + if (VectorLength(displacement) - (epsilon + (depth * divergence)) > 0.0f) return (vec_t)FLT_MAX; + return depth; +} + +// Tomas Moller and Ben Trumbore. Fast, minimum storage ray-triangle intersection. Journal of graphics tools, 2(1):21-28, 1997 + +#define EPSILON 0.000001 + +vec_t ray_intersect_triangle(const ray_t *ray, qboolean bCullBack, const vec3_t vert0, const vec3_t vert1, const vec3_t vert2) +{ + float edge1[3], edge2[3], tvec[3], pvec[3], qvec[3]; + float det,inv_det; + float u, v; + vec_t depth = (vec_t)FLT_MAX; + + /* find vectors for two edges sharing vert0 */ + VectorSubtract(vert1, vert0, edge1); + VectorSubtract(vert2, vert0, edge2); + + /* begin calculating determinant - also used to calculate U parameter */ + CrossProduct(ray->direction, edge2, pvec); + + /* if determinant is near zero, ray lies in plane of triangle */ + det = DotProduct(edge1, pvec); + + if (bCullBack == qtrue) + { + if (det < EPSILON) + return depth; + + // calculate distance from vert0 to ray origin + VectorSubtract(ray->origin, vert0, tvec); + + // calculate U parameter and test bounds + u = DotProduct(tvec, pvec); + if (u < 0.0 || u > det) + return depth; + + // prepare to test V parameter + CrossProduct(tvec, edge1, qvec); + + // calculate V parameter and test bounds + v = DotProduct(ray->direction, qvec); + if (v < 0.0 || u + v > det) + return depth; + + // calculate t, scale parameters, ray intersects triangle + depth = DotProduct(edge2, qvec); + inv_det = 1.0f / det; + depth *= inv_det; + //u *= inv_det; + //v *= inv_det; + } + else + { + /* the non-culling branch */ + if (det > -EPSILON && det < EPSILON) + return depth; + inv_det = 1.0f / det; + + /* calculate distance from vert0 to ray origin */ + VectorSubtract(ray->origin, vert0, tvec); + + /* calculate U parameter and test bounds */ + u = DotProduct(tvec, pvec) * inv_det; + if (u < 0.0 || u > 1.0) + return depth; + + /* prepare to test V parameter */ + CrossProduct(tvec, edge1, qvec); + + /* calculate V parameter and test bounds */ + v = DotProduct(ray->direction, qvec) * inv_det; + if (v < 0.0 || u + v > 1.0) + return depth; + + /* calculate t, ray intersects triangle */ + depth = DotProduct(edge2, qvec) * inv_det; + } + return depth; +} + +vec_t ray_intersect_plane(const ray_t* ray, const vec3_t normal, vec_t dist) +{ + return -(DotProduct(normal, ray->origin) - dist) / DotProduct(ray->direction, normal); +} + diff --git a/tools/urt/libs/md5lib.h b/tools/urt/libs/md5lib.h new file mode 100644 index 00000000..7f63c61b --- /dev/null +++ b/tools/urt/libs/md5lib.h @@ -0,0 +1,91 @@ +/* + Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/* $Id: md5lib.h,v 1.1.2.1 2003/07/19 23:25:50 spog Exp $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321, whose + text is available at + http://www.ietf.org/rfc/rfc1321.txt + The code is derived from the text of the RFC, including the test suite + (section A.5) but excluding the rest of Appendix A. It does not include + any code or documentation that is identified in the RFC as being + copyrighted. + + The original and principal author of md5.h is L. Peter Deutsch + . Other authors are noted in the change history + that follows (in reverse chronological order): + + 2002-04-13 lpd Removed support for non-ANSI compilers; removed + references to Ghostscript; clarified derivation from RFC 1321; + now handles byte order either statically or dynamically. + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); + added conditionalization for C++ compilation from Martin + Purschke . + 1999-05-03 lpd Original version. + */ + +#ifndef md5_INCLUDED +# define md5_INCLUDED + +/* + * This package supports both compile-time and run-time determination of CPU + * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be + * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is + * defined as non-zero, the code will be compiled to run only on big-endian + * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to + * run on either big- or little-endian CPUs, but will run slightly less + * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. + */ + +typedef unsigned char md5_byte_t; /* 8-bit byte */ +typedef unsigned int md5_word_t; /* 32-bit word */ + +/* Define the state of the MD5 Algorithm. */ +typedef struct md5_state_s { + md5_word_t count[2]; /* message length in bits, lsw first */ + md5_word_t abcd[4]; /* digest buffer */ + md5_byte_t buf[64]; /* accumulate block */ +} md5_state_t; + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Initialize the algorithm. */ +void md5_init(md5_state_t *pms); + +/* Append a string to the message. */ +void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); + +/* Finish the message and return the digest. */ +void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +#endif /* md5_INCLUDED */ diff --git a/tools/urt/libs/md5lib/md5lib.c b/tools/urt/libs/md5lib/md5lib.c new file mode 100644 index 00000000..6fb45f13 --- /dev/null +++ b/tools/urt/libs/md5lib/md5lib.c @@ -0,0 +1,395 @@ +/* + Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/* $Id: md5lib.c,v 1.1 2003/07/18 04:24:39 ydnar Exp $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321, whose + text is available at + http://www.ietf.org/rfc/rfc1321.txt + The code is derived from the text of the RFC, including the test suite + (section A.5) but excluding the rest of Appendix A. It does not include + any code or documentation that is identified in the RFC as being + copyrighted. + + The original and principal author of md5.c is L. Peter Deutsch + . Other authors are noted in the change history + that follows (in reverse chronological order): + + 2003-07-17 ydnar added to gtkradiant project from + http://sourceforge.net/projects/libmd5-rfc/ + 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order + either statically or dynamically; added missing #include + in library. + 2002-03-11 lpd Corrected argument list for main(), and added int return + type, in test program and T value program. + 2002-02-21 lpd Added missing #include in test program. + 2000-07-03 lpd Patched to eliminate warnings about "constant is + unsigned in ANSI C, signed in traditional"; made test program + self-checking. + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). + 1999-05-03 lpd Original version. + */ + +#include "md5lib.h" /* ydnar */ +#include + +/* ydnar: gtkradiant endian picking */ +#ifdef _SGI_SOURCE +#define __BIG_ENDIAN__ +#endif + +#ifdef __BIG_ENDIAN__ +#define ARCH_IS_BIG_ENDIAN 1 +#else +#define ARCH_IS_BIG_ENDIAN 0 +#endif +/* ydnar: end */ + +#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ +#ifdef ARCH_IS_BIG_ENDIAN +# define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1) +#else +# define BYTE_ORDER 0 +#endif + +#define T_MASK ((md5_word_t)~0) +#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) +#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) +#define T3 0x242070db +#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) +#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) +#define T6 0x4787c62a +#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) +#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) +#define T9 0x698098d8 +#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) +#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) +#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) +#define T13 0x6b901122 +#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) +#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) +#define T16 0x49b40821 +#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) +#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) +#define T19 0x265e5a51 +#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) +#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) +#define T22 0x02441453 +#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) +#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) +#define T25 0x21e1cde6 +#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) +#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) +#define T28 0x455a14ed +#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) +#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) +#define T31 0x676f02d9 +#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) +#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) +#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) +#define T35 0x6d9d6122 +#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) +#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) +#define T38 0x4bdecfa9 +#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) +#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) +#define T41 0x289b7ec6 +#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) +#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) +#define T44 0x04881d05 +#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) +#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) +#define T47 0x1fa27cf8 +#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) +#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) +#define T50 0x432aff97 +#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) +#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) +#define T53 0x655b59c3 +#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) +#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) +#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) +#define T57 0x6fa87e4f +#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) +#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) +#define T60 0x4e0811a1 +#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) +#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) +#define T63 0x2ad7d2bb +#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) + + +static void +md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) +{ + md5_word_t + a = pms->abcd[0], b = pms->abcd[1], + c = pms->abcd[2], d = pms->abcd[3]; + md5_word_t t; +#if BYTE_ORDER > 0 + /* Define storage only for big-endian CPUs. */ + md5_word_t X[16]; +#else + /* Define storage for little-endian or both types of CPUs. */ + md5_word_t xbuf[16]; + const md5_word_t *X; +#endif + + { +#if BYTE_ORDER == 0 + /* + * Determine dynamically whether this is a big-endian or + * little-endian machine, since we can use a more efficient + * algorithm on the latter. + */ + static const int w = 1; + + if (*((const md5_byte_t *)&w)) /* dynamic little-endian */ +#endif +#if BYTE_ORDER <= 0 /* little-endian */ + { + /* + * On little-endian machines, we can process properly aligned + * data without copying it. + */ + if (!((data - (const md5_byte_t *)0) & 3)) { + /* data are properly aligned */ + X = (const md5_word_t *)data; + } else { + /* not aligned */ + memcpy(xbuf, data, 64); + X = xbuf; + } + } +#endif +#if BYTE_ORDER == 0 + else /* dynamic big-endian */ +#endif +#if BYTE_ORDER >= 0 /* big-endian */ + { + /* + * On big-endian machines, we must arrange the bytes in the + * right order. + */ + const md5_byte_t *xp = data; + int i; + +# if BYTE_ORDER == 0 + X = xbuf; /* (dynamic only) */ +# else +# define xbuf X /* (static only) */ +# endif + for (i = 0; i < 16; ++i, xp += 4) + xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); + } +#endif + } + +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + + /* Round 1. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ +#define F(x, y, z) (((x) & (y)) | (~(x) & (z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + F(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 7, T1); + SET(d, a, b, c, 1, 12, T2); + SET(c, d, a, b, 2, 17, T3); + SET(b, c, d, a, 3, 22, T4); + SET(a, b, c, d, 4, 7, T5); + SET(d, a, b, c, 5, 12, T6); + SET(c, d, a, b, 6, 17, T7); + SET(b, c, d, a, 7, 22, T8); + SET(a, b, c, d, 8, 7, T9); + SET(d, a, b, c, 9, 12, T10); + SET(c, d, a, b, 10, 17, T11); + SET(b, c, d, a, 11, 22, T12); + SET(a, b, c, d, 12, 7, T13); + SET(d, a, b, c, 13, 12, T14); + SET(c, d, a, b, 14, 17, T15); + SET(b, c, d, a, 15, 22, T16); +#undef SET + + /* Round 2. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ +#define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + G(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 1, 5, T17); + SET(d, a, b, c, 6, 9, T18); + SET(c, d, a, b, 11, 14, T19); + SET(b, c, d, a, 0, 20, T20); + SET(a, b, c, d, 5, 5, T21); + SET(d, a, b, c, 10, 9, T22); + SET(c, d, a, b, 15, 14, T23); + SET(b, c, d, a, 4, 20, T24); + SET(a, b, c, d, 9, 5, T25); + SET(d, a, b, c, 14, 9, T26); + SET(c, d, a, b, 3, 14, T27); + SET(b, c, d, a, 8, 20, T28); + SET(a, b, c, d, 13, 5, T29); + SET(d, a, b, c, 2, 9, T30); + SET(c, d, a, b, 7, 14, T31); + SET(b, c, d, a, 12, 20, T32); +#undef SET + + /* Round 3. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + H(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 5, 4, T33); + SET(d, a, b, c, 8, 11, T34); + SET(c, d, a, b, 11, 16, T35); + SET(b, c, d, a, 14, 23, T36); + SET(a, b, c, d, 1, 4, T37); + SET(d, a, b, c, 4, 11, T38); + SET(c, d, a, b, 7, 16, T39); + SET(b, c, d, a, 10, 23, T40); + SET(a, b, c, d, 13, 4, T41); + SET(d, a, b, c, 0, 11, T42); + SET(c, d, a, b, 3, 16, T43); + SET(b, c, d, a, 6, 23, T44); + SET(a, b, c, d, 9, 4, T45); + SET(d, a, b, c, 12, 11, T46); + SET(c, d, a, b, 15, 16, T47); + SET(b, c, d, a, 2, 23, T48); +#undef SET + + /* Round 4. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ +#define I(x, y, z) ((y) ^ ((x) | ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + I(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 6, T49); + SET(d, a, b, c, 7, 10, T50); + SET(c, d, a, b, 14, 15, T51); + SET(b, c, d, a, 5, 21, T52); + SET(a, b, c, d, 12, 6, T53); + SET(d, a, b, c, 3, 10, T54); + SET(c, d, a, b, 10, 15, T55); + SET(b, c, d, a, 1, 21, T56); + SET(a, b, c, d, 8, 6, T57); + SET(d, a, b, c, 15, 10, T58); + SET(c, d, a, b, 6, 15, T59); + SET(b, c, d, a, 13, 21, T60); + SET(a, b, c, d, 4, 6, T61); + SET(d, a, b, c, 11, 10, T62); + SET(c, d, a, b, 2, 15, T63); + SET(b, c, d, a, 9, 21, T64); +#undef SET + + /* Then perform the following additions. (That is increment each + of the four registers by the value it had before this block + was started.) */ + pms->abcd[0] += a; + pms->abcd[1] += b; + pms->abcd[2] += c; + pms->abcd[3] += d; +} + +void +md5_init(md5_state_t *pms) +{ + pms->count[0] = pms->count[1] = 0; + pms->abcd[0] = 0x67452301; + pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; + pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; + pms->abcd[3] = 0x10325476; +} + +void +md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) +{ + const md5_byte_t *p = data; + int left = nbytes; + int offset = (pms->count[0] >> 3) & 63; + md5_word_t nbits = (md5_word_t)(nbytes << 3); + + if (nbytes <= 0) + return; + + /* Update the message length. */ + pms->count[1] += nbytes >> 29; + pms->count[0] += nbits; + if (pms->count[0] < nbits) + pms->count[1]++; + + /* Process an initial partial block. */ + if (offset) { + int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); + + memcpy(pms->buf + offset, p, copy); + if (offset + copy < 64) + return; + p += copy; + left -= copy; + md5_process(pms, pms->buf); + } + + /* Process full blocks. */ + for (; left >= 64; p += 64, left -= 64) + md5_process(pms, p); + + /* Process a final partial block. */ + if (left) + memcpy(pms->buf, p, left); +} + +void +md5_finish(md5_state_t *pms, md5_byte_t digest[16]) +{ + static const md5_byte_t pad[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + md5_byte_t data[8]; + int i; + + /* Save the length before padding. */ + for (i = 0; i < 8; ++i) + data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); + /* Pad to 56 bytes mod 64. */ + md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); + /* Append the length. */ + md5_append(pms, data, 8); + for (i = 0; i < 16; ++i) + digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); +} diff --git a/tools/urt/libs/md5lib/md5lib.dsp b/tools/urt/libs/md5lib/md5lib.dsp new file mode 100644 index 00000000..81addbbc --- /dev/null +++ b/tools/urt/libs/md5lib/md5lib.dsp @@ -0,0 +1,106 @@ +# Microsoft Developer Studio Project File - Name="md5lib" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=md5lib - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "md5lib.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "md5lib.mak" CFG="md5lib - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "md5lib - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "md5lib - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "Perforce Project" +# PROP Scc_LocalPath ".." +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "md5lib - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +MTL=midl.exe +F90=df.exe +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /O2 /I ".." /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FR /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "md5lib - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +MTL=midl.exe +F90=df.exe +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /ZI /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FR /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "md5lib - Win32 Release" +# Name "md5lib - Win32 Debug" +# Begin Group "src" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\md5lib.c +# End Source File +# End Group +# Begin Group "include" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\md5lib.h +# End Source File +# End Group +# End Target +# End Project diff --git a/tools/urt/libs/md5lib/md5lib.vcproj b/tools/urt/libs/md5lib/md5lib.vcproj new file mode 100644 index 00000000..08eb232e --- /dev/null +++ b/tools/urt/libs/md5lib/md5lib.vcproj @@ -0,0 +1,211 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/urt/libs/memory/allocator.cpp b/tools/urt/libs/memory/allocator.cpp new file mode 100644 index 00000000..2f213c7e --- /dev/null +++ b/tools/urt/libs/memory/allocator.cpp @@ -0,0 +1,60 @@ + +#include "memory/allocator.h" + +#include + +template +struct Vector +{ + typedef std::vector > Type; +}; + +namespace +{ + class Bleh + { + int* m_blah; + public: + Bleh(int* blah) : m_blah(blah) + { + } + ~Bleh() + { + *m_blah = 15; + } + }; + + void TestAllocator() + { + Vector::Type test; + + int i = 0; + test.push_back(Bleh(&i)); + } + + void TestNewDelete() + { + { + NamedAllocator allocator("test"); + int* p = NamedNew::type(allocator).scalar(); + //new int(); + NamedDelete::type(allocator).scalar(p); + } + + { + int* p = New().scalar(3); + Delete().scalar(p); + } + + { + int* p = New().scalar(int(15.9)); + Delete().scalar(p); + } + + { + int* p = New().vector(15); + // new int[15] + Delete().vector(p, 15); + } + } +} \ No newline at end of file diff --git a/tools/urt/libs/memory/allocator.h b/tools/urt/libs/memory/allocator.h new file mode 100644 index 00000000..b7689b81 --- /dev/null +++ b/tools/urt/libs/memory/allocator.h @@ -0,0 +1,316 @@ + +#if !defined(INCLUDED_MEMORY_ALLOCATOR_H) +#define INCLUDED_MEMORY_ALLOCATOR_H + +#include + +#if 0 + +#define DefaultAllocator std::allocator + +#else + +/// \brief An allocator that uses c++ new/delete. +/// Compliant with the std::allocator interface. +template +class DefaultAllocator +{ +public: + + typedef Type value_type; + typedef value_type* pointer; + typedef const Type* const_pointer; + typedef Type& reference; + typedef const Type& const_reference; + typedef size_t size_type; + typedef ptrdiff_t difference_type; + + template + struct rebind + { + typedef DefaultAllocator other; + }; + + DefaultAllocator() + { + } + DefaultAllocator(const DefaultAllocator&) + { + } + template DefaultAllocator(const DefaultAllocator&) + { + } + ~DefaultAllocator() + { + } + + pointer address(reference instance) const + { + return &instance; + } + const_pointer address(const_reference instance) const + { + return &instance; + } + Type* allocate(size_type size, const void* = 0) + { + return static_cast(::operator new(size * sizeof(Type))); + } + void deallocate(pointer p, size_type) + { + ::operator delete(p); + } + size_type max_size() const + { + return std::size_t(-1) / sizeof (Type); + } + void construct(pointer p, const Type& value) + { + new(p) Type(value); + } + void destroy(pointer p) + { + p->~Type(); + } +}; + +template +inline bool operator==(const DefaultAllocator&, const DefaultAllocator&) +{ + return true; +} +template +inline bool operator==(const DefaultAllocator&, const OtherAllocator&) +{ + return false; +} + +#endif + + +template +class NamedAllocator : public DefaultAllocator +{ + typedef DefaultAllocator allocator_type; + + const char* m_name; +public: + + typedef Type value_type; + typedef value_type* pointer; + typedef const Type* const_pointer; + typedef Type& reference; + typedef const Type& const_reference; + typedef size_t size_type; + typedef ptrdiff_t difference_type; + + template + struct rebind + { + typedef NamedAllocator other; + }; + + explicit NamedAllocator(const char* name) : m_name(name) + { + } + NamedAllocator(const NamedAllocator& other) : m_name(other.m_name) + { + } + template NamedAllocator(const NamedAllocator& other) : m_name(other.m_name) + { + } + ~NamedAllocator() + { + } + + pointer address(reference instance) const + { + return allocator_type::address(instance); + } + const_pointer address(const_reference instance) const + { + return allocator_type::address(instance); + } + Type* allocate(size_type size, const void* = 0) + { + return allocator_type::allocate(size); + } + void deallocate(pointer p, size_type size) + { + allocator_type::deallocate(p, size); + } + size_type max_size() const + { + return allocator_type::max_size(); + } + void construct(pointer p, const Type& value) + { + allocator_type::construct(p, value); + } + void destroy(pointer p) + { + allocator_type::destroy(p); + } + + template + bool operator==(const NamedAllocator& other) + { + return true; + } + + // returns true if the allocators are not interchangeable + template + bool operator!=(const NamedAllocator& other) + { + return false; + } +}; + + + +#include +#include "generic/object.h" + + + +template +class DefaultConstruct +{ +public: + void operator()(Type& t) + { + constructor(t); + } +}; + +template +class Construct +{ + const T1& other; +public: + Construct(const T1& other_) : other(other_) + { + } + void operator()(Type& t) + { + constructor(t, other); + } +}; + +template +class Destroy +{ +public: + void operator()(Type& t) + { + destructor(t); + } +}; + +template > +class New : public Allocator +{ +public: + New() + { + } + explicit New(const Allocator& allocator) : Allocator(allocator) + { + } + + Type* scalar() + { + return new(Allocator::allocate(1)) Type(); + } + template + Type* scalar(const T1& t1) + { + return new(Allocator::allocate(1)) Type(t1); + } + template + Type* scalar(const T1& t1, const T2& t2) + { + return new(Allocator::allocate(1)) Type(t1, t2); + } + template + Type* scalar(const T1& t1, const T2& t2, const T3& t3) + { + return new(Allocator::allocate(1)) Type(t1, t2, t3); + } + template + Type* scalar(const T1& t1, const T2& t2, const T3& t3, const T4& t4) + { + return new(Allocator::allocate(1)) Type(t1, t2, t3, t4); + } + template + Type* scalar(const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5) + { + return new(Allocator::allocate(1)) Type(t1, t2, t3, t4, t5); + } + Type* vector(std::size_t size) + { +#if 1 + Type* p = Allocator::allocate(size); + std::for_each(p, p + size, DefaultConstruct()); + return p; +#else + // this does not work with msvc71 runtime + return new(Allocator::allocate(size)) Type[size]; +#endif + } + template + Type* vector(std::size_t size, const T1& t1) + { + Type* p = Allocator::allocate(size); + std::for_each(p, p + size, Construct(t1)); + return p; + } +}; + +template > +class Delete : public Allocator +{ +public: + Delete() + { + } + explicit Delete(const Allocator& allocator) : Allocator(allocator) + { + } + + void scalar(Type* p) + { + if(p != 0) + { + p->~Type(); + Allocator::deallocate(p, 1); + } + } + void vector(Type* p, std::size_t size) + { + // '::operator delete' handles null + // 'std::allocator::deallocate' requires non-null + if(p != 0) + { + std::for_each(p, p + size, Destroy()); + Allocator::deallocate(p, size); + } + } +}; + + +template +class NamedNew +{ +public: + typedef New > type; +}; + +template +class NamedDelete +{ +public: + typedef Delete > type; +}; + +#endif diff --git a/tools/urt/libs/moduleobservers.cpp b/tools/urt/libs/moduleobservers.cpp new file mode 100644 index 00000000..adb54303 --- /dev/null +++ b/tools/urt/libs/moduleobservers.cpp @@ -0,0 +1,3 @@ + +#include "moduleobservers.h" + diff --git a/tools/urt/libs/moduleobservers.h b/tools/urt/libs/moduleobservers.h new file mode 100644 index 00000000..71c53dfd --- /dev/null +++ b/tools/urt/libs/moduleobservers.h @@ -0,0 +1,44 @@ + +#if !defined(INCLUDED_MODULEOBSERVERS_H) +#define INCLUDED_MODULEOBSERVERS_H + +#include "debugging/debugging.h" +#include +#include "moduleobserver.h" + +class ModuleObservers +{ + typedef std::set Observers; + Observers m_observers; +public: + ~ModuleObservers() + { + ASSERT_MESSAGE(m_observers.empty(), "ModuleObservers::~ModuleObservers: observers still attached"); + } + void attach(ModuleObserver& observer) + { + ASSERT_MESSAGE(m_observers.find(&observer) == m_observers.end(), "ModuleObservers::attach: cannot attach observer"); + m_observers.insert(&observer); + } + void detach(ModuleObserver& observer) + { + ASSERT_MESSAGE(m_observers.find(&observer) != m_observers.end(), "ModuleObservers::detach: cannot detach observer"); + m_observers.erase(&observer); + } + void realise() + { + for(Observers::iterator i = m_observers.begin(); i != m_observers.end(); ++i) + { + (*i)->realise(); + } + } + void unrealise() + { + for(Observers::reverse_iterator i = m_observers.rbegin(); i != m_observers.rend(); ++i) + { + (*i)->unrealise(); + } + } +}; + +#endif diff --git a/tools/urt/libs/modulesystem/moduleregistry.cpp b/tools/urt/libs/modulesystem/moduleregistry.cpp new file mode 100644 index 00000000..6aa54ce8 --- /dev/null +++ b/tools/urt/libs/modulesystem/moduleregistry.cpp @@ -0,0 +1,3 @@ + +#include "moduleregistry.h" + diff --git a/tools/urt/libs/modulesystem/moduleregistry.h b/tools/urt/libs/modulesystem/moduleregistry.h new file mode 100644 index 00000000..bbd5544e --- /dev/null +++ b/tools/urt/libs/modulesystem/moduleregistry.h @@ -0,0 +1,45 @@ + +#if !defined(INCLUDED_MODULESYSTEM_MODULEREGISTRY_H) +#define INCLUDED_MODULESYSTEM_MODULEREGISTRY_H + +#include "generic/static.h" +#include + +class ModuleRegisterable +{ +public: + virtual void selfRegister() = 0; +}; + +class ModuleRegistryList +{ + typedef std::list RegisterableModules; + RegisterableModules m_modules; +public: + void addModule(ModuleRegisterable& module) + { + m_modules.push_back(&module); + } + void registerModules() const + { + for(RegisterableModules::const_iterator i = m_modules.begin(); i != m_modules.end(); ++i) + { + (*i)->selfRegister(); + } + } +}; + +typedef SmartStatic StaticModuleRegistryList; + + +class StaticRegisterModule : public StaticModuleRegistryList +{ +public: + StaticRegisterModule(ModuleRegisterable& module) + { + StaticModuleRegistryList::instance().addModule(module); + } +}; + + +#endif diff --git a/tools/urt/libs/modulesystem/modulesmap.cpp b/tools/urt/libs/modulesystem/modulesmap.cpp new file mode 100644 index 00000000..659eec05 --- /dev/null +++ b/tools/urt/libs/modulesystem/modulesmap.cpp @@ -0,0 +1,2 @@ + +#include "modulesmap.h" diff --git a/tools/urt/libs/modulesystem/modulesmap.h b/tools/urt/libs/modulesystem/modulesmap.h new file mode 100644 index 00000000..2b69c090 --- /dev/null +++ b/tools/urt/libs/modulesystem/modulesmap.h @@ -0,0 +1,132 @@ + +#if !defined(INCLUDED_MODULESYSTEM_MODULESMAP_H) +#define INCLUDED_MODULESYSTEM_MODULESMAP_H + +#include "modulesystem.h" +#include "string/string.h" +#include +#include + +template +class ModulesMap : public Modules +{ + typedef std::map modules_t; + modules_t m_modules; +public: + ~ModulesMap() + { + for(modules_t::iterator i = m_modules.begin(); i != m_modules.end(); ++i) + { + (*i).second->release(); + } + } + + typedef modules_t::const_iterator iterator; + + iterator begin() const + { + return m_modules.begin(); + } + iterator end() const + { + return m_modules.end(); + } + + void insert(const char* name, Module& module) + { + module.capture(); + if(globalModuleServer().getError()) + { + module.release(); + globalModuleServer().setError(false); + } + else + { + m_modules.insert(modules_t::value_type(name, &module)); + } + } + + Type* find(const char* name) + { + modules_t::iterator i = m_modules.find(name); + if(i != m_modules.end()) + { + return static_cast(Module_getTable(*(*i).second)); + } + return 0; + } + + Type* findModule(const char* name) + { + return find(name); + } + void foreachModule(typename Modules::Visitor& visitor) + { + for(modules_t::iterator i = m_modules.begin(); i != m_modules.end(); ++i) + { + visitor.visit((*i).first.c_str(), *static_cast(Module_getTable(*(*i).second))); + } + } +}; + +template +class InsertModules : public ModuleServer::Visitor +{ + ModulesMap& m_modules; +public: + InsertModules(ModulesMap& modules) + : m_modules(modules) + { + } + void visit(const char* name, Module& module) + { + m_modules.insert(name, module); + } +}; + +template +class ModulesRef +{ + ModulesMap m_modules; +public: + ModulesRef(const char* names) + { + if(!globalModuleServer().getError()) + { + if(string_equal(names, "*")) + { + InsertModules visitor(m_modules); + globalModuleServer().foreachModule(Type::name(), Type::VERSION, visitor); + } + else + { + StringTokeniser tokeniser(names); + for(;;) + { + const char* name = tokeniser.getToken(); + if(string_empty(name)) + { + break; + } + Module* module = globalModuleServer().findModule(Type::name(), Type::VERSION, name); + if(module == 0) + { + globalModuleServer().setError(true); + globalErrorStream() << "ModulesRef::initialise: type=" << makeQuoted(Type::name()) << " version= " << makeQuoted(int(Type::VERSION)) << " name=" << makeQuoted(name) << " - not found\n"; + break; + } + else + { + m_modules.insert(name, *module); + } + } + } + } + } + ModulesMap& get() + { + return m_modules; + } +}; + +#endif diff --git a/tools/urt/libs/modulesystem/singletonmodule.cpp b/tools/urt/libs/modulesystem/singletonmodule.cpp new file mode 100644 index 00000000..9d6a6823 --- /dev/null +++ b/tools/urt/libs/modulesystem/singletonmodule.cpp @@ -0,0 +1,33 @@ + +#include "singletonmodule.h" + +class NullType +{ +public: + enum { VERSION = 1 }; + static const char* name() + { + return ""; + } +}; + +class NullModule +{ +public: + typedef NullType Type; + static const char* getName() + { + return ""; + } + void* getTable() + { + return NULL; + } +}; + +void TEST_SINGLETONMODULE() +{ + SingletonModule null; + null.capture(); + null.release(); +} diff --git a/tools/urt/libs/modulesystem/singletonmodule.h b/tools/urt/libs/modulesystem/singletonmodule.h new file mode 100644 index 00000000..d60a1679 --- /dev/null +++ b/tools/urt/libs/modulesystem/singletonmodule.h @@ -0,0 +1,130 @@ + +#if !defined(INCLUDED_MODULESYSTEM_SINGLETONMODULE_H) +#define INCLUDED_MODULESYSTEM_SINGLETONMODULE_H + +#include "modulesystem.h" +#include +#include "debugging/debugging.h" +#include "modulesystem/moduleregistry.h" +#include "generic/reference.h" + +template +class DefaultAPIConstructor +{ +public: + const char* getName() + { + return API::getName(); + } + + API* constructAPI(Dependencies& dependencies) + { + return new API; + } + void destroyAPI(API* api) + { + delete api; + } +}; + +template +class DependenciesAPIConstructor +{ +public: + const char* getName() + { + return API::getName(); + } + + API* constructAPI(Dependencies& dependencies) + { + return new API(dependencies); + } + void destroyAPI(API* api) + { + delete api; + } +}; + +class NullDependencies +{ +}; + + +template > +class SingletonModule : public APIConstructor, public Module, public ModuleRegisterable +{ + Dependencies* m_dependencies; + API* m_api; + std::size_t m_refcount; + bool m_dependencyCheck; + bool m_cycleCheck; +public: + typedef typename API::Type Type; + + SingletonModule() + : m_dependencies(0), m_api(0), m_refcount(0), m_dependencyCheck(false), m_cycleCheck(false) + { + } + explicit SingletonModule(const APIConstructor& constructor) + : APIConstructor(constructor), m_dependencies(0), m_api(0), m_refcount(0), m_dependencyCheck(false), m_cycleCheck(false) + { + } + ~SingletonModule() + { + ASSERT_MESSAGE(m_refcount == 0, "module still referenced at shutdown"); + } + + void selfRegister() + { + globalModuleServer().registerModule(Type::name(), Type::VERSION, APIConstructor::getName(), *this); + } + + Dependencies& getDependencies() + { + return *m_dependencies; + } + void* getTable() + { + if(m_api != 0) + { + return m_api->getTable(); + } + return 0; + } + void capture() + { + if(++m_refcount == 1) + { + globalOutputStream() << "Module Initialising: '" << Type::name() << "' '" << APIConstructor::getName() << "'\n"; + m_dependencies = new Dependencies(); + m_dependencyCheck = !globalModuleServer().getError(); + if(m_dependencyCheck) + { + m_api = APIConstructor::constructAPI(*m_dependencies); + globalOutputStream() << "Module Ready: '" << Type::name() << "' '" << APIConstructor::getName() << "'\n"; + } + else + { + globalOutputStream() << "Module Dependencies Failed: '" << Type::name() << "' '" << APIConstructor::getName() << "'\n"; + } + m_cycleCheck = true; + } + + ASSERT_MESSAGE(m_cycleCheck, "cyclic dependency detected"); + } + void release() + { + if(--m_refcount == 0) + { + if(m_dependencyCheck) + { + APIConstructor::destroyAPI(m_api); + } + delete m_dependencies; + } + } +}; + + +#endif diff --git a/tools/urt/libs/os/dir.cpp b/tools/urt/libs/os/dir.cpp new file mode 100644 index 00000000..f107d9bb --- /dev/null +++ b/tools/urt/libs/os/dir.cpp @@ -0,0 +1,3 @@ + +#include "dir.h" + diff --git a/tools/urt/libs/os/dir.h b/tools/urt/libs/os/dir.h new file mode 100644 index 00000000..ffdf01d2 --- /dev/null +++ b/tools/urt/libs/os/dir.h @@ -0,0 +1,55 @@ + +#if !defined(INCLUDED_OS_DIR_H) +#define INCLUDED_OS_DIR_H + +/// \file +/// \brief OS directory-listing object. + +#include + +typedef GDir Directory; + +inline bool directory_good(Directory* directory) +{ + return directory != 0; +} + +inline Directory* directory_open(const char* name) +{ + return g_dir_open(name, 0, 0); +} + +inline void directory_close(Directory* directory) +{ + g_dir_close(directory); +} + +inline const char* directory_read_and_increment(Directory* directory) +{ + return g_dir_read_name(directory); +} + +template +void Directory_forEach(const char* path, const Functor& functor) +{ + Directory* dir = directory_open(path); + + if(directory_good(dir)) + { + for(;;) + { + const char* name = directory_read_and_increment(dir); + if(name == 0) + { + break; + } + + functor(name); + } + + directory_close(dir); + } +} + + +#endif diff --git a/tools/urt/libs/os/file.cpp b/tools/urt/libs/os/file.cpp new file mode 100644 index 00000000..2220905c --- /dev/null +++ b/tools/urt/libs/os/file.cpp @@ -0,0 +1,3 @@ + +#include "file.h" + diff --git a/tools/urt/libs/os/file.h b/tools/urt/libs/os/file.h new file mode 100644 index 00000000..94f9975e --- /dev/null +++ b/tools/urt/libs/os/file.h @@ -0,0 +1,123 @@ + +#if !defined(INCLUDED_OS_FILE_H) +#define INCLUDED_OS_FILE_H + +/// \file +/// \brief OS file-system querying and manipulation. + +#if defined( WIN32 ) +#define S_ISDIR(mode) (mode & _S_IFDIR) +#include // access() +#define F_OK 0x00 +#define W_OK 0x02 +#define R_OK 0x04 +#else +#include // access() +#endif + +#include // rename(), remove() +#include // stat() +#include // this is included by stat.h on win32 +#include +#include + +/// \brief Attempts to move the file identified by \p from to \p to and returns true if the operation was successful. +/// +/// The operation will fail unless: +/// - The path \p from identifies an existing file which is accessible for writing. +/// - The directory component of \p from identifies an existing directory which is accessible for writing. +/// - The path \p to does not identify an existing file or directory. +/// - The directory component of \p to identifies an existing directory which is accessible for writing. +inline bool file_move(const char* from, const char* to) +{ + return rename(from, to) == 0; +} + +/// \brief Attempts to remove the file identified by \p path and returns true if the operation was successful. +/// +/// The operation will fail unless: +/// - The \p path identifies an existing file. +/// - The parent-directory component of \p path identifies an existing directory which is accessible for writing. +inline bool file_remove(const char* path) +{ + return remove(path) == 0; +} + +namespace FileAccess +{ + enum Mode + { + Read = R_OK, + Write = W_OK, + ReadWrite = Read | Write, + Exists = F_OK + }; +} + +/// \brief Returns true if the file or directory identified by \p path exists and/or may be accessed for reading, writing or both, depending on the value of \p mode. +inline bool file_accessible(const char* path, FileAccess::Mode mode) +{ + return access(path, static_cast(mode)) == 0; +} + +/// \brief Returns true if the file or directory identified by \p path exists and may be opened for reading. +inline bool file_readable(const char* path) +{ + return file_accessible(path, FileAccess::Read); +} + +/// \brief Returns true if the file or directory identified by \p path exists and may be opened for writing. +inline bool file_writeable(const char* path) +{ + return file_accessible(path, FileAccess::Write); +} + +/// \brief Returns true if the file or directory identified by \p path exists. +inline bool file_exists(const char* path) +{ + return file_accessible(path, FileAccess::Exists); +} + +/// \brief Returns true if the file or directory identified by \p path exists and is a directory. +inline bool file_is_directory(const char* path) +{ + struct stat st; + if(stat(path, &st) == -1) + { + return false; + } + return S_ISDIR (st.st_mode) != 0; +} + +typedef std::size_t FileSize; + +/// \brief Returns the size in bytes of the file identified by \p path, or 0 if the file was not found. +inline FileSize file_size(const char* path) +{ + struct stat st; + if(stat(path, &st) == -1) + { + return 0; + } + return st.st_size; +} + +/// Seconds elapsed since Jan 1, 1970 +typedef std::time_t FileTime; +/// No file can have been modified earlier than this time. +const FileTime c_invalidFileTime = -1; + +/// \brief Returns the time that the file identified by \p path was last modified, or c_invalidFileTime if the file was not found. +inline FileTime file_modified(const char* path) +{ + struct stat st; + if(stat(path, &st) == -1) + { + return c_invalidFileTime; + } + return st.st_mtime; +} + + + +#endif diff --git a/tools/urt/libs/os/path.cpp b/tools/urt/libs/os/path.cpp new file mode 100644 index 00000000..5abd021a --- /dev/null +++ b/tools/urt/libs/os/path.cpp @@ -0,0 +1,2 @@ + +#include "path.h" diff --git a/tools/urt/libs/os/path.h b/tools/urt/libs/os/path.h new file mode 100644 index 00000000..0ccbf096 --- /dev/null +++ b/tools/urt/libs/os/path.h @@ -0,0 +1,263 @@ + +#if !defined (INCLUDED_OS_PATH_H) +#define INCLUDED_OS_PATH_H + +/// \file +/// \brief OS file-system path comparison, decomposition and manipulation. +/// +/// - Paths are c-style null-terminated-character-arrays. +/// - Path separators must be forward slashes (unix style). +/// - Directory paths must end in a separator. +/// - Paths must not contain the ascii characters \\ : * ? " < > or |. +/// - Paths may be encoded in UTF-8 or any extended-ascii character set. + +#include "string/string.h" + +#if defined(WIN32) +#define OS_CASE_INSENSITIVE +#endif + +/// \brief Returns true if \p path is lexicographically sorted before \p other. +/// If both \p path and \p other refer to the same file, neither will be sorted before the other. +/// O(n) +inline bool path_less(const char* path, const char* other) +{ +#if defined(OS_CASE_INSENSITIVE) + return string_less_nocase(path, other); +#else + return string_less(path, other); +#endif +} + +/// \brief Returns <0 if \p path is lexicographically less than \p other. +/// Returns >0 if \p path is lexicographically greater than \p other. +/// Returns 0 if both \p path and \p other refer to the same file. +/// O(n) +inline int path_compare(const char* path, const char* other) +{ +#if defined(OS_CASE_INSENSITIVE) + return string_compare_nocase(path, other); +#else + return string_compare(path, other); +#endif +} + +/// \brief Returns true if \p path and \p other refer to the same file or directory. +/// O(n) +inline bool path_equal(const char* path, const char* other) +{ +#if defined(OS_CASE_INSENSITIVE) + return string_equal_nocase(path, other); +#else + return string_equal(path, other); +#endif +} + +/// \brief Returns true if the first \p n bytes of \p path and \p other form paths that refer to the same file or directory. +/// If the paths are UTF-8 encoded, [\p path, \p path + \p n) must be a complete path. +/// O(n) +inline bool path_equal_n(const char* path, const char* other, std::size_t n) +{ +#if defined(OS_CASE_INSENSITIVE) + return string_equal_nocase_n(path, other, n); +#else + return string_equal_n(path, other, n); +#endif +} + + +/// \brief Returns true if \p path is a fully qualified file-system path. +/// O(1) +inline bool path_is_absolute(const char* path) +{ +#if defined(WIN32) + return path[0] == '/' + || (path[0] != '\0' && path[1] == ':'); // local drive +#elif defined(__linux__) || defined(__APPLE__) + return path[0] == '/'; +#endif +} + +/// \brief Returns true if \p path is a directory. +/// O(n) +inline bool path_is_directory(const char* path) +{ + std::size_t length = strlen(path); + if(length > 0) + { + return path[length-1] == '/'; + } + return false; +} + +/// \brief Returns a pointer to the first character of the component of \p path following the first directory component. +/// O(n) +inline const char* path_remove_directory(const char* path) +{ + const char* first_separator = strchr(path, '/'); + if(first_separator != 0) + { + return ++first_separator; + } + return ""; +} + +/// \brief Returns a pointer to the first character of the filename component of \p path. +/// O(n) +inline const char* path_get_filename_start(const char* path) +{ + { + const char* last_forward_slash = strrchr(path, '/'); + if(last_forward_slash != 0) + { + return last_forward_slash + 1; + } + } + + // not strictly necessary,since paths should not contain '\' + { + const char* last_backward_slash = strrchr(path, '\\'); + if(last_backward_slash != 0) + { + return last_backward_slash + 1; + } + } + + return path; +} + +/// \brief Returns a pointer to the character after the end of the filename component of \p path - either the extension separator or the terminating null character. +/// O(n) +inline const char* path_get_filename_base_end(const char* path) +{ + const char* last_period = strrchr(path_get_filename_start(path), '.'); + return (last_period != 0) ? last_period : path + string_length(path); +} + +/// \brief Returns the length of the filename component (not including extension) of \p path. +/// O(n) +inline std::size_t path_get_filename_base_length(const char* path) +{ + return path_get_filename_base_end(path) - path; +} + +/// \brief If \p path is a child of \p base, returns the subpath relative to \p base, else returns \p path. +/// O(n) +inline const char* path_make_relative(const char* path, const char* base) +{ + const std::size_t length = string_length(base); + if(path_equal_n(path, base, length)) + { + return path + length; + } + return path; +} + +/// \brief Returns a pointer to the first character of the file extension of \p path, or "" if not found. +/// O(n) +inline const char* path_get_extension(const char* path) +{ + const char* last_period = strrchr(path_get_filename_start(path), '.'); + if(last_period != 0) + { + return ++last_period; + } + return ""; +} + +/// \brief Returns true if \p extension is of the same type as \p other. +/// O(n) +inline bool extension_equal(const char* extension, const char* other) +{ + return path_equal(extension, other); +} + +template +class MatchFileExtension +{ + const char* m_extension; + const Functor& m_functor; +public: + MatchFileExtension(const char* extension, const Functor& functor) : m_extension(extension), m_functor(functor) + { + } + void operator()(const char* name) const + { + const char* extension = path_get_extension(name); + if(extension_equal(extension, m_extension)) + { + m_functor(name); + } + } +}; + +/// \brief A functor which invokes its contained \p functor if the \p name argument matches its \p extension. +template +inline MatchFileExtension matchFileExtension(const char* extension, const Functor& functor) +{ + return MatchFileExtension(extension, functor); +} + +class PathCleaned +{ +public: + const char* m_path; + PathCleaned(const char* path) : m_path(path) + { + } +}; + +/// \brief Writes \p path to \p ostream with dos-style separators replaced by unix-style separators. +template +TextOutputStreamType& ostream_write(TextOutputStreamType& ostream, const PathCleaned& path) +{ + const char* i = path.m_path; + for(; *i != '\0'; ++i) + { + if(*i == '\\') + { + ostream << '/'; + } + else + { + ostream << *i; + } + } + return ostream; +} + +class DirectoryCleaned +{ +public: + const char* m_path; + DirectoryCleaned(const char* path) : m_path(path) + { + } +}; + +/// \brief Writes \p path to \p ostream with dos-style separators replaced by unix-style separators, and appends a separator if necessary. +template +TextOutputStreamType& ostream_write(TextOutputStreamType& ostream, const DirectoryCleaned& path) +{ + const char* i = path.m_path; + for(; *i != '\0'; ++i) + { + if(*i == '\\') + { + ostream << '/'; + } + else + { + ostream << *i; + } + } + char c = *(i - 1); + if(c != '/' && c != '\\') + { + ostream << '/'; + } + return ostream; +} + + +#endif diff --git a/tools/urt/libs/picomodel.h b/tools/urt/libs/picomodel.h new file mode 100644 index 00000000..dbb38a93 --- /dev/null +++ b/tools/urt/libs/picomodel.h @@ -0,0 +1,353 @@ +/* ----------------------------------------------------------------------------- + +PicoModel Library + +Copyright (c) 2002, Randy Reddig & seaw0lf +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list +of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +Neither the names of the copyright holders nor the names of its contributors may +be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +----------------------------------------------------------------------------- */ + + + +/* marker */ +#ifndef PICOMODEL_H +#define PICOMODEL_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + + + +/* version */ +#define PICOMODEL_VERSION "0.8.20" + + +/* constants */ +#define PICO_GROW_SHADERS 16 +#define PICO_GROW_SURFACES 16 +#define PICO_GROW_VERTEXES 1024 +#define PICO_GROW_INDEXES 1024 +#define PICO_GROW_ARRAYS 8 +#define PICO_GROW_FACES 256 +#define PICO_MAX_SPECIAL 8 +#define PICO_MAX_DEFAULT_EXTS 4 /* max default extensions per module */ + + +/* types */ +typedef unsigned char picoByte_t; +typedef float picoVec_t; +typedef float picoVec2_t[ 2 ]; +typedef float picoVec3_t[ 3 ]; +typedef float picoVec4_t[ 4 ]; +typedef picoByte_t picoColor_t[ 4 ]; +typedef int picoIndex_t; + +typedef enum +{ + PICO_BAD, + PICO_TRIANGLES, + PICO_PATCH +} +picoSurfaceType_t; + +typedef enum +{ + PICO_NORMAL, + PICO_VERBOSE, + PICO_WARNING, + PICO_ERROR, + PICO_FATAL +} +picoPrintLevel_t; + +typedef struct picoSurface_s picoSurface_t; +typedef struct picoShader_s picoShader_t; +typedef struct picoModel_s picoModel_t; +typedef struct picoModule_s picoModule_t; + +struct picoSurface_s +{ + void *data; + + picoModel_t *model; /* owner model */ + + picoSurfaceType_t type; + char *name; /* sea: surface name */ + picoShader_t *shader; /* ydnar: changed to ptr */ + + int numVertexes, maxVertexes; + picoVec3_t *xyz; + picoVec3_t *normal; + picoIndex_t *smoothingGroup; + + int numSTArrays, maxSTArrays; + picoVec2_t **st; + + int numColorArrays, maxColorArrays; + picoColor_t **color; + + int numIndexes, maxIndexes; + picoIndex_t *index; + + int numFaceNormals, maxFaceNormals; + picoVec3_t *faceNormal; + + int special[ PICO_MAX_SPECIAL ]; + + int submodel; //sub models are tracked +}; + + +/* seaw0lf */ +struct picoShader_s +{ + picoModel_t *model; /* owner model */ + + char *name; /* shader name */ + char *mapName; /* shader file name (name of diffuse texturemap) */ + picoColor_t ambientColor; /* ambient color of mesh (rgba) */ + picoColor_t diffuseColor; /* diffuse color of mesh (rgba) */ + picoColor_t specularColor; /* specular color of mesh (rgba) */ + float transparency; /* transparency (0..1; 1 = 100% transparent) */ + float shininess; /* shininess (0..128; 128 = 100% shiny) */ +}; + +struct picoModel_s +{ + void *data; + char *name; /* model name */ + char *fileName; /* sea: model file name */ + int frameNum; /* sea: renamed to frameNum */ + int numFrames; /* sea: number of frames */ + picoVec3_t mins; + picoVec3_t maxs; + + int numShaders, maxShaders; + picoShader_t **shader; + + int numSurfaces, maxSurfaces; + picoSurface_t **surface; + + const picoModule_t *module; /* sea */ +}; + + +/* seaw0lf */ +/* return codes used by the validation callbacks; pmv is short */ +/* for 'pico module validation'. everything >PICO_PMV_OK means */ +/* that there was an error. */ +enum +{ + PICO_PMV_OK, /* file valid */ + PICO_PMV_ERROR, /* file not valid */ + PICO_PMV_ERROR_IDENT, /* unknown file magic (aka ident) */ + PICO_PMV_ERROR_VERSION, /* unsupported file version */ + PICO_PMV_ERROR_SIZE, /* file size error */ + PICO_PMV_ERROR_MEMORY, /* out of memory error */ +}; + +/* convenience (makes it easy to add new params to the callbacks) */ +#define PM_PARAMS_CANLOAD \ + char *fileName, const void *buffer, int bufSize + +#define PM_PARAMS_LOAD \ + char *fileName, int frameNum, const void *buffer, int bufSize + +#define PM_PARAMS_CANSAVE \ + void + +#define PM_PARAMS_SAVE \ + char *fileName, picoModel_t *model + +/* pico file format module structure */ +struct picoModule_s +{ + char *version; /* internal module version (e.g. '1.5-b2') */ + + char *displayName; /* string used to display in guis, etc. */ + char *authorName; /* author name (eg. 'My Real Name') */ + char *copyright; /* copyright year and holder (eg. '2002 My Company') */ + + char *defaultExts[ PICO_MAX_DEFAULT_EXTS ]; /* default file extensions used by this file type */ + int (*canload)( PM_PARAMS_CANLOAD ); /* checks whether module can load given file (returns PMVR_*) */ + picoModel_t *(*load)( PM_PARAMS_LOAD ); /* parses model file data */ + int (*cansave)( PM_PARAMS_CANSAVE ); /* checks whether module can save (returns 1 or 0 and might spit out a message) */ + int (*save)( PM_PARAMS_SAVE ); /* saves a pico model in module's native model format */ +}; + + + +/* general functions */ +int PicoInit( void ); +void PicoShutdown( void ); +int PicoError( void ); + +void PicoSetMallocFunc( void *(*func)( size_t ) ); +void PicoSetFreeFunc( void (*func)( void* ) ); +void PicoSetLoadFileFunc( void (*func)( char*, unsigned char**, int* ) ); +void PicoSetFreeFileFunc( void (*func)( void* ) ); +void PicoSetPrintFunc( void (*func)( int, const char* ) ); + +const picoModule_t **PicoModuleList( int *numModules ); + +picoModel_t *PicoLoadModel( char *name, int frameNum ); + +typedef size_t (*PicoInputStreamReadFunc)(void* inputStream, unsigned char* buffer, size_t length); +picoModel_t* PicoModuleLoadModelStream( const picoModule_t* module, void* inputStream, PicoInputStreamReadFunc inputStreamRead, size_t streamLength, int frameNum ); + +/* model functions */ +picoModel_t *PicoNewModel( void ); +void PicoFreeModel( picoModel_t *model ); +int PicoAdjustModel( picoModel_t *model, int numShaders, int numSurfaces ); + + +/* shader functions */ +picoShader_t *PicoNewShader( picoModel_t *model ); +void PicoFreeShader( picoShader_t *shader ); +picoShader_t *PicoFindShader( picoModel_t *model, char *name, int caseSensitive ); + + +/* surface functions */ +picoSurface_t *PicoNewSurface( picoModel_t *model ); +void PicoFreeSurface( picoSurface_t *surface ); +picoSurface_t *PicoFindSurface( picoModel_t *model, char *name, int caseSensitive ); +int PicoAdjustSurface( picoSurface_t *surface, int numVertexes, int numSTArrays, int numColorArrays, int numIndexes, int numFaceNormals ); + + +/* setter functions */ +void PicoSetModelName( picoModel_t *model, char *name ); +void PicoSetModelFileName( picoModel_t *model, char *fileName ); +void PicoSetModelFrameNum( picoModel_t *model, int frameNum ); +void PicoSetModelNumFrames( picoModel_t *model, int numFrames ); +void PicoSetModelData( picoModel_t *model, void *data ); + +void PicoSetShaderName( picoShader_t *shader, char *name ); +void PicoSetShaderMapName( picoShader_t *shader, char *mapName ); +void PicoSetShaderAmbientColor( picoShader_t *shader, picoColor_t color ); +void PicoSetShaderDiffuseColor( picoShader_t *shader, picoColor_t color ); +void PicoSetShaderSpecularColor( picoShader_t *shader, picoColor_t color ); +void PicoSetShaderTransparency( picoShader_t *shader, float value ); +void PicoSetShaderShininess( picoShader_t *shader, float value ); + +void PicoSetSurfaceData( picoSurface_t *surface, void *data ); +void PicoSetSurfaceType( picoSurface_t *surface, picoSurfaceType_t type ); +void PicoSetSurfaceName( picoSurface_t *surface, char *name ); +void PicoSetSurfaceShader( picoSurface_t *surface, picoShader_t *shader ); +void PicoSetSurfaceXYZ( picoSurface_t *surface, int num, picoVec3_t xyz ); +void PicoSetSurfaceNormal( picoSurface_t *surface, int num, picoVec3_t normal ); +void PicoSetSurfaceST( picoSurface_t *surface, int array, int num, picoVec2_t st ); +void PicoSetSurfaceColor( picoSurface_t *surface, int array, int num, picoColor_t color ); +void PicoSetSurfaceIndex( picoSurface_t *surface, int num, picoIndex_t index ); +void PicoSetSurfaceIndexes( picoSurface_t *surface, int num, picoIndex_t *index, int count ); +void PicoSetFaceNormal( picoSurface_t *surface, int num, picoVec3_t normal ); +void PicoSetSurfaceSpecial( picoSurface_t *surface, int num, int special ); +void PicoSetSurfaceSmoothingGroup( picoSurface_t *surface, int num, picoIndex_t smoothingGroup ); + + +/* getter functions */ +char *PicoGetModelName( picoModel_t *model ); +char *PicoGetModelFileName( picoModel_t *model ); +int PicoGetModelFrameNum( picoModel_t *model ); +int PicoGetModelNumFrames( picoModel_t *model ); +void *PicoGetModelData( picoModel_t *model ); +int PicoGetModelNumShaders( picoModel_t *model ); +picoShader_t *PicoGetModelShader( picoModel_t *model, int num ); /* sea */ +int PicoGetModelNumSurfaces( picoModel_t *model ); +picoSurface_t *PicoGetModelSurface( picoModel_t *model, int num ); +int PicoGetModelTotalVertexes( picoModel_t *model ); +int PicoGetModelTotalIndexes( picoModel_t *model ); + +char *PicoGetShaderName( picoShader_t *shader ); +char *PicoGetShaderMapName( picoShader_t *shader ); +picoByte_t *PicoGetShaderAmbientColor( picoShader_t *shader ); +picoByte_t *PicoGetShaderDiffuseColor( picoShader_t *shader ); +picoByte_t *PicoGetShaderSpecularColor( picoShader_t *shader ); +float PicoGetShaderTransparency( picoShader_t *shader ); +float PicoGetShaderShininess( picoShader_t *shader ); + +void *PicoGetSurfaceData( picoSurface_t *surface ); +char *PicoGetSurfaceName( picoSurface_t *surface ); /* sea */ +picoSurfaceType_t PicoGetSurfaceType( picoSurface_t *surface ); +char *PicoGetSurfaceName( picoSurface_t *surface ); +picoShader_t *PicoGetSurfaceShader( picoSurface_t *surface ); /* sea */ + +int PicoGetSurfaceNumVertexes( picoSurface_t *surface ); +picoVec_t *PicoGetSurfaceXYZ( picoSurface_t *surface, int num ); +picoVec_t *PicoGetSurfaceNormal( picoSurface_t *surface, int num ); +picoVec_t *PicoGetSurfaceST( picoSurface_t *surface, int array, int num ); +picoByte_t *PicoGetSurfaceColor( picoSurface_t *surface, int array, int num ); +int PicoGetSurfaceNumIndexes( picoSurface_t *surface ); +picoIndex_t PicoGetSurfaceIndex( picoSurface_t *surface, int num ); +picoIndex_t *PicoGetSurfaceIndexes( picoSurface_t *surface, int num ); +picoVec_t *PicoGetFaceNormal( picoSurface_t *surface, int num ); +int PicoGetSurfaceSpecial( picoSurface_t *surface, int num ); + + +/* hashtable related functions */ +typedef struct picoVertexCombinationData_s +{ + picoVec3_t xyz, normal; + picoVec2_t st; + picoColor_t color; +} picoVertexCombinationData_t; + +typedef struct picoVertexCombinationHash_s +{ + picoVertexCombinationData_t vcd; + picoIndex_t index; + + void *data; + + struct picoVertexCombinationHash_s *next; +} picoVertexCombinationHash_t; + +int PicoGetHashTableSize( void ); +unsigned int PicoVertexCoordGenerateHash( picoVec3_t xyz ); +picoVertexCombinationHash_t **PicoNewVertexCombinationHashTable( void ); +void PicoFreeVertexCombinationHashTable( picoVertexCombinationHash_t **hashTable ); +picoVertexCombinationHash_t *PicoFindVertexCombinationInHashTable( picoVertexCombinationHash_t **hashTable, picoVec3_t xyz, picoVec3_t normal, picoVec3_t st, picoColor_t color ); +picoVertexCombinationHash_t *PicoAddVertexCombinationToHashTable( picoVertexCombinationHash_t **hashTable, picoVec3_t xyz, picoVec3_t normal, picoVec3_t st, picoColor_t color, picoIndex_t index ); + +/* specialized functions */ +int PicoFindSurfaceVertexNum( picoSurface_t *surface, picoVec3_t xyz, picoVec3_t normal, int numSTs, picoVec2_t *st, int numColors, picoColor_t *color, picoIndex_t smoothingGroup ); +void PicoFixSurfaceNormals( picoSurface_t *surface ); +int PicoRemapModel( picoModel_t *model, char *remapFile ); + + +void PicoAddTriangleToModel( picoModel_t *model, picoVec3_t** xyz, picoVec3_t** normals, int numSTs, picoVec2_t **st, int numColors, picoColor_t **colors, picoShader_t* shader, picoIndex_t* smoothingGroupm, int submodel); + +/* end marker */ +#ifdef __cplusplus +} +#endif + +#endif diff --git a/tools/urt/libs/picomodel/lwo/clip.c b/tools/urt/libs/picomodel/lwo/clip.c new file mode 100644 index 00000000..0d8e1360 --- /dev/null +++ b/tools/urt/libs/picomodel/lwo/clip.c @@ -0,0 +1,278 @@ +/* +====================================================================== +clip.c + +Functions for LWO2 image references. + +Ernie Wright 17 Sep 00 +====================================================================== */ + +#include "../picointernal.h" +#include "lwo2.h" + + +/* +====================================================================== +lwFreeClip() + +Free memory used by an lwClip. +====================================================================== */ + +void lwFreeClip( lwClip *clip ) +{ + if ( clip ) { + lwListFree( clip->ifilter, (void *) lwFreePlugin ); + lwListFree( clip->pfilter, (void *) lwFreePlugin ); + + switch ( clip->type ) { + case ID_STIL: + _pico_free( clip->source.still.name); + break; + + case ID_ISEQ: + _pico_free( clip->source.seq.prefix ); + _pico_free( clip->source.seq.suffix ); + break; + + case ID_ANIM: + _pico_free( clip->source.anim.name ); + _pico_free( clip->source.anim.server ); + _pico_free( clip->source.anim.data ); + break; + + case ID_XREF: + _pico_free( clip->source.xref.string ); + break; + + case ID_STCC: + _pico_free( clip->source.cycle.name ); + break; + + default: + break; + } + + _pico_free( clip ); + } +} + + +/* +====================================================================== +lwGetClip() + +Read image references from a CLIP chunk in an LWO2 file. +====================================================================== */ + +lwClip *lwGetClip( picoMemStream_t *fp, int cksize ) +{ + lwClip *clip; + lwPlugin *filt; + unsigned int id; + unsigned short sz; + int pos, rlen; + + + /* allocate the Clip structure */ + + clip = _pico_calloc( 1, sizeof( lwClip )); + if ( !clip ) goto Fail; + + clip->contrast.val = 1.0f; + clip->brightness.val = 1.0f; + clip->saturation.val = 1.0f; + clip->gamma.val = 1.0f; + + /* remember where we started */ + + set_flen( 0 ); + pos = _pico_memstream_tell( fp ); + + /* index */ + + clip->index = getI4( fp ); + + /* first subchunk header */ + + clip->type = getU4( fp ); + sz = getU2( fp ); + if ( 0 > get_flen() ) goto Fail; + + sz += sz & 1; + set_flen( 0 ); + + switch ( clip->type ) { + case ID_STIL: + clip->source.still.name = getS0( fp ); + break; + + case ID_ISEQ: + clip->source.seq.digits = getU1( fp ); + clip->source.seq.flags = getU1( fp ); + clip->source.seq.offset = getI2( fp ); + getU2( fp ); /* not sure what this is yet */ + clip->source.seq.start = getI2( fp ); + clip->source.seq.end = getI2( fp ); + clip->source.seq.prefix = getS0( fp ); + clip->source.seq.suffix = getS0( fp ); + break; + + case ID_ANIM: + clip->source.anim.name = getS0( fp ); + clip->source.anim.server = getS0( fp ); + rlen = get_flen(); + clip->source.anim.data = getbytes( fp, sz - rlen ); + break; + + case ID_XREF: + clip->source.xref.index = getI4( fp ); + clip->source.xref.string = getS0( fp ); + break; + + case ID_STCC: + clip->source.cycle.lo = getI2( fp ); + clip->source.cycle.hi = getI2( fp ); + clip->source.cycle.name = getS0( fp ); + break; + + default: + break; + } + + /* error while reading current subchunk? */ + + rlen = get_flen(); + if ( rlen < 0 || rlen > sz ) goto Fail; + + /* skip unread parts of the current subchunk */ + + if ( rlen < sz ) + _pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR ); + + /* end of the CLIP chunk? */ + + rlen = _pico_memstream_tell( fp ) - pos; + if ( cksize < rlen ) goto Fail; + if ( cksize == rlen ) + return clip; + + /* process subchunks as they're encountered */ + + id = getU4( fp ); + sz = getU2( fp ); + if ( 0 > get_flen() ) goto Fail; + + while ( 1 ) { + sz += sz & 1; + set_flen( 0 ); + + switch ( id ) { + case ID_TIME: + clip->start_time = getF4( fp ); + clip->duration = getF4( fp ); + clip->frame_rate = getF4( fp ); + break; + + case ID_CONT: + clip->contrast.val = getF4( fp ); + clip->contrast.eindex = getVX( fp ); + break; + + case ID_BRIT: + clip->brightness.val = getF4( fp ); + clip->brightness.eindex = getVX( fp ); + break; + + case ID_SATR: + clip->saturation.val = getF4( fp ); + clip->saturation.eindex = getVX( fp ); + break; + + case ID_HUE: + clip->hue.val = getF4( fp ); + clip->hue.eindex = getVX( fp ); + break; + + case ID_GAMM: + clip->gamma.val = getF4( fp ); + clip->gamma.eindex = getVX( fp ); + break; + + case ID_NEGA: + clip->negative = getU2( fp ); + break; + + case ID_IFLT: + case ID_PFLT: + filt = _pico_calloc( 1, sizeof( lwPlugin )); + if ( !filt ) goto Fail; + + filt->name = getS0( fp ); + filt->flags = getU2( fp ); + rlen = get_flen(); + filt->data = getbytes( fp, sz - rlen ); + + if ( id == ID_IFLT ) { + lwListAdd( (void *) &clip->ifilter, filt ); + clip->nifilters++; + } + else { + lwListAdd( (void *) &clip->pfilter, filt ); + clip->npfilters++; + } + break; + + default: + break; + } + + /* error while reading current subchunk? */ + + rlen = get_flen(); + if ( rlen < 0 || rlen > sz ) goto Fail; + + /* skip unread parts of the current subchunk */ + + if ( rlen < sz ) + _pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR ); + + /* end of the CLIP chunk? */ + + rlen = _pico_memstream_tell( fp ) - pos; + if ( cksize < rlen ) goto Fail; + if ( cksize == rlen ) break; + + /* get the next chunk header */ + + set_flen( 0 ); + id = getU4( fp ); + sz = getU2( fp ); + if ( 6 != get_flen() ) goto Fail; + } + + return clip; + +Fail: + lwFreeClip( clip ); + return NULL; +} + + +/* +====================================================================== +lwFindClip() + +Returns an lwClip pointer, given a clip index. +====================================================================== */ + +lwClip *lwFindClip( lwClip *list, int index ) +{ + lwClip *clip; + + clip = list; + while ( clip ) { + if ( clip->index == index ) break; + clip = clip->next; + } + return clip; +} diff --git a/tools/urt/libs/picomodel/lwo/envelope.c b/tools/urt/libs/picomodel/lwo/envelope.c new file mode 100644 index 00000000..94e9c906 --- /dev/null +++ b/tools/urt/libs/picomodel/lwo/envelope.c @@ -0,0 +1,600 @@ +/* +====================================================================== +envelope.c + +Envelope functions for an LWO2 reader. + +Ernie Wright 16 Nov 00 +====================================================================== */ + +#include "../picointernal.h" +#include "lwo2.h" + +/* +====================================================================== +lwFreeEnvelope() + +Free the memory used by an lwEnvelope. +====================================================================== */ + +void lwFreeEnvelope( lwEnvelope *env ) +{ + if ( env ) { + if ( env->name ) _pico_free( env->name ); + lwListFree( env->key, _pico_free ); + lwListFree( env->cfilter, (void *) lwFreePlugin ); + _pico_free( env ); + } +} + + +static int compare_keys( lwKey *k1, lwKey *k2 ) +{ + return k1->time > k2->time ? 1 : k1->time < k2->time ? -1 : 0; +} + + +/* +====================================================================== +lwGetEnvelope() + +Read an ENVL chunk from an LWO2 file. +====================================================================== */ + +lwEnvelope *lwGetEnvelope( picoMemStream_t *fp, int cksize ) +{ + lwEnvelope *env; + lwKey *key; + lwPlugin *plug; + unsigned int id; + unsigned short sz; + float f[ 4 ]; + int i, nparams, pos, rlen; + + + /* allocate the Envelope structure */ + + env = _pico_calloc( 1, sizeof( lwEnvelope )); + if ( !env ) goto Fail; + + /* remember where we started */ + + set_flen( 0 ); + pos = _pico_memstream_tell( fp ); + + /* index */ + + env->index = getVX( fp ); + + /* first subchunk header */ + + id = getU4( fp ); + sz = getU2( fp ); + if ( 0 > get_flen() ) goto Fail; + + /* process subchunks as they're encountered */ + + while ( 1 ) { + sz += sz & 1; + set_flen( 0 ); + + switch ( id ) { + case ID_TYPE: + env->type = getU2( fp ); + break; + + case ID_NAME: + env->name = getS0( fp ); + break; + + case ID_PRE: + env->behavior[ 0 ] = getU2( fp ); + break; + + case ID_POST: + env->behavior[ 1 ] = getU2( fp ); + break; + + case ID_KEY: + key = _pico_calloc( 1, sizeof( lwKey )); + if ( !key ) goto Fail; + key->time = getF4( fp ); + key->value = getF4( fp ); + lwListInsert( (void **) &env->key, key, (void *) compare_keys ); + env->nkeys++; + break; + + case ID_SPAN: + if ( !key ) goto Fail; + key->shape = getU4( fp ); + + nparams = ( sz - 4 ) / 4; + if ( nparams > 4 ) nparams = 4; + for ( i = 0; i < nparams; i++ ) + f[ i ] = getF4( fp ); + + switch ( key->shape ) { + case ID_TCB: + key->tension = f[ 0 ]; + key->continuity = f[ 1 ]; + key->bias = f[ 2 ]; + break; + + case ID_BEZI: + case ID_HERM: + case ID_BEZ2: + for ( i = 0; i < nparams; i++ ) + key->param[ i ] = f[ i ]; + break; + } + break; + + case ID_CHAN: + plug = _pico_calloc( 1, sizeof( lwPlugin )); + if ( !plug ) goto Fail; + + plug->name = getS0( fp ); + plug->flags = getU2( fp ); + plug->data = getbytes( fp, sz - get_flen() ); + + lwListAdd( (void *) &env->cfilter, plug ); + env->ncfilters++; + break; + + default: + break; + } + + /* error while reading current subchunk? */ + + rlen = get_flen(); + if ( rlen < 0 || rlen > sz ) goto Fail; + + /* skip unread parts of the current subchunk */ + + if ( rlen < sz ) + _pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR ); + + /* end of the ENVL chunk? */ + + rlen = _pico_memstream_tell( fp ) - pos; + if ( cksize < rlen ) goto Fail; + if ( cksize == rlen ) break; + + /* get the next subchunk header */ + + set_flen( 0 ); + id = getU4( fp ); + sz = getU2( fp ); + if ( 6 != get_flen() ) goto Fail; + } + + return env; + +Fail: + lwFreeEnvelope( env ); + return NULL; +} + + +/* +====================================================================== +lwFindEnvelope() + +Returns an lwEnvelope pointer, given an envelope index. +====================================================================== */ + +lwEnvelope *lwFindEnvelope( lwEnvelope *list, int index ) +{ + lwEnvelope *env; + + env = list; + while ( env ) { + if ( env->index == index ) break; + env = env->next; + } + return env; +} + + +/* +====================================================================== +range() + +Given the value v of a periodic function, returns the equivalent value +v2 in the principal interval [lo, hi]. If i isn't NULL, it receives +the number of wavelengths between v and v2. + + v2 = v - i * (hi - lo) + +For example, range( 3 pi, 0, 2 pi, i ) returns pi, with i = 1. +====================================================================== */ + +static float range( float v, float lo, float hi, int *i ) +{ + float v2, r = hi - lo; + + if ( r == 0.0 ) { + if ( i ) *i = 0; + return lo; + } + + v2 = lo + v - r * ( float ) floor(( double ) v / r ); + if ( i ) *i = -( int )(( v2 - v ) / r + ( v2 > v ? 0.5 : -0.5 )); + + return v2; +} + + +/* +====================================================================== +hermite() + +Calculate the Hermite coefficients. +====================================================================== */ + +static void hermite( float t, float *h1, float *h2, float *h3, float *h4 ) +{ + float t2, t3; + + t2 = t * t; + t3 = t * t2; + + *h2 = 3.0f * t2 - t3 - t3; + *h1 = 1.0f - *h2; + *h4 = t3 - t2; + *h3 = *h4 - t2 + t; +} + + +/* +====================================================================== +bezier() + +Interpolate the value of a 1D Bezier curve. +====================================================================== */ + +static float bezier( float x0, float x1, float x2, float x3, float t ) +{ + float a, b, c, t2, t3; + + t2 = t * t; + t3 = t2 * t; + + c = 3.0f * ( x1 - x0 ); + b = 3.0f * ( x2 - x1 ) - c; + a = x3 - x0 - c - b; + + return a * t3 + b * t2 + c * t + x0; +} + + +/* +====================================================================== +bez2_time() + +Find the t for which bezier() returns the input time. The handle +endpoints of a BEZ2 curve represent the control points, and these have +(time, value) coordinates, so time is used as both a coordinate and a +parameter for this curve type. +====================================================================== */ + +static float bez2_time( float x0, float x1, float x2, float x3, float time, + float *t0, float *t1 ) +{ + float v, t; + + t = *t0 + ( *t1 - *t0 ) * 0.5f; + v = bezier( x0, x1, x2, x3, t ); + if ( fabs( time - v ) > .0001f ) { + if ( v > time ) + *t1 = t; + else + *t0 = t; + return bez2_time( x0, x1, x2, x3, time, t0, t1 ); + } + else + return t; +} + + +/* +====================================================================== +bez2() + +Interpolate the value of a BEZ2 curve. +====================================================================== */ + +static float bez2( lwKey *key0, lwKey *key1, float time ) +{ + float x, y, t, t0 = 0.0f, t1 = 1.0f; + + if ( key0->shape == ID_BEZ2 ) + x = key0->time + key0->param[ 2 ]; + else + x = key0->time + ( key1->time - key0->time ) / 3.0f; + + t = bez2_time( key0->time, x, key1->time + key1->param[ 0 ], key1->time, + time, &t0, &t1 ); + + if ( key0->shape == ID_BEZ2 ) + y = key0->value + key0->param[ 3 ]; + else + y = key0->value + key0->param[ 1 ] / 3.0f; + + return bezier( key0->value, y, key1->param[ 1 ] + key1->value, key1->value, t ); +} + + +/* +====================================================================== +outgoing() + +Return the outgoing tangent to the curve at key0. The value returned +for the BEZ2 case is used when extrapolating a linear pre behavior and +when interpolating a non-BEZ2 span. +====================================================================== */ + +static float outgoing( lwKey *key0, lwKey *key1 ) +{ + float a, b, d, t, out; + + switch ( key0->shape ) + { + case ID_TCB: + a = ( 1.0f - key0->tension ) + * ( 1.0f + key0->continuity ) + * ( 1.0f + key0->bias ); + b = ( 1.0f - key0->tension ) + * ( 1.0f - key0->continuity ) + * ( 1.0f - key0->bias ); + d = key1->value - key0->value; + + if ( key0->prev ) { + t = ( key1->time - key0->time ) / ( key1->time - key0->prev->time ); + out = t * ( a * ( key0->value - key0->prev->value ) + b * d ); + } + else + out = b * d; + break; + + case ID_LINE: + d = key1->value - key0->value; + if ( key0->prev ) { + t = ( key1->time - key0->time ) / ( key1->time - key0->prev->time ); + out = t * ( key0->value - key0->prev->value + d ); + } + else + out = d; + break; + + case ID_BEZI: + case ID_HERM: + out = key0->param[ 1 ]; + if ( key0->prev ) + out *= ( key1->time - key0->time ) / ( key1->time - key0->prev->time ); + break; + + case ID_BEZ2: + out = key0->param[ 3 ] * ( key1->time - key0->time ); + if ( fabs( key0->param[ 2 ] ) > 1e-5f ) + out /= key0->param[ 2 ]; + else + out *= 1e5f; + break; + + case ID_STEP: + default: + out = 0.0f; + break; + } + + return out; +} + + +/* +====================================================================== +incoming() + +Return the incoming tangent to the curve at key1. The value returned +for the BEZ2 case is used when extrapolating a linear post behavior. +====================================================================== */ + +static float incoming( lwKey *key0, lwKey *key1 ) +{ + float a, b, d, t, in; + + switch ( key1->shape ) + { + case ID_LINE: + d = key1->value - key0->value; + if ( key1->next ) { + t = ( key1->time - key0->time ) / ( key1->next->time - key0->time ); + in = t * ( key1->next->value - key1->value + d ); + } + else + in = d; + break; + + case ID_TCB: + a = ( 1.0f - key1->tension ) + * ( 1.0f - key1->continuity ) + * ( 1.0f + key1->bias ); + b = ( 1.0f - key1->tension ) + * ( 1.0f + key1->continuity ) + * ( 1.0f - key1->bias ); + d = key1->value - key0->value; + + if ( key1->next ) { + t = ( key1->time - key0->time ) / ( key1->next->time - key0->time ); + in = t * ( b * ( key1->next->value - key1->value ) + a * d ); + } + else + in = a * d; + break; + + case ID_BEZI: + case ID_HERM: + in = key1->param[ 0 ]; + if ( key1->next ) + in *= ( key1->time - key0->time ) / ( key1->next->time - key0->time ); + break; + return in; + + case ID_BEZ2: + in = key1->param[ 1 ] * ( key1->time - key0->time ); + if ( fabs( key1->param[ 0 ] ) > 1e-5f ) + in /= key1->param[ 0 ]; + else + in *= 1e5f; + break; + + case ID_STEP: + default: + in = 0.0f; + break; + } + + return in; +} + + +/* +====================================================================== +evalEnvelope() + +Given a list of keys and a time, returns the interpolated value of the +envelope at that time. +====================================================================== */ + +float evalEnvelope( lwEnvelope *env, float time ) +{ + lwKey *key0, *key1, *skey, *ekey; + float t, h1, h2, h3, h4, in, out, offset = 0.0f; + int noff; + + + /* if there's no key, the value is 0 */ + + if ( env->nkeys == 0 ) return 0.0f; + + /* if there's only one key, the value is constant */ + + if ( env->nkeys == 1 ) + return env->key->value; + + /* find the first and last keys */ + + skey = ekey = env->key; + while ( ekey->next ) ekey = ekey->next; + + /* use pre-behavior if time is before first key time */ + + if ( time < skey->time ) { + switch ( env->behavior[ 0 ] ) + { + case BEH_RESET: + return 0.0f; + + case BEH_CONSTANT: + return skey->value; + + case BEH_REPEAT: + time = range( time, skey->time, ekey->time, NULL ); + break; + + case BEH_OSCILLATE: + time = range( time, skey->time, ekey->time, &noff ); + if ( noff % 2 ) + time = ekey->time - skey->time - time; + break; + + case BEH_OFFSET: + time = range( time, skey->time, ekey->time, &noff ); + offset = noff * ( ekey->value - skey->value ); + break; + + case BEH_LINEAR: + out = outgoing( skey, skey->next ) + / ( skey->next->time - skey->time ); + return out * ( time - skey->time ) + skey->value; + } + } + + /* use post-behavior if time is after last key time */ + + else if ( time > ekey->time ) { + switch ( env->behavior[ 1 ] ) + { + case BEH_RESET: + return 0.0f; + + case BEH_CONSTANT: + return ekey->value; + + case BEH_REPEAT: + time = range( time, skey->time, ekey->time, NULL ); + break; + + case BEH_OSCILLATE: + time = range( time, skey->time, ekey->time, &noff ); + if ( noff % 2 ) + time = ekey->time - skey->time - time; + break; + + case BEH_OFFSET: + time = range( time, skey->time, ekey->time, &noff ); + offset = noff * ( ekey->value - skey->value ); + break; + + case BEH_LINEAR: + in = incoming( ekey->prev, ekey ) + / ( ekey->time - ekey->prev->time ); + return in * ( time - ekey->time ) + ekey->value; + } + } + + /* get the endpoints of the interval being evaluated */ + + key0 = env->key; + while ( time > key0->next->time ) + key0 = key0->next; + key1 = key0->next; + + /* check for singularities first */ + + if ( time == key0->time ) + return key0->value + offset; + else if ( time == key1->time ) + return key1->value + offset; + + /* get interval length, time in [0, 1] */ + + t = ( time - key0->time ) / ( key1->time - key0->time ); + + /* interpolate */ + + switch ( key1->shape ) + { + case ID_TCB: + case ID_BEZI: + case ID_HERM: + out = outgoing( key0, key1 ); + in = incoming( key0, key1 ); + hermite( t, &h1, &h2, &h3, &h4 ); + return h1 * key0->value + h2 * key1->value + h3 * out + h4 * in + offset; + + case ID_BEZ2: + return bez2( key0, key1, time ) + offset; + + case ID_LINE: + return key0->value + t * ( key1->value - key0->value ) + offset; + + case ID_STEP: + return key0->value + offset; + + default: + return offset; + } +} diff --git a/tools/urt/libs/picomodel/lwo/libs_rar.rar b/tools/urt/libs/picomodel/lwo/libs_rar.rar new file mode 100644 index 00000000..6421f5cc Binary files /dev/null and b/tools/urt/libs/picomodel/lwo/libs_rar.rar differ diff --git a/tools/urt/libs/picomodel/lwo/list.c b/tools/urt/libs/picomodel/lwo/list.c new file mode 100644 index 00000000..d07b0337 --- /dev/null +++ b/tools/urt/libs/picomodel/lwo/list.c @@ -0,0 +1,101 @@ +/* +====================================================================== +list.c + +Generic linked list operations. + +Ernie Wright 17 Sep 00 +====================================================================== */ + +#include "../picointernal.h" +#include "lwo2.h" + + +/* +====================================================================== +lwListFree() + +Free the items in a list. +====================================================================== */ + +void lwListFree( void *list, void ( *freeNode )( void * )) +{ + lwNode *node, *next; + + node = ( lwNode * ) list; + while ( node ) { + next = node->next; + freeNode( node ); + node = next; + } +} + + +/* +====================================================================== +lwListAdd() + +Append a node to a list. +====================================================================== */ + +void lwListAdd( void **list, void *node ) +{ + lwNode *head, *tail; + + head = *(( lwNode ** ) list ); + if ( !head ) { + *list = node; + return; + } + while ( head ) { + tail = head; + head = head->next; + } + tail->next = ( lwNode * ) node; + (( lwNode * ) node )->prev = tail; +} + + +/* +====================================================================== +lwListInsert() + +Insert a node into a list in sorted order. +====================================================================== */ + +void lwListInsert( void **vlist, void *vitem, int ( *compare )( void *, void * )) +{ + lwNode **list, *item, *node, *prev; + + if ( !*vlist ) { + *vlist = vitem; + return; + } + + list = ( lwNode ** ) vlist; + item = ( lwNode * ) vitem; + node = *list; + prev = NULL; + + while ( node ) { + if ( 0 < compare( node, item )) break; + prev = node; + node = node->next; + } + + if ( !prev ) { + *list = item; + node->prev = item; + item->next = node; + } + else if ( !node ) { + prev->next = item; + item->prev = prev; + } + else { + item->next = node; + item->prev = prev; + prev->next = item; + node->prev = item; + } +} diff --git a/tools/urt/libs/picomodel/lwo/lwio.c b/tools/urt/libs/picomodel/lwo/lwio.c new file mode 100644 index 00000000..f8408abd --- /dev/null +++ b/tools/urt/libs/picomodel/lwo/lwio.c @@ -0,0 +1,442 @@ +/* +====================================================================== +lwio.c + +Functions for reading basic LWO2 data types. + +Ernie Wright 17 Sep 00 +====================================================================== */ + +#include "../picointernal.h" +#include "lwo2.h" + + +/* +====================================================================== +flen + +This accumulates a count of the number of bytes read. Callers can set +it at the beginning of a sequence of reads and then retrieve it to get +the number of bytes actually read. If one of the I/O functions fails, +flen is set to an error code, after which the I/O functions ignore +read requests until flen is reset. +====================================================================== */ + +#define INT_MIN (-2147483647 - 1) /* minimum (signed) int value */ +#define FLEN_ERROR INT_MIN + +static int flen; + +void set_flen( int i ) { flen = i; } + +int get_flen( void ) { return flen; } + + +#ifndef __BIG_ENDIAN__ +/* +===================================================================== +revbytes() + +Reverses byte order in place. + +INPUTS + bp bytes to reverse + elsize size of the underlying data type + elcount number of elements to swap + +RESULTS + Reverses the byte order in each of elcount elements. + +This only needs to be defined on little-endian platforms, most +notably Windows. lwo2.h replaces this with a #define on big-endian +platforms. +===================================================================== */ + +void revbytes( void *bp, int elsize, int elcount ) +{ + register unsigned char *p, *q; + + p = ( unsigned char * ) bp; + + if ( elsize == 2 ) { + q = p + 1; + while ( elcount-- ) { + *p ^= *q; + *q ^= *p; + *p ^= *q; + p += 2; + q += 2; + } + return; + } + + while ( elcount-- ) { + q = p + elsize - 1; + while ( p < q ) { + *p ^= *q; + *q ^= *p; + *p ^= *q; + ++p; + --q; + } + p += elsize >> 1; + } +} +#endif + + +void *getbytes( picoMemStream_t *fp, int size ) +{ + void *data; + + if ( flen == FLEN_ERROR ) return NULL; + if ( size < 0 ) { + flen = FLEN_ERROR; + return NULL; + } + data = _pico_alloc( size ); + if ( !data ) { + flen = FLEN_ERROR; + return NULL; + } + if ( 1 != _pico_memstream_read( fp, data, size )) { + flen = FLEN_ERROR; + _pico_free( data ); + return NULL; + } + + flen += size; + return data; +} + + +void skipbytes( picoMemStream_t *fp, int n ) +{ + if ( flen == FLEN_ERROR ) return; + if ( _pico_memstream_seek( fp, n, PICO_SEEK_CUR )) + flen = FLEN_ERROR; + else + flen += n; +} + + +int getI1( picoMemStream_t *fp ) +{ + int i; + + if ( flen == FLEN_ERROR ) return 0; + i = _pico_memstream_getc( fp ); + if ( i < 0 ) { + flen = FLEN_ERROR; + return 0; + } + if ( i > 127 ) i -= 256; + flen += 1; + return i; +} + + +short getI2( picoMemStream_t *fp ) +{ + short i; + + if ( flen == FLEN_ERROR ) return 0; + if ( 1 != _pico_memstream_read( fp, &i, 2 )) { + flen = FLEN_ERROR; + return 0; + } + revbytes( &i, 2, 1 ); + flen += 2; + return i; +} + + +int getI4( picoMemStream_t *fp ) +{ + int i; + + if ( flen == FLEN_ERROR ) return 0; + if ( 1 != _pico_memstream_read( fp, &i, 4 )) { + flen = FLEN_ERROR; + return 0; + } + revbytes( &i, 4, 1 ); + flen += 4; + return i; +} + + +unsigned char getU1( picoMemStream_t *fp ) +{ + int i; + + if ( flen == FLEN_ERROR ) return 0; + i = _pico_memstream_getc( fp ); + if ( i < 0 ) { + flen = FLEN_ERROR; + return 0; + } + flen += 1; + return i; +} + + +unsigned short getU2( picoMemStream_t *fp ) +{ + unsigned short i; + + if ( flen == FLEN_ERROR ) return 0; + if ( 1 != _pico_memstream_read( fp, &i, 2 )) { + flen = FLEN_ERROR; + return 0; + } + revbytes( &i, 2, 1 ); + flen += 2; + return i; +} + + +unsigned int getU4( picoMemStream_t *fp ) +{ + unsigned int i; + + if ( flen == FLEN_ERROR ) return 0; + if ( 1 != _pico_memstream_read( fp, &i, 4 )) { + flen = FLEN_ERROR; + return 0; + } + revbytes( &i, 4, 1 ); + flen += 4; + return i; +} + + +int getVX( picoMemStream_t *fp ) +{ + int i, c; + + if ( flen == FLEN_ERROR ) return 0; + + c = _pico_memstream_getc( fp ); + if ( c != 0xFF ) { + i = c << 8; + c = _pico_memstream_getc( fp ); + i |= c; + flen += 2; + } + else { + c = _pico_memstream_getc( fp ); + i = c << 16; + c = _pico_memstream_getc( fp ); + i |= c << 8; + c = _pico_memstream_getc( fp ); + i |= c; + flen += 4; + } + + if ( _pico_memstream_error( fp )) { + flen = FLEN_ERROR; + return 0; + } + return i; +} + + +float getF4( picoMemStream_t *fp ) +{ + float f; + + if ( flen == FLEN_ERROR ) return 0.0f; + if ( 1 != _pico_memstream_read( fp, &f, 4 )) { + flen = FLEN_ERROR; + return 0.0f; + } + revbytes( &f, 4, 1 ); + flen += 4; + return f; +} + + +char *getS0( picoMemStream_t *fp ) +{ + char *s; + int i, c, len, pos; + + if ( flen == FLEN_ERROR ) return NULL; + + pos = _pico_memstream_tell( fp ); + for ( i = 1; ; i++ ) { + c = _pico_memstream_getc( fp ); + if ( c <= 0 ) break; + } + if ( c < 0 ) { + flen = FLEN_ERROR; + return NULL; + } + + if ( i == 1 ) { + if ( _pico_memstream_seek( fp, pos + 2, PICO_SEEK_SET )) + flen = FLEN_ERROR; + else + flen += 2; + return NULL; + } + + len = i + ( i & 1 ); + s = _pico_alloc( len ); + if ( !s ) { + flen = FLEN_ERROR; + return NULL; + } + + if ( _pico_memstream_seek( fp, pos, PICO_SEEK_SET )) { + flen = FLEN_ERROR; + return NULL; + } + if ( 1 != _pico_memstream_read( fp, s, len )) { + flen = FLEN_ERROR; + return NULL; + } + + flen += len; + return s; +} + + +int sgetI1( unsigned char **bp ) +{ + int i; + + if ( flen == FLEN_ERROR ) return 0; + i = **bp; + if ( i > 127 ) i -= 256; + flen += 1; + *bp++; + return i; +} + + +short sgetI2( unsigned char **bp ) +{ + short i; + + if ( flen == FLEN_ERROR ) return 0; + memcpy( &i, *bp, 2 ); + revbytes( &i, 2, 1 ); + flen += 2; + *bp += 2; + return i; +} + + +int sgetI4( unsigned char **bp ) +{ + int i; + + if ( flen == FLEN_ERROR ) return 0; + memcpy( &i, *bp, 4 ); + revbytes( &i, 4, 1 ); + flen += 4; + *bp += 4; + return i; +} + + +unsigned char sgetU1( unsigned char **bp ) +{ + unsigned char c; + + if ( flen == FLEN_ERROR ) return 0; + c = **bp; + flen += 1; + *bp++; + return c; +} + + +unsigned short sgetU2( unsigned char **bp ) +{ + unsigned char *buf = *bp; + unsigned short i; + + if ( flen == FLEN_ERROR ) return 0; + i = ( buf[ 0 ] << 8 ) | buf[ 1 ]; + flen += 2; + *bp += 2; + return i; +} + + +unsigned int sgetU4( unsigned char **bp ) +{ + unsigned int i; + + if ( flen == FLEN_ERROR ) return 0; + memcpy( &i, *bp, 4 ); + revbytes( &i, 4, 1 ); + flen += 4; + *bp += 4; + return i; +} + + +int sgetVX( unsigned char **bp ) +{ + unsigned char *buf = *bp; + int i; + + if ( flen == FLEN_ERROR ) return 0; + + if ( buf[ 0 ] != 0xFF ) { + i = buf[ 0 ] << 8 | buf[ 1 ]; + flen += 2; + *bp += 2; + } + else { + i = ( buf[ 1 ] << 16 ) | ( buf[ 2 ] << 8 ) | buf[ 3 ]; + flen += 4; + *bp += 4; + } + return i; +} + + +float sgetF4( unsigned char **bp ) +{ + float f; + + if ( flen == FLEN_ERROR ) return 0.0f; + memcpy( &f, *bp, 4 ); + revbytes( &f, 4, 1 ); + flen += 4; + *bp += 4; + return f; +} + + +char *sgetS0( unsigned char **bp ) +{ + char *s; + unsigned char *buf = *bp; + int len; + + if ( flen == FLEN_ERROR ) return NULL; + + len = strlen( buf ) + 1; + if ( len == 1 ) { + flen += 2; + *bp += 2; + return NULL; + } + len += len & 1; + s = _pico_alloc( len ); + if ( !s ) { + flen = FLEN_ERROR; + return NULL; + } + + memcpy( s, buf, len ); + flen += len; + *bp += len; + return s; +} diff --git a/tools/urt/libs/picomodel/lwo/lwo2.c b/tools/urt/libs/picomodel/lwo/lwo2.c new file mode 100644 index 00000000..cc46f395 --- /dev/null +++ b/tools/urt/libs/picomodel/lwo/lwo2.c @@ -0,0 +1,308 @@ +/* +====================================================================== +lwo2.c + +The entry point for loading LightWave object files. + +Ernie Wright 17 Sep 00 +====================================================================== */ + +#include "../picointernal.h" +#include "lwo2.h" + +/* disable warnings */ +#ifdef WIN32 +#pragma warning( disable:4018 ) /* signed/unsigned mismatch */ +#endif + + +/* +====================================================================== +lwFreeLayer() + +Free memory used by an lwLayer. +====================================================================== */ + +void lwFreeLayer( lwLayer *layer ) +{ + if ( layer ) { + if ( layer->name ) _pico_free( layer->name ); + lwFreePoints( &layer->point ); + lwFreePolygons( &layer->polygon ); + lwListFree( layer->vmap, (void *) lwFreeVMap ); + _pico_free( layer ); + } +} + + +/* +====================================================================== +lwFreeObject() + +Free memory used by an lwObject. +====================================================================== */ + +void lwFreeObject( lwObject *object ) +{ + if ( object ) { + lwListFree( object->layer, (void *) lwFreeLayer ); + lwListFree( object->env, (void *) lwFreeEnvelope ); + lwListFree( object->clip, (void *) lwFreeClip ); + lwListFree( object->surf, (void *) lwFreeSurface ); + lwFreeTags( &object->taglist ); + _pico_free( object ); + } +} + + +/* +====================================================================== +lwGetObject() + +Returns the contents of a LightWave object, given its filename, or +NULL if the file couldn't be loaded. On failure, failID and failpos +can be used to diagnose the cause. + +1. If the file isn't an LWO2 or an LWOB, failpos will contain 12 and + failID will be unchanged. + +2. If an error occurs while reading, failID will contain the most + recently read IFF chunk ID, and failpos will contain the value + returned by _pico_memstream_tell() at the time of the failure. + +3. If the file couldn't be opened, or an error occurs while reading + the first 12 bytes, both failID and failpos will be unchanged. + +If you don't need this information, failID and failpos can be NULL. +====================================================================== */ + +lwObject *lwGetObject( char *filename, picoMemStream_t *fp, unsigned int *failID, int *failpos ) +{ + lwObject *object; + lwLayer *layer; + lwNode *node; + unsigned int id, formsize, type, cksize; + int i, rlen; + + /* open the file */ + + if ( !fp ) return NULL; + + /* read the first 12 bytes */ + + set_flen( 0 ); + id = getU4( fp ); + formsize = getU4( fp ); + type = getU4( fp ); + if ( 12 != get_flen() ) { + return NULL; + } + + /* is this a LW object? */ + + if ( id != ID_FORM ) { + if ( failpos ) *failpos = 12; + return NULL; + } + + if ( type != ID_LWO2 ) { + if ( type == ID_LWOB ) + return lwGetObject5( filename, fp, failID, failpos ); + else { + if ( failpos ) *failpos = 12; + return NULL; + } + } + + /* allocate an object and a default layer */ + + object = _pico_calloc( 1, sizeof( lwObject )); + if ( !object ) goto Fail; + + layer = _pico_calloc( 1, sizeof( lwLayer )); + if ( !layer ) goto Fail; + object->layer = layer; + + /* get the first chunk header */ + + id = getU4( fp ); + cksize = getU4( fp ); + if ( 0 > get_flen() ) goto Fail; + + /* process chunks as they're encountered */ + + while ( 1 ) { + cksize += cksize & 1; + + switch ( id ) + { + case ID_LAYR: + if ( object->nlayers > 0 ) { + layer = _pico_calloc( 1, sizeof( lwLayer )); + if ( !layer ) goto Fail; + lwListAdd( (void **) &object->layer, layer ); + } + object->nlayers++; + + set_flen( 0 ); + layer->index = getU2( fp ); + layer->flags = getU2( fp ); + layer->pivot[ 0 ] = getF4( fp ); + layer->pivot[ 1 ] = getF4( fp ); + layer->pivot[ 2 ] = getF4( fp ); + layer->name = getS0( fp ); + + rlen = get_flen(); + if ( rlen < 0 || rlen > cksize ) goto Fail; + if ( rlen <= cksize - 2 ) + layer->parent = getU2( fp ); + rlen = get_flen(); + if ( rlen < cksize ) + _pico_memstream_seek( fp, cksize - rlen, PICO_SEEK_CUR ); + break; + + case ID_PNTS: + if ( !lwGetPoints( fp, cksize, &layer->point )) + goto Fail; + break; + + case ID_POLS: + if ( !lwGetPolygons( fp, cksize, &layer->polygon, + layer->point.offset )) + goto Fail; + break; + + case ID_VMAP: + case ID_VMAD: + node = ( lwNode * ) lwGetVMap( fp, cksize, layer->point.offset, + layer->polygon.offset, id == ID_VMAD ); + if ( !node ) goto Fail; + lwListAdd( (void **) &layer->vmap, node ); + layer->nvmaps++; + break; + + case ID_PTAG: + if ( !lwGetPolygonTags( fp, cksize, &object->taglist, + &layer->polygon )) + goto Fail; + break; + + case ID_BBOX: + set_flen( 0 ); + for ( i = 0; i < 6; i++ ) + layer->bbox[ i ] = getF4( fp ); + rlen = get_flen(); + if ( rlen < 0 || rlen > cksize ) goto Fail; + if ( rlen < cksize ) + _pico_memstream_seek( fp, cksize - rlen, PICO_SEEK_CUR ); + break; + + case ID_TAGS: + if ( !lwGetTags( fp, cksize, &object->taglist )) + goto Fail; + break; + + case ID_ENVL: + node = ( lwNode * ) lwGetEnvelope( fp, cksize ); + if ( !node ) goto Fail; + lwListAdd( (void **) &object->env, node ); + object->nenvs++; + break; + + case ID_CLIP: + node = ( lwNode * ) lwGetClip( fp, cksize ); + if ( !node ) goto Fail; + lwListAdd( (void **) &object->clip, node ); + object->nclips++; + break; + + case ID_SURF: + node = ( lwNode * ) lwGetSurface( fp, cksize ); + if ( !node ) goto Fail; + lwListAdd( (void **) &object->surf, node ); + object->nsurfs++; + break; + + case ID_DESC: + case ID_TEXT: + case ID_ICON: + default: + _pico_memstream_seek( fp, cksize, PICO_SEEK_CUR ); + break; + } + + /* end of the file? */ + + if ( formsize <= _pico_memstream_tell( fp ) - 8 ) break; + + /* get the next chunk header */ + + set_flen( 0 ); + id = getU4( fp ); + cksize = getU4( fp ); + if ( 8 != get_flen() ) goto Fail; + } + + if ( object->nlayers == 0 ) + object->nlayers = 1; + + layer = object->layer; + while ( layer ) { + lwGetBoundingBox( &layer->point, layer->bbox ); + lwGetPolyNormals( &layer->point, &layer->polygon ); + if ( !lwGetPointPolygons( &layer->point, &layer->polygon )) goto Fail; + if ( !lwResolvePolySurfaces( &layer->polygon, &object->taglist, + &object->surf, &object->nsurfs )) goto Fail; + lwGetVertNormals( &layer->point, &layer->polygon ); + if ( !lwGetPointVMaps( &layer->point, layer->vmap )) goto Fail; + if ( !lwGetPolyVMaps( &layer->polygon, layer->vmap )) goto Fail; + layer = layer->next; + } + + return object; + +Fail: + if ( failID ) *failID = id; + if ( fp ) { + if ( failpos ) *failpos = _pico_memstream_tell( fp ); + } + lwFreeObject( object ); + return NULL; +} + +int lwValidateObject( char *filename, picoMemStream_t *fp, unsigned int *failID, int *failpos ) +{ + unsigned int id, formsize, type; + + /* open the file */ + + if ( !fp ) return PICO_PMV_ERROR_MEMORY; + + /* read the first 12 bytes */ + + set_flen( 0 ); + id = getU4( fp ); + formsize = getU4( fp ); + type = getU4( fp ); + if ( 12 != get_flen() ) { + return PICO_PMV_ERROR_SIZE; + } + + /* is this a LW object? */ + + if ( id != ID_FORM ) { + if ( failpos ) *failpos = 12; + return PICO_PMV_ERROR_SIZE; + } + + if ( type != ID_LWO2 ) { + if ( type == ID_LWOB ) + return lwValidateObject5( filename, fp, failID, failpos ); + else { + if ( failpos ) *failpos = 12; + return PICO_PMV_ERROR_IDENT; + } + } + + return PICO_PMV_OK; +} diff --git a/tools/urt/libs/picomodel/lwo/lwo2.h b/tools/urt/libs/picomodel/lwo/lwo2.h new file mode 100644 index 00000000..e48e06ea --- /dev/null +++ b/tools/urt/libs/picomodel/lwo/lwo2.h @@ -0,0 +1,651 @@ +/* +====================================================================== +lwo2.h + +Definitions and typedefs for LWO2 files. + +Ernie Wright 17 Sep 00 +====================================================================== */ + +#ifndef LWO2_H +#define LWO2_H + +/* chunk and subchunk IDs */ + +#define LWID_(a,b,c,d) (((a)<<24)|((b)<<16)|((c)<<8)|(d)) + +#define ID_FORM LWID_('F','O','R','M') +#define ID_LWO2 LWID_('L','W','O','2') +#define ID_LWOB LWID_('L','W','O','B') + +/* top-level chunks */ +#define ID_LAYR LWID_('L','A','Y','R') +#define ID_TAGS LWID_('T','A','G','S') +#define ID_PNTS LWID_('P','N','T','S') +#define ID_BBOX LWID_('B','B','O','X') +#define ID_VMAP LWID_('V','M','A','P') +#define ID_VMAD LWID_('V','M','A','D') +#define ID_POLS LWID_('P','O','L','S') +#define ID_PTAG LWID_('P','T','A','G') +#define ID_ENVL LWID_('E','N','V','L') +#define ID_CLIP LWID_('C','L','I','P') +#define ID_SURF LWID_('S','U','R','F') +#define ID_DESC LWID_('D','E','S','C') +#define ID_TEXT LWID_('T','E','X','T') +#define ID_ICON LWID_('I','C','O','N') + +/* polygon types */ +#define ID_FACE LWID_('F','A','C','E') +#define ID_CURV LWID_('C','U','R','V') +#define ID_PTCH LWID_('P','T','C','H') +#define ID_MBAL LWID_('M','B','A','L') +#define ID_BONE LWID_('B','O','N','E') + +/* polygon tags */ +#define ID_SURF LWID_('S','U','R','F') +#define ID_PART LWID_('P','A','R','T') +#define ID_SMGP LWID_('S','M','G','P') + +/* envelopes */ +#define ID_PRE LWID_('P','R','E',' ') +#define ID_POST LWID_('P','O','S','T') +#define ID_KEY LWID_('K','E','Y',' ') +#define ID_SPAN LWID_('S','P','A','N') +#define ID_TCB LWID_('T','C','B',' ') +#define ID_HERM LWID_('H','E','R','M') +#define ID_BEZI LWID_('B','E','Z','I') +#define ID_BEZ2 LWID_('B','E','Z','2') +#define ID_LINE LWID_('L','I','N','E') +#define ID_STEP LWID_('S','T','E','P') + +/* clips */ +#define ID_STIL LWID_('S','T','I','L') +#define ID_ISEQ LWID_('I','S','E','Q') +#define ID_ANIM LWID_('A','N','I','M') +#define ID_XREF LWID_('X','R','E','F') +#define ID_STCC LWID_('S','T','C','C') +#define ID_TIME LWID_('T','I','M','E') +#define ID_CONT LWID_('C','O','N','T') +#define ID_BRIT LWID_('B','R','I','T') +#define ID_SATR LWID_('S','A','T','R') +#define ID_HUE LWID_('H','U','E',' ') +#define ID_GAMM LWID_('G','A','M','M') +#define ID_NEGA LWID_('N','E','G','A') +#define ID_IFLT LWID_('I','F','L','T') +#define ID_PFLT LWID_('P','F','L','T') + +/* surfaces */ +#define ID_COLR LWID_('C','O','L','R') +#define ID_LUMI LWID_('L','U','M','I') +#define ID_DIFF LWID_('D','I','F','F') +#define ID_SPEC LWID_('S','P','E','C') +#define ID_GLOS LWID_('G','L','O','S') +#define ID_REFL LWID_('R','E','F','L') +#define ID_RFOP LWID_('R','F','O','P') +#define ID_RIMG LWID_('R','I','M','G') +#define ID_RSAN LWID_('R','S','A','N') +#define ID_TRAN LWID_('T','R','A','N') +#define ID_TROP LWID_('T','R','O','P') +#define ID_TIMG LWID_('T','I','M','G') +#define ID_RIND LWID_('R','I','N','D') +#define ID_TRNL LWID_('T','R','N','L') +#define ID_BUMP LWID_('B','U','M','P') +#define ID_SMAN LWID_('S','M','A','N') +#define ID_SIDE LWID_('S','I','D','E') +#define ID_CLRH LWID_('C','L','R','H') +#define ID_CLRF LWID_('C','L','R','F') +#define ID_ADTR LWID_('A','D','T','R') +#define ID_SHRP LWID_('S','H','R','P') +#define ID_LINE LWID_('L','I','N','E') +#define ID_LSIZ LWID_('L','S','I','Z') +#define ID_ALPH LWID_('A','L','P','H') +#define ID_AVAL LWID_('A','V','A','L') +#define ID_GVAL LWID_('G','V','A','L') +#define ID_BLOK LWID_('B','L','O','K') + +/* texture layer */ +#define ID_TYPE LWID_('T','Y','P','E') +#define ID_CHAN LWID_('C','H','A','N') +#define ID_NAME LWID_('N','A','M','E') +#define ID_ENAB LWID_('E','N','A','B') +#define ID_OPAC LWID_('O','P','A','C') +#define ID_FLAG LWID_('F','L','A','G') +#define ID_PROJ LWID_('P','R','O','J') +#define ID_STCK LWID_('S','T','C','K') +#define ID_TAMP LWID_('T','A','M','P') + +/* texture coordinates */ +#define ID_TMAP LWID_('T','M','A','P') +#define ID_AXIS LWID_('A','X','I','S') +#define ID_CNTR LWID_('C','N','T','R') +#define ID_SIZE LWID_('S','I','Z','E') +#define ID_ROTA LWID_('R','O','T','A') +#define ID_OREF LWID_('O','R','E','F') +#define ID_FALL LWID_('F','A','L','L') +#define ID_CSYS LWID_('C','S','Y','S') + +/* image map */ +#define ID_IMAP LWID_('I','M','A','P') +#define ID_IMAG LWID_('I','M','A','G') +#define ID_WRAP LWID_('W','R','A','P') +#define ID_WRPW LWID_('W','R','P','W') +#define ID_WRPH LWID_('W','R','P','H') +#define ID_VMAP LWID_('V','M','A','P') +#define ID_AAST LWID_('A','A','S','T') +#define ID_PIXB LWID_('P','I','X','B') + +/* procedural */ +#define ID_PROC LWID_('P','R','O','C') +#define ID_COLR LWID_('C','O','L','R') +#define ID_VALU LWID_('V','A','L','U') +#define ID_FUNC LWID_('F','U','N','C') +#define ID_FTPS LWID_('F','T','P','S') +#define ID_ITPS LWID_('I','T','P','S') +#define ID_ETPS LWID_('E','T','P','S') + +/* gradient */ +#define ID_GRAD LWID_('G','R','A','D') +#define ID_GRST LWID_('G','R','S','T') +#define ID_GREN LWID_('G','R','E','N') +#define ID_PNAM LWID_('P','N','A','M') +#define ID_INAM LWID_('I','N','A','M') +#define ID_GRPT LWID_('G','R','P','T') +#define ID_FKEY LWID_('F','K','E','Y') +#define ID_IKEY LWID_('I','K','E','Y') + +/* shader */ +#define ID_SHDR LWID_('S','H','D','R') +#define ID_DATA LWID_('D','A','T','A') + + +/* generic linked list */ + +typedef struct st_lwNode { + struct st_lwNode *next, *prev; + void *data; +} lwNode; + + +/* plug-in reference */ + +typedef struct st_lwPlugin { + struct st_lwPlugin *next, *prev; + char *ord; + char *name; + int flags; + void *data; +} lwPlugin; + + +/* envelopes */ + +typedef struct st_lwKey { + struct st_lwKey *next, *prev; + float value; + float time; + unsigned int shape; /* ID_TCB, ID_BEZ2, etc. */ + float tension; + float continuity; + float bias; + float param[ 4 ]; +} lwKey; + +typedef struct st_lwEnvelope { + struct st_lwEnvelope *next, *prev; + int index; + int type; + char *name; + lwKey *key; /* linked list of keys */ + int nkeys; + int behavior[ 2 ]; /* pre and post (extrapolation) */ + lwPlugin *cfilter; /* linked list of channel filters */ + int ncfilters; +} lwEnvelope; + +#define BEH_RESET 0 +#define BEH_CONSTANT 1 +#define BEH_REPEAT 2 +#define BEH_OSCILLATE 3 +#define BEH_OFFSET 4 +#define BEH_LINEAR 5 + + +/* values that can be enveloped */ + +typedef struct st_lwEParam { + float val; + int eindex; +} lwEParam; + +typedef struct st_lwVParam { + float val[ 3 ]; + int eindex; +} lwVParam; + + +/* clips */ + +typedef struct st_lwClipStill { + char *name; +} lwClipStill; + +typedef struct st_lwClipSeq { + char *prefix; /* filename before sequence digits */ + char *suffix; /* after digits, e.g. extensions */ + int digits; + int flags; + int offset; + int start; + int end; +} lwClipSeq; + +typedef struct st_lwClipAnim { + char *name; + char *server; /* anim loader plug-in */ + void *data; +} lwClipAnim; + +typedef struct st_lwClipXRef { + char *string; + int index; + struct st_lwClip *clip; +} lwClipXRef; + +typedef struct st_lwClipCycle { + char *name; + int lo; + int hi; +} lwClipCycle; + +typedef struct st_lwClip { + struct st_lwClip *next, *prev; + int index; + unsigned int type; /* ID_STIL, ID_ISEQ, etc. */ + union { + lwClipStill still; + lwClipSeq seq; + lwClipAnim anim; + lwClipXRef xref; + lwClipCycle cycle; + } source; + float start_time; + float duration; + float frame_rate; + lwEParam contrast; + lwEParam brightness; + lwEParam saturation; + lwEParam hue; + lwEParam gamma; + int negative; + lwPlugin *ifilter; /* linked list of image filters */ + int nifilters; + lwPlugin *pfilter; /* linked list of pixel filters */ + int npfilters; +} lwClip; + + +/* textures */ + +typedef struct st_lwTMap { + lwVParam size; + lwVParam center; + lwVParam rotate; + lwVParam falloff; + int fall_type; + char *ref_object; + int coord_sys; +} lwTMap; + +typedef struct st_lwImageMap { + int cindex; + int projection; + char *vmap_name; + int axis; + int wrapw_type; + int wraph_type; + lwEParam wrapw; + lwEParam wraph; + float aa_strength; + int aas_flags; + int pblend; + lwEParam stck; + lwEParam amplitude; +} lwImageMap; + +#define PROJ_PLANAR 0 +#define PROJ_CYLINDRICAL 1 +#define PROJ_SPHERICAL 2 +#define PROJ_CUBIC 3 +#define PROJ_FRONT 4 + +#define WRAP_NONE 0 +#define WRAP_EDGE 1 +#define WRAP_REPEAT 2 +#define WRAP_MIRROR 3 + +typedef struct st_lwProcedural { + int axis; + float value[ 3 ]; + char *name; + void *data; +} lwProcedural; + +typedef struct st_lwGradKey { + struct st_lwGradKey *next, *prev; + float value; + float rgba[ 4 ]; +} lwGradKey; + +typedef struct st_lwGradient { + char *paramname; + char *itemname; + float start; + float end; + int repeat; + lwGradKey *key; /* array of gradient keys */ + short *ikey; /* array of interpolation codes */ +} lwGradient; + +typedef struct st_lwTexture { + struct st_lwTexture *next, *prev; + char *ord; + unsigned int type; + unsigned int chan; + lwEParam opacity; + short opac_type; + short enabled; + short negative; + short axis; + union { + lwImageMap imap; + lwProcedural proc; + lwGradient grad; + } param; + lwTMap tmap; +} lwTexture; + + +/* values that can be textured */ + +typedef struct st_lwTParam { + float val; + int eindex; + lwTexture *tex; /* linked list of texture layers */ +} lwTParam; + +typedef struct st_lwCParam { + float rgb[ 3 ]; + int eindex; + lwTexture *tex; /* linked list of texture layers */ +} lwCParam; + + +/* surfaces */ + +typedef struct st_lwGlow { + short enabled; + short type; + lwEParam intensity; + lwEParam size; +} Glow; + +typedef struct st_lwRMap { + lwTParam val; + int options; + int cindex; + float seam_angle; +} lwRMap; + +typedef struct st_lwLine { + short enabled; + unsigned short flags; + lwEParam size; +} lwLine; + +typedef struct st_lwSurface { + struct st_lwSurface *next, *prev; + char *name; + char *srcname; + lwCParam color; + lwTParam luminosity; + lwTParam diffuse; + lwTParam specularity; + lwTParam glossiness; + lwRMap reflection; + lwRMap transparency; + lwTParam eta; + lwTParam translucency; + lwTParam bump; + float smooth; + int sideflags; + float alpha; + int alpha_mode; + lwEParam color_hilite; + lwEParam color_filter; + lwEParam add_trans; + lwEParam dif_sharp; + lwEParam glow; + lwLine line; + lwPlugin *shader; /* linked list of shaders */ + int nshaders; +} lwSurface; + + +/* vertex maps */ + +typedef struct st_lwVMap { + struct st_lwVMap *next, *prev; + char *name; + unsigned int type; + int dim; + int nverts; + int perpoly; + int *vindex; /* array of point indexes */ + int *pindex; /* array of polygon indexes */ + float **val; +} lwVMap; + +typedef struct st_lwVMapPt { + lwVMap *vmap; + int index; /* vindex or pindex element */ +} lwVMapPt; + + +/* points and polygons */ + +typedef struct st_lwPoint { + float pos[ 3 ]; + int npols; /* number of polygons sharing the point */ + int *pol; /* array of polygon indexes */ + int nvmaps; + lwVMapPt *vm; /* array of vmap references */ +} lwPoint; + +typedef struct st_lwPolVert { + int index; /* index into the point array */ + float norm[ 3 ]; + int nvmaps; + lwVMapPt *vm; /* array of vmap references */ +} lwPolVert; + +typedef struct st_lwPolygon { + lwSurface *surf; + int part; /* part index */ + int smoothgrp; /* smoothing group */ + int flags; + unsigned int type; + float norm[ 3 ]; + int nverts; + lwPolVert *v; /* array of vertex records */ +} lwPolygon; + +typedef struct st_lwPointList { + int count; + int offset; /* only used during reading */ + lwPoint *pt; /* array of points */ +} lwPointList; + +typedef struct st_lwPolygonList { + int count; + int offset; /* only used during reading */ + int vcount; /* total number of vertices */ + int voffset; /* only used during reading */ + lwPolygon *pol; /* array of polygons */ +} lwPolygonList; + + +/* geometry layers */ + +typedef struct st_lwLayer { + struct st_lwLayer *next, *prev; + char *name; + int index; + int parent; + int flags; + float pivot[ 3 ]; + float bbox[ 6 ]; + lwPointList point; + lwPolygonList polygon; + int nvmaps; + lwVMap *vmap; /* linked list of vmaps */ +} lwLayer; + + +/* tag strings */ + +typedef struct st_lwTagList { + int count; + int offset; /* only used during reading */ + char **tag; /* array of strings */ +} lwTagList; + + +/* an object */ + +typedef struct st_lwObject { + lwLayer *layer; /* linked list of layers */ + lwEnvelope *env; /* linked list of envelopes */ + lwClip *clip; /* linked list of clips */ + lwSurface *surf; /* linked list of surfaces */ + lwTagList taglist; + int nlayers; + int nenvs; + int nclips; + int nsurfs; +} lwObject; + + +/* lwo2.c */ + +void lwFreeLayer( lwLayer *layer ); +void lwFreeObject( lwObject *object ); +lwObject *lwGetObject( char *filename, picoMemStream_t *fp, unsigned int *failID, int *failpos ); +int lwValidateObject( char *filename, picoMemStream_t *fp, unsigned int *failID, int *failpos ); + +/* pntspols.c */ + +void lwFreePoints( lwPointList *point ); +void lwFreePolygons( lwPolygonList *plist ); +int lwGetPoints( picoMemStream_t *fp, int cksize, lwPointList *point ); +void lwGetBoundingBox( lwPointList *point, float bbox[] ); +int lwAllocPolygons( lwPolygonList *plist, int npols, int nverts ); +int lwGetPolygons( picoMemStream_t *fp, int cksize, lwPolygonList *plist, int ptoffset ); +void lwGetPolyNormals( lwPointList *point, lwPolygonList *polygon ); +int lwGetPointPolygons( lwPointList *point, lwPolygonList *polygon ); +int lwResolvePolySurfaces( lwPolygonList *polygon, lwTagList *tlist, + lwSurface **surf, int *nsurfs ); +void lwGetVertNormals( lwPointList *point, lwPolygonList *polygon ); +void lwFreeTags( lwTagList *tlist ); +int lwGetTags( picoMemStream_t *fp, int cksize, lwTagList *tlist ); +int lwGetPolygonTags( picoMemStream_t *fp, int cksize, lwTagList *tlist, + lwPolygonList *plist ); + +/* vmap.c */ + +void lwFreeVMap( lwVMap *vmap ); +lwVMap *lwGetVMap( picoMemStream_t *fp, int cksize, int ptoffset, int poloffset, + int perpoly ); +int lwGetPointVMaps( lwPointList *point, lwVMap *vmap ); +int lwGetPolyVMaps( lwPolygonList *polygon, lwVMap *vmap ); + +/* clip.c */ + +void lwFreeClip( lwClip *clip ); +lwClip *lwGetClip( picoMemStream_t *fp, int cksize ); +lwClip *lwFindClip( lwClip *list, int index ); + +/* envelope.c */ + +void lwFreeEnvelope( lwEnvelope *env ); +lwEnvelope *lwGetEnvelope( picoMemStream_t *fp, int cksize ); +lwEnvelope *lwFindEnvelope( lwEnvelope *list, int index ); +float lwEvalEnvelope( lwEnvelope *env, float time ); + +/* surface.c */ + +void lwFreePlugin( lwPlugin *p ); +void lwFreeTexture( lwTexture *t ); +void lwFreeSurface( lwSurface *surf ); +int lwGetTHeader( picoMemStream_t *fp, int hsz, lwTexture *tex ); +int lwGetTMap( picoMemStream_t *fp, int tmapsz, lwTMap *tmap ); +int lwGetImageMap( picoMemStream_t *fp, int rsz, lwTexture *tex ); +int lwGetProcedural( picoMemStream_t *fp, int rsz, lwTexture *tex ); +int lwGetGradient( picoMemStream_t *fp, int rsz, lwTexture *tex ); +lwTexture *lwGetTexture( picoMemStream_t *fp, int bloksz, unsigned int type ); +lwPlugin *lwGetShader( picoMemStream_t *fp, int bloksz ); +lwSurface *lwGetSurface( picoMemStream_t *fp, int cksize ); +lwSurface *lwDefaultSurface( void ); + +/* lwob.c */ + +lwSurface *lwGetSurface5( picoMemStream_t *fp, int cksize, lwObject *obj ); +int lwGetPolygons5( picoMemStream_t *fp, int cksize, lwPolygonList *plist, int ptoffset ); +lwObject *lwGetObject5( char *filename, picoMemStream_t *fp, unsigned int *failID, int *failpos ); +int lwValidateObject5( char *filename, picoMemStream_t *fp, unsigned int *failID, int *failpos ); + +/* list.c */ + +void lwListFree( void *list, void ( *freeNode )( void * )); +void lwListAdd( void **list, void *node ); +void lwListInsert( void **vlist, void *vitem, + int ( *compare )( void *, void * )); + +/* vecmath.c */ + +float dot( float a[], float b[] ); +void cross( float a[], float b[], float c[] ); +void normalize( float v[] ); +#define vecangle( a, b ) ( float ) acos( dot( a, b )) + +/* lwio.c */ + +void set_flen( int i ); +int get_flen( void ); +void *getbytes( picoMemStream_t *fp, int size ); +void skipbytes( picoMemStream_t *fp, int n ); +int getI1( picoMemStream_t *fp ); +short getI2( picoMemStream_t *fp ); +int getI4( picoMemStream_t *fp ); +unsigned char getU1( picoMemStream_t *fp ); +unsigned short getU2( picoMemStream_t *fp ); +unsigned int getU4( picoMemStream_t *fp ); +int getVX( picoMemStream_t *fp ); +float getF4( picoMemStream_t *fp ); +char *getS0( picoMemStream_t *fp ); +int sgetI1( unsigned char **bp ); +short sgetI2( unsigned char **bp ); +int sgetI4( unsigned char **bp ); +unsigned char sgetU1( unsigned char **bp ); +unsigned short sgetU2( unsigned char **bp ); +unsigned int sgetU4( unsigned char **bp ); +int sgetVX( unsigned char **bp ); +float sgetF4( unsigned char **bp ); +char *sgetS0( unsigned char **bp ); + +#ifndef __BIG_ENDIAN__ + void revbytes( void *bp, int elsize, int elcount ); +#else + #define revbytes( b, s, c ) +#endif + +#endif diff --git a/tools/urt/libs/picomodel/lwo/lwob.c b/tools/urt/libs/picomodel/lwo/lwob.c new file mode 100644 index 00000000..e97e5268 --- /dev/null +++ b/tools/urt/libs/picomodel/lwo/lwob.c @@ -0,0 +1,723 @@ +/* +====================================================================== +lwob.c + +Functions for an LWOB reader. LWOB is the LightWave object format +for versions of LW prior to 6.0. + +Ernie Wright 17 Sep 00 +====================================================================== */ + +#include "../picointernal.h" +#include "lwo2.h" + +/* disable warnings */ +#ifdef WIN32 +#pragma warning( disable:4018 ) /* signed/unsigned mismatch */ +#endif + + +/* IDs specific to LWOB */ + +#define ID_SRFS LWID_('S','R','F','S') +#define ID_FLAG LWID_('F','L','A','G') +#define ID_VLUM LWID_('V','L','U','M') +#define ID_VDIF LWID_('V','D','I','F') +#define ID_VSPC LWID_('V','S','P','C') +#define ID_RFLT LWID_('R','F','L','T') +#define ID_BTEX LWID_('B','T','E','X') +#define ID_CTEX LWID_('C','T','E','X') +#define ID_DTEX LWID_('D','T','E','X') +#define ID_LTEX LWID_('L','T','E','X') +#define ID_RTEX LWID_('R','T','E','X') +#define ID_STEX LWID_('S','T','E','X') +#define ID_TTEX LWID_('T','T','E','X') +#define ID_TFLG LWID_('T','F','L','G') +#define ID_TSIZ LWID_('T','S','I','Z') +#define ID_TCTR LWID_('T','C','T','R') +#define ID_TFAL LWID_('T','F','A','L') +#define ID_TVEL LWID_('T','V','E','L') +#define ID_TCLR LWID_('T','C','L','R') +#define ID_TVAL LWID_('T','V','A','L') +#define ID_TAMP LWID_('T','A','M','P') +#define ID_TIMG LWID_('T','I','M','G') +#define ID_TAAS LWID_('T','A','A','S') +#define ID_TREF LWID_('T','R','E','F') +#define ID_TOPC LWID_('T','O','P','C') +#define ID_SDAT LWID_('S','D','A','T') +#define ID_TFP0 LWID_('T','F','P','0') +#define ID_TFP1 LWID_('T','F','P','1') + + +/* +====================================================================== +add_clip() + +Add a clip to the clip list. Used to store the contents of an RIMG or +TIMG surface subchunk. +====================================================================== */ + +static int add_clip( char *s, lwClip **clist, int *nclips ) +{ + lwClip *clip; + char *p; + + clip = _pico_calloc( 1, sizeof( lwClip )); + if ( !clip ) return 0; + + clip->contrast.val = 1.0f; + clip->brightness.val = 1.0f; + clip->saturation.val = 1.0f; + clip->gamma.val = 1.0f; + + if ( p = strstr( s, "(sequence)" )) { + p[ -1 ] = 0; + clip->type = ID_ISEQ; + clip->source.seq.prefix = s; + clip->source.seq.digits = 3; + } + else { + clip->type = ID_STIL; + clip->source.still.name = s; + } + + *nclips++; + clip->index = *nclips; + + lwListAdd( (void *) clist, clip ); + + return clip->index; +} + + +/* +====================================================================== +add_tvel() + +Add a triple of envelopes to simulate the old texture velocity +parameters. +====================================================================== */ + +static int add_tvel( float pos[], float vel[], lwEnvelope **elist, int *nenvs ) +{ + lwEnvelope *env; + lwKey *key0, *key1; + int i; + + for ( i = 0; i < 3; i++ ) { + env = _pico_calloc( 1, sizeof( lwEnvelope )); + key0 = _pico_calloc( 1, sizeof( lwKey )); + key1 = _pico_calloc( 1, sizeof( lwKey )); + if ( !env || !key0 || !key1 ) return 0; + + key0->next = key1; + key0->value = pos[ i ]; + key0->time = 0.0f; + key1->prev = key0; + key1->value = pos[ i ] + vel[ i ] * 30.0f; + key1->time = 1.0f; + key0->shape = key1->shape = ID_LINE; + + env->index = *nenvs + i + 1; + env->type = 0x0301 + i; + env->name = _pico_alloc( 11 ); + if ( env->name ) { + strcpy( env->name, "Position.X" ); + env->name[ 9 ] += i; + } + env->key = key0; + env->nkeys = 2; + env->behavior[ 0 ] = BEH_LINEAR; + env->behavior[ 1 ] = BEH_LINEAR; + + lwListAdd( (void *) elist, env ); + } + + *nenvs += 3; + return env->index - 2; +} + + +/* +====================================================================== +get_texture() + +Create a new texture for BTEX, CTEX, etc. subchunks. +====================================================================== */ + +static lwTexture *get_texture( char *s ) +{ + lwTexture *tex; + + tex = _pico_calloc( 1, sizeof( lwTexture )); + if ( !tex ) return NULL; + + tex->tmap.size.val[ 0 ] = + tex->tmap.size.val[ 1 ] = + tex->tmap.size.val[ 2 ] = 1.0f; + tex->opacity.val = 1.0f; + tex->enabled = 1; + + if ( strstr( s, "Image Map" )) { + tex->type = ID_IMAP; + if ( strstr( s, "Planar" )) tex->param.imap.projection = 0; + else if ( strstr( s, "Cylindrical" )) tex->param.imap.projection = 1; + else if ( strstr( s, "Spherical" )) tex->param.imap.projection = 2; + else if ( strstr( s, "Cubic" )) tex->param.imap.projection = 3; + else if ( strstr( s, "Front" )) tex->param.imap.projection = 4; + tex->param.imap.aa_strength = 1.0f; + tex->param.imap.amplitude.val = 1.0f; + _pico_free( s ); + } + else { + tex->type = ID_PROC; + tex->param.proc.name = s; + } + + return tex; +} + + +/* +====================================================================== +lwGetSurface5() + +Read an lwSurface from an LWOB file. +====================================================================== */ + +lwSurface *lwGetSurface5( picoMemStream_t *fp, int cksize, lwObject *obj ) +{ + lwSurface *surf; + lwTexture *tex; + lwPlugin *shdr; + char *s; + float v[ 3 ]; + unsigned int id, flags; + unsigned short sz; + int pos, rlen, i; + + + /* allocate the Surface structure */ + + surf = _pico_calloc( 1, sizeof( lwSurface )); + if ( !surf ) goto Fail; + + /* non-zero defaults */ + + surf->color.rgb[ 0 ] = 0.78431f; + surf->color.rgb[ 1 ] = 0.78431f; + surf->color.rgb[ 2 ] = 0.78431f; + surf->diffuse.val = 1.0f; + surf->glossiness.val = 0.4f; + surf->bump.val = 1.0f; + surf->eta.val = 1.0f; + surf->sideflags = 1; + + /* remember where we started */ + + set_flen( 0 ); + pos = _pico_memstream_tell( fp ); + + /* name */ + + surf->name = getS0( fp ); + + /* first subchunk header */ + + id = getU4( fp ); + sz = getU2( fp ); + if ( 0 > get_flen() ) goto Fail; + + /* process subchunks as they're encountered */ + + while ( 1 ) { + sz += sz & 1; + set_flen( 0 ); + + switch ( id ) { + case ID_COLR: + surf->color.rgb[ 0 ] = getU1( fp ) / 255.0f; + surf->color.rgb[ 1 ] = getU1( fp ) / 255.0f; + surf->color.rgb[ 2 ] = getU1( fp ) / 255.0f; + break; + + case ID_FLAG: + flags = getU2( fp ); + if ( flags & 4 ) surf->smooth = 1.56207f; + if ( flags & 8 ) surf->color_hilite.val = 1.0f; + if ( flags & 16 ) surf->color_filter.val = 1.0f; + if ( flags & 128 ) surf->dif_sharp.val = 0.5f; + if ( flags & 256 ) surf->sideflags = 3; + if ( flags & 512 ) surf->add_trans.val = 1.0f; + break; + + case ID_LUMI: + surf->luminosity.val = getI2( fp ) / 256.0f; + break; + + case ID_VLUM: + surf->luminosity.val = getF4( fp ); + break; + + case ID_DIFF: + surf->diffuse.val = getI2( fp ) / 256.0f; + break; + + case ID_VDIF: + surf->diffuse.val = getF4( fp ); + break; + + case ID_SPEC: + surf->specularity.val = getI2( fp ) / 256.0f; + break; + + case ID_VSPC: + surf->specularity.val = getF4( fp ); + break; + + case ID_GLOS: + surf->glossiness.val = ( float ) log( getU2( fp )) / 20.7944f; + break; + + case ID_SMAN: + surf->smooth = getF4( fp ); + break; + + case ID_REFL: + surf->reflection.val.val = getI2( fp ) / 256.0f; + break; + + case ID_RFLT: + surf->reflection.options = getU2( fp ); + break; + + case ID_RIMG: + s = getS0( fp ); + surf->reflection.cindex = add_clip( s, &obj->clip, &obj->nclips ); + surf->reflection.options = 3; + break; + + case ID_RSAN: + surf->reflection.seam_angle = getF4( fp ); + break; + + case ID_TRAN: + surf->transparency.val.val = getI2( fp ) / 256.0f; + break; + + case ID_RIND: + surf->eta.val = getF4( fp ); + break; + + case ID_BTEX: + s = getbytes( fp, sz ); + tex = get_texture( s ); + lwListAdd( (void *) &surf->bump.tex, tex ); + break; + + case ID_CTEX: + s = getbytes( fp, sz ); + tex = get_texture( s ); + lwListAdd( (void *) &surf->color.tex, tex ); + break; + + case ID_DTEX: + s = getbytes( fp, sz ); + tex = get_texture( s ); + lwListAdd( (void *) &surf->diffuse.tex, tex ); + break; + + case ID_LTEX: + s = getbytes( fp, sz ); + tex = get_texture( s ); + lwListAdd( (void *) &surf->luminosity.tex, tex ); + break; + + case ID_RTEX: + s = getbytes( fp, sz ); + tex = get_texture( s ); + lwListAdd( (void *) &surf->reflection.val.tex, tex ); + break; + + case ID_STEX: + s = getbytes( fp, sz ); + tex = get_texture( s ); + lwListAdd( (void *) &surf->specularity.tex, tex ); + break; + + case ID_TTEX: + s = getbytes( fp, sz ); + tex = get_texture( s ); + lwListAdd( (void *) &surf->transparency.val.tex, tex ); + break; + + case ID_TFLG: + flags = getU2( fp ); + + if ( flags & 1 ) i = 0; + if ( flags & 2 ) i = 1; + if ( flags & 4 ) i = 2; + tex->axis = i; + if ( tex->type == ID_IMAP ) + tex->param.imap.axis = i; + else + tex->param.proc.axis = i; + + if ( flags & 8 ) tex->tmap.coord_sys = 1; + if ( flags & 16 ) tex->negative = 1; + if ( flags & 32 ) tex->param.imap.pblend = 1; + if ( flags & 64 ) { + tex->param.imap.aa_strength = 1.0f; + tex->param.imap.aas_flags = 1; + } + break; + + case ID_TSIZ: + for ( i = 0; i < 3; i++ ) + tex->tmap.size.val[ i ] = getF4( fp ); + break; + + case ID_TCTR: + for ( i = 0; i < 3; i++ ) + tex->tmap.center.val[ i ] = getF4( fp ); + break; + + case ID_TFAL: + for ( i = 0; i < 3; i++ ) + tex->tmap.falloff.val[ i ] = getF4( fp ); + break; + + case ID_TVEL: + for ( i = 0; i < 3; i++ ) + v[ i ] = getF4( fp ); + tex->tmap.center.eindex = add_tvel( tex->tmap.center.val, v, + &obj->env, &obj->nenvs ); + break; + + case ID_TCLR: + if ( tex->type == ID_PROC ) + for ( i = 0; i < 3; i++ ) + tex->param.proc.value[ i ] = getU1( fp ) / 255.0f; + break; + + case ID_TVAL: + tex->param.proc.value[ 0 ] = getI2( fp ) / 256.0f; + break; + + case ID_TAMP: + if ( tex->type == ID_IMAP ) + tex->param.imap.amplitude.val = getF4( fp ); + break; + + case ID_TIMG: + s = getS0( fp ); + tex->param.imap.cindex = add_clip( s, &obj->clip, &obj->nclips ); + break; + + case ID_TAAS: + tex->param.imap.aa_strength = getF4( fp ); + tex->param.imap.aas_flags = 1; + break; + + case ID_TREF: + tex->tmap.ref_object = getbytes( fp, sz ); + break; + + case ID_TOPC: + tex->opacity.val = getF4( fp ); + break; + + case ID_TFP0: + if ( tex->type == ID_IMAP ) + tex->param.imap.wrapw.val = getF4( fp ); + break; + + case ID_TFP1: + if ( tex->type == ID_IMAP ) + tex->param.imap.wraph.val = getF4( fp ); + break; + + case ID_SHDR: + shdr = _pico_calloc( 1, sizeof( lwPlugin )); + if ( !shdr ) goto Fail; + shdr->name = getbytes( fp, sz ); + lwListAdd( (void *) &surf->shader, shdr ); + surf->nshaders++; + break; + + case ID_SDAT: + shdr->data = getbytes( fp, sz ); + break; + + default: + break; + } + + /* error while reading current subchunk? */ + + rlen = get_flen(); + if ( rlen < 0 || rlen > sz ) goto Fail; + + /* skip unread parts of the current subchunk */ + + if ( rlen < sz ) + _pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR ); + + /* end of the SURF chunk? */ + + if ( cksize <= _pico_memstream_tell( fp ) - pos ) + break; + + /* get the next subchunk header */ + + set_flen( 0 ); + id = getU4( fp ); + sz = getU2( fp ); + if ( 6 != get_flen() ) goto Fail; + } + + return surf; + +Fail: + if ( surf ) lwFreeSurface( surf ); + return NULL; +} + + +/* +====================================================================== +lwGetPolygons5() + +Read polygon records from a POLS chunk in an LWOB file. The polygons +are added to the array in the lwPolygonList. +====================================================================== */ + +int lwGetPolygons5( picoMemStream_t *fp, int cksize, lwPolygonList *plist, int ptoffset ) +{ + lwPolygon *pp; + lwPolVert *pv; + unsigned char *buf, *bp; + int i, j, nv, nverts, npols; + + + if ( cksize == 0 ) return 1; + + /* read the whole chunk */ + + set_flen( 0 ); + buf = getbytes( fp, cksize ); + if ( !buf ) goto Fail; + + /* count the polygons and vertices */ + + nverts = 0; + npols = 0; + bp = buf; + + while ( bp < buf + cksize ) { + nv = sgetU2( &bp ); + nverts += nv; + npols++; + bp += 2 * nv; + i = sgetI2( &bp ); + if ( i < 0 ) bp += 2; /* detail polygons */ + } + + if ( !lwAllocPolygons( plist, npols, nverts )) + goto Fail; + + /* fill in the new polygons */ + + bp = buf; + pp = plist->pol + plist->offset; + pv = plist->pol[ 0 ].v + plist->voffset; + + for ( i = 0; i < npols; i++ ) { + nv = sgetU2( &bp ); + + pp->nverts = nv; + pp->type = ID_FACE; + if ( !pp->v ) pp->v = pv; + for ( j = 0; j < nv; j++ ) + pv[ j ].index = sgetU2( &bp ) + ptoffset; + j = sgetI2( &bp ); + if ( j < 0 ) { + j = -j; + bp += 2; + } + j -= 1; + pp->surf = ( lwSurface * ) j; + + pp++; + pv += nv; + } + + _pico_free( buf ); + return 1; + +Fail: + if ( buf ) _pico_free( buf ); + lwFreePolygons( plist ); + return 0; +} + + +/* +====================================================================== +getLWObject5() + +Returns the contents of an LWOB, given its filename, or NULL if the +file couldn't be loaded. On failure, failID and failpos can be used +to diagnose the cause. + +1. If the file isn't an LWOB, failpos will contain 12 and failID will + be unchanged. + +2. If an error occurs while reading an LWOB, failID will contain the + most recently read IFF chunk ID, and failpos will contain the + value returned by _pico_memstream_tell() at the time of the failure. + +3. If the file couldn't be opened, or an error occurs while reading + the first 12 bytes, both failID and failpos will be unchanged. + +If you don't need this information, failID and failpos can be NULL. +====================================================================== */ + +lwObject *lwGetObject5( char *filename, picoMemStream_t *fp, unsigned int *failID, int *failpos ) +{ + lwObject *object; + lwLayer *layer; + lwNode *node; + unsigned int id, formsize, type, cksize; + + + /* open the file */ + + if ( !fp ) return NULL; + + /* read the first 12 bytes */ + + set_flen( 0 ); + id = getU4( fp ); + formsize = getU4( fp ); + type = getU4( fp ); + if ( 12 != get_flen() ) { + return NULL; + } + + /* LWOB? */ + + if ( id != ID_FORM || type != ID_LWOB ) { + if ( failpos ) *failpos = 12; + return NULL; + } + + /* allocate an object and a default layer */ + + object = _pico_calloc( 1, sizeof( lwObject )); + if ( !object ) goto Fail; + + layer = _pico_calloc( 1, sizeof( lwLayer )); + if ( !layer ) goto Fail; + object->layer = layer; + object->nlayers = 1; + + /* get the first chunk header */ + + id = getU4( fp ); + cksize = getU4( fp ); + if ( 0 > get_flen() ) goto Fail; + + /* process chunks as they're encountered */ + + while ( 1 ) { + cksize += cksize & 1; + + switch ( id ) + { + case ID_PNTS: + if ( !lwGetPoints( fp, cksize, &layer->point )) + goto Fail; + break; + + case ID_POLS: + if ( !lwGetPolygons5( fp, cksize, &layer->polygon, + layer->point.offset )) + goto Fail; + break; + + case ID_SRFS: + if ( !lwGetTags( fp, cksize, &object->taglist )) + goto Fail; + break; + + case ID_SURF: + node = ( lwNode * ) lwGetSurface5( fp, cksize, object ); + if ( !node ) goto Fail; + lwListAdd( (void *) &object->surf, node ); + object->nsurfs++; + break; + + default: + _pico_memstream_seek( fp, cksize, PICO_SEEK_CUR ); + break; + } + + /* end of the file? */ + + if ( formsize <= _pico_memstream_tell( fp ) - 8 ) break; + + /* get the next chunk header */ + + set_flen( 0 ); + id = getU4( fp ); + cksize = getU4( fp ); + if ( 8 != get_flen() ) goto Fail; + } + + lwGetBoundingBox( &layer->point, layer->bbox ); + lwGetPolyNormals( &layer->point, &layer->polygon ); + if ( !lwGetPointPolygons( &layer->point, &layer->polygon )) goto Fail; + if ( !lwResolvePolySurfaces( &layer->polygon, &object->taglist, + &object->surf, &object->nsurfs )) goto Fail; + lwGetVertNormals( &layer->point, &layer->polygon ); + + return object; + +Fail: + if ( failID ) *failID = id; + if ( fp ) { + if ( failpos ) *failpos = _pico_memstream_tell( fp ); + } + lwFreeObject( object ); + return NULL; +} + +int lwValidateObject5( char *filename, picoMemStream_t *fp, unsigned int *failID, int *failpos ) +{ + unsigned int id, formsize, type; + + + /* open the file */ + + if ( !fp ) return PICO_PMV_ERROR_MEMORY; + + /* read the first 12 bytes */ + + set_flen( 0 ); + id = getU4( fp ); + formsize = getU4( fp ); + type = getU4( fp ); + if ( 12 != get_flen() ) { + return PICO_PMV_ERROR_SIZE; + } + + /* LWOB? */ + + if ( id != ID_FORM || type != ID_LWOB ) { + if ( failpos ) *failpos = 12; + return PICO_PMV_ERROR_IDENT; + } + + return PICO_PMV_OK; +} diff --git a/tools/urt/libs/picomodel/lwo/pntspols.c b/tools/urt/libs/picomodel/lwo/pntspols.c new file mode 100644 index 00000000..f38f6ea7 --- /dev/null +++ b/tools/urt/libs/picomodel/lwo/pntspols.c @@ -0,0 +1,537 @@ +/* +====================================================================== +pntspols.c + +Point and polygon functions for an LWO2 reader. + +Ernie Wright 17 Sep 00 +====================================================================== */ + +#include "../picointernal.h" +#include "lwo2.h" + + +/* +====================================================================== +lwFreePoints() + +Free the memory used by an lwPointList. +====================================================================== */ + +void lwFreePoints( lwPointList *point ) +{ + int i; + + if ( point ) { + if ( point->pt ) { + for ( i = 0; i < point->count; i++ ) { + if ( point->pt[ i ].pol ) _pico_free( point->pt[ i ].pol ); + if ( point->pt[ i ].vm ) _pico_free( point->pt[ i ].vm ); + } + _pico_free( point->pt ); + } + memset( point, 0, sizeof( lwPointList )); + } +} + + +/* +====================================================================== +lwFreePolygons() + +Free the memory used by an lwPolygonList. +====================================================================== */ + +void lwFreePolygons( lwPolygonList *plist ) +{ + int i, j; + + if ( plist ) { + if ( plist->pol ) { + for ( i = 0; i < plist->count; i++ ) { + if ( plist->pol[ i ].v ) { + for ( j = 0; j < plist->pol[ i ].nverts; j++ ) + if ( plist->pol[ i ].v[ j ].vm ) + _pico_free( plist->pol[ i ].v[ j ].vm ); + } + } + if ( plist->pol[ 0 ].v ) + _pico_free( plist->pol[ 0 ].v ); + _pico_free( plist->pol ); + } + memset( plist, 0, sizeof( lwPolygonList )); + } +} + + +/* +====================================================================== +lwGetPoints() + +Read point records from a PNTS chunk in an LWO2 file. The points are +added to the array in the lwPointList. +====================================================================== */ + +int lwGetPoints( picoMemStream_t *fp, int cksize, lwPointList *point ) +{ + float *f; + int np, i, j; + + if ( cksize == 1 ) return 1; + + /* extend the point array to hold the new points */ + + np = cksize / 12; + point->offset = point->count; + point->count += np; + if ( !_pico_realloc( (void *) &point->pt, (point->count - np) * sizeof( lwPoint ), point->count * sizeof( lwPoint )) ) + return 0; + memset( &point->pt[ point->offset ], 0, np * sizeof( lwPoint )); + + /* read the whole chunk */ + + f = ( float * ) getbytes( fp, cksize ); + if ( !f ) return 0; + revbytes( f, 4, np * 3 ); + + /* assign position values */ + + for ( i = 0, j = 0; i < np; i++, j += 3 ) { + point->pt[ i ].pos[ 0 ] = f[ j ]; + point->pt[ i ].pos[ 1 ] = f[ j + 1 ]; + point->pt[ i ].pos[ 2 ] = f[ j + 2 ]; + } + + _pico_free( f ); + return 1; +} + + +/* +====================================================================== +lwGetBoundingBox() + +Calculate the bounding box for a point list, but only if the bounding +box hasn't already been initialized. +====================================================================== */ + +void lwGetBoundingBox( lwPointList *point, float bbox[] ) +{ + int i, j; + + if ( point->count == 0 ) return; + + for ( i = 0; i < 6; i++ ) + if ( bbox[ i ] != 0.0f ) return; + + bbox[ 0 ] = bbox[ 1 ] = bbox[ 2 ] = 1e20f; + bbox[ 3 ] = bbox[ 4 ] = bbox[ 5 ] = -1e20f; + for ( i = 0; i < point->count; i++ ) { + for ( j = 0; j < 3; j++ ) { + if ( bbox[ j ] > point->pt[ i ].pos[ j ] ) + bbox[ j ] = point->pt[ i ].pos[ j ]; + if ( bbox[ j + 3 ] < point->pt[ i ].pos[ j ] ) + bbox[ j + 3 ] = point->pt[ i ].pos[ j ]; + } + } +} + + +/* +====================================================================== +lwAllocPolygons() + +Allocate or extend the polygon arrays to hold new records. +====================================================================== */ + +int lwAllocPolygons( lwPolygonList *plist, int npols, int nverts ) +{ + int i; + + plist->offset = plist->count; + plist->count += npols; + if ( !_pico_realloc( (void *) &plist->pol, (plist->count - npols) * sizeof( lwPolygon ), plist->count * sizeof( lwPolygon )) ) + return 0; + memset( plist->pol + plist->offset, 0, npols * sizeof( lwPolygon )); + + plist->voffset = plist->vcount; + plist->vcount += nverts; + if ( !_pico_realloc( (void *) &plist->pol[ 0 ].v, (plist->vcount - nverts) * sizeof( lwPolVert ), plist->vcount * sizeof( lwPolVert )) ) + return 0; + memset( plist->pol[ 0 ].v + plist->voffset, 0, nverts * sizeof( lwPolVert )); + + /* fix up the old vertex pointers */ + + for ( i = 1; i < plist->offset; i++ ) + plist->pol[ i ].v = plist->pol[ i - 1 ].v + plist->pol[ i - 1 ].nverts; + + return 1; +} + + +/* +====================================================================== +lwGetPolygons() + +Read polygon records from a POLS chunk in an LWO2 file. The polygons +are added to the array in the lwPolygonList. +====================================================================== */ + +int lwGetPolygons( picoMemStream_t *fp, int cksize, lwPolygonList *plist, int ptoffset ) +{ + lwPolygon *pp; + lwPolVert *pv; + unsigned char *buf, *bp; + int i, j, flags, nv, nverts, npols; + unsigned int type; + + + if ( cksize == 0 ) return 1; + + /* read the whole chunk */ + + set_flen( 0 ); + type = getU4( fp ); + buf = getbytes( fp, cksize - 4 ); + if ( cksize != get_flen() ) goto Fail; + + /* count the polygons and vertices */ + + nverts = 0; + npols = 0; + bp = buf; + + while ( bp < buf + cksize - 4 ) { + nv = sgetU2( &bp ); + nv &= 0x03FF; + nverts += nv; + npols++; + for ( i = 0; i < nv; i++ ) + j = sgetVX( &bp ); + } + + if ( !lwAllocPolygons( plist, npols, nverts )) + goto Fail; + + /* fill in the new polygons */ + + bp = buf; + pp = plist->pol + plist->offset; + pv = plist->pol[ 0 ].v + plist->voffset; + + for ( i = 0; i < npols; i++ ) { + nv = sgetU2( &bp ); + flags = nv & 0xFC00; + nv &= 0x03FF; + + pp->nverts = nv; + pp->flags = flags; + pp->type = type; + if ( !pp->v ) pp->v = pv; + for ( j = 0; j < nv; j++ ) + pp->v[ j ].index = sgetVX( &bp ) + ptoffset; + + pp++; + pv += nv; + } + + _pico_free( buf ); + return 1; + +Fail: + if ( buf ) _pico_free( buf ); + lwFreePolygons( plist ); + return 0; +} + + +/* +====================================================================== +lwGetPolyNormals() + +Calculate the polygon normals. By convention, LW's polygon normals +are found as the cross product of the first and last edges. It's +undefined for one- and two-point polygons. +====================================================================== */ + +void lwGetPolyNormals( lwPointList *point, lwPolygonList *polygon ) +{ + int i, j; + float p1[ 3 ], p2[ 3 ], pn[ 3 ], v1[ 3 ], v2[ 3 ]; + + for ( i = 0; i < polygon->count; i++ ) { + if ( polygon->pol[ i ].nverts < 3 ) continue; + for ( j = 0; j < 3; j++ ) { + p1[ j ] = point->pt[ polygon->pol[ i ].v[ 0 ].index ].pos[ j ]; + p2[ j ] = point->pt[ polygon->pol[ i ].v[ 1 ].index ].pos[ j ]; + pn[ j ] = point->pt[ polygon->pol[ i ].v[ + polygon->pol[ i ].nverts - 1 ].index ].pos[ j ]; + } + + for ( j = 0; j < 3; j++ ) { + v1[ j ] = p2[ j ] - p1[ j ]; + v2[ j ] = pn[ j ] - p1[ j ]; + } + + cross( v1, v2, polygon->pol[ i ].norm ); + normalize( polygon->pol[ i ].norm ); + } +} + + +/* +====================================================================== +lwGetPointPolygons() + +For each point, fill in the indexes of the polygons that share the +point. Returns 0 if any of the memory allocations fail, otherwise +returns 1. +====================================================================== */ + +int lwGetPointPolygons( lwPointList *point, lwPolygonList *polygon ) +{ + int i, j, k; + + /* count the number of polygons per point */ + + for ( i = 0; i < polygon->count; i++ ) + for ( j = 0; j < polygon->pol[ i ].nverts; j++ ) + ++point->pt[ polygon->pol[ i ].v[ j ].index ].npols; + + /* alloc per-point polygon arrays */ + + for ( i = 0; i < point->count; i++ ) { + if ( point->pt[ i ].npols == 0 ) continue; + point->pt[ i ].pol = _pico_calloc( point->pt[ i ].npols, sizeof( int )); + if ( !point->pt[ i ].pol ) return 0; + point->pt[ i ].npols = 0; + } + + /* fill in polygon array for each point */ + + for ( i = 0; i < polygon->count; i++ ) { + for ( j = 0; j < polygon->pol[ i ].nverts; j++ ) { + k = polygon->pol[ i ].v[ j ].index; + point->pt[ k ].pol[ point->pt[ k ].npols ] = i; + ++point->pt[ k ].npols; + } + } + + return 1; +} + + +/* +====================================================================== +lwResolvePolySurfaces() + +Convert tag indexes into actual lwSurface pointers. If any polygons +point to tags for which no corresponding surface can be found, a +default surface is created. +====================================================================== */ + +int lwResolvePolySurfaces( lwPolygonList *polygon, lwTagList *tlist, + lwSurface **surf, int *nsurfs ) +{ + lwSurface **s, *st; + int i, index; + + if ( tlist->count == 0 ) return 1; + + s = _pico_calloc( tlist->count, sizeof( lwSurface * )); + if ( !s ) return 0; + + for ( i = 0; i < tlist->count; i++ ) { + st = *surf; + while ( st ) { + if ( !strcmp( st->name, tlist->tag[ i ] )) { + s[ i ] = st; + break; + } + st = st->next; + } + } + + for ( i = 0; i < polygon->count; i++ ) { + index = ( int ) polygon->pol[ i ].surf; + if ( index < 0 || index > tlist->count ) return 0; + if ( !s[ index ] ) { + s[ index ] = lwDefaultSurface(); + if ( !s[ index ] ) return 0; + s[ index ]->name = _pico_alloc( strlen( tlist->tag[ index ] ) + 1 ); + if ( !s[ index ]->name ) return 0; + strcpy( s[ index ]->name, tlist->tag[ index ] ); + lwListAdd( (void *) surf, s[ index ] ); + *nsurfs = *nsurfs + 1; + } + polygon->pol[ i ].surf = s[ index ]; + } + + _pico_free( s ); + return 1; +} + + +/* +====================================================================== +lwGetVertNormals() + +Calculate the vertex normals. For each polygon vertex, sum the +normals of the polygons that share the point. If the normals of the +current and adjacent polygons form an angle greater than the max +smoothing angle for the current polygon's surface, the normal of the +adjacent polygon is excluded from the sum. It's also excluded if the +polygons aren't in the same smoothing group. + +Assumes that lwGetPointPolygons(), lwGetPolyNormals() and +lwResolvePolySurfaces() have already been called. +====================================================================== */ + +void lwGetVertNormals( lwPointList *point, lwPolygonList *polygon ) +{ + int j, k, n, g, h, p; + float a; + + for ( j = 0; j < polygon->count; j++ ) { + for ( n = 0; n < polygon->pol[ j ].nverts; n++ ) { + for ( k = 0; k < 3; k++ ) + polygon->pol[ j ].v[ n ].norm[ k ] = polygon->pol[ j ].norm[ k ]; + + if ( polygon->pol[ j ].surf->smooth <= 0 ) continue; + + p = polygon->pol[ j ].v[ n ].index; + + for ( g = 0; g < point->pt[ p ].npols; g++ ) { + h = point->pt[ p ].pol[ g ]; + if ( h == j ) continue; + + if ( polygon->pol[ j ].smoothgrp != polygon->pol[ h ].smoothgrp ) + continue; + a = vecangle( polygon->pol[ j ].norm, polygon->pol[ h ].norm ); + if ( a > polygon->pol[ j ].surf->smooth ) continue; + + for ( k = 0; k < 3; k++ ) + polygon->pol[ j ].v[ n ].norm[ k ] += polygon->pol[ h ].norm[ k ]; + } + + normalize( polygon->pol[ j ].v[ n ].norm ); + } + } +} + + +/* +====================================================================== +lwFreeTags() + +Free memory used by an lwTagList. +====================================================================== */ + +void lwFreeTags( lwTagList *tlist ) +{ + int i; + + if ( tlist ) { + if ( tlist->tag ) { + for ( i = 0; i < tlist->count; i++ ) + if ( tlist->tag[ i ] ) _pico_free( tlist->tag[ i ] ); + _pico_free( tlist->tag ); + } + memset( tlist, 0, sizeof( lwTagList )); + } +} + + +/* +====================================================================== +lwGetTags() + +Read tag strings from a TAGS chunk in an LWO2 file. The tags are +added to the lwTagList array. +====================================================================== */ + +int lwGetTags( picoMemStream_t *fp, int cksize, lwTagList *tlist ) +{ + char *buf, *bp; + int i, len, ntags; + + if ( cksize == 0 ) return 1; + + /* read the whole chunk */ + + set_flen( 0 ); + buf = getbytes( fp, cksize ); + if ( !buf ) return 0; + + /* count the strings */ + + ntags = 0; + bp = buf; + while ( bp < buf + cksize ) { + len = strlen( bp ) + 1; + len += len & 1; + bp += len; + ++ntags; + } + + /* expand the string array to hold the new tags */ + + tlist->offset = tlist->count; + tlist->count += ntags; + if ( !_pico_realloc( (void *) &tlist->tag, (tlist->count - ntags) * sizeof( char * ), tlist->count * sizeof( char * )) ) + goto Fail; + memset( &tlist->tag[ tlist->offset ], 0, ntags * sizeof( char * )); + + /* copy the new tags to the tag array */ + + bp = buf; + for ( i = 0; i < ntags; i++ ) + tlist->tag[ i + tlist->offset ] = sgetS0( (unsigned char **) &bp ); + + _pico_free( buf ); + return 1; + +Fail: + if ( buf ) _pico_free( buf ); + return 0; +} + + +/* +====================================================================== +lwGetPolygonTags() + +Read polygon tags from a PTAG chunk in an LWO2 file. +====================================================================== */ + +int lwGetPolygonTags( picoMemStream_t *fp, int cksize, lwTagList *tlist, + lwPolygonList *plist ) +{ + unsigned int type; + int rlen = 0, i, j; + + set_flen( 0 ); + type = getU4( fp ); + rlen = get_flen(); + if ( rlen < 0 ) return 0; + + if ( type != ID_SURF && type != ID_PART && type != ID_SMGP ) { + _pico_memstream_seek( fp, cksize - 4, PICO_SEEK_CUR ); + return 1; + } + + while ( rlen < cksize ) { + i = getVX( fp ) + plist->offset; + j = getVX( fp ) + tlist->offset; + rlen = get_flen(); + if ( rlen < 0 || rlen > cksize ) return 0; + + switch ( type ) { + case ID_SURF: plist->pol[ i ].surf = ( lwSurface * ) j; break; + case ID_PART: plist->pol[ i ].part = j; break; + case ID_SMGP: plist->pol[ i ].smoothgrp = j; break; + } + } + + return 1; +} diff --git a/tools/urt/libs/picomodel/lwo/surface.c b/tools/urt/libs/picomodel/lwo/surface.c new file mode 100644 index 00000000..cdb05e58 --- /dev/null +++ b/tools/urt/libs/picomodel/lwo/surface.c @@ -0,0 +1,1005 @@ +/* +====================================================================== +surface.c + +Surface functions for an LWO2 reader. + +Ernie Wright 17 Sep 00 +====================================================================== */ + +#include "../picointernal.h" +#include "lwo2.h" + + +/* +====================================================================== +lwFreePlugin() + +Free the memory used by an lwPlugin. +====================================================================== */ + +void lwFreePlugin( lwPlugin *p ) +{ + if ( p ) { + if ( p->ord ) _pico_free( p->ord ); + if ( p->name ) _pico_free( p->name ); + if ( p->data ) _pico_free( p->data ); + _pico_free( p ); + } +} + + +/* +====================================================================== +lwFreeTexture() + +Free the memory used by an lwTexture. +====================================================================== */ + +void lwFreeTexture( lwTexture *t ) +{ + if ( t ) { + if ( t->ord ) _pico_free( t->ord ); + switch ( t->type ) { + case ID_IMAP: + if ( t->param.imap.vmap_name ) _pico_free( t->param.imap.vmap_name ); + if ( t->tmap.ref_object ) _pico_free( t->tmap.ref_object ); + break; + case ID_PROC: + if ( t->param.proc.name ) _pico_free( t->param.proc.name ); + if ( t->param.proc.data ) _pico_free( t->param.proc.data ); + break; + case ID_GRAD: + if ( t->param.grad.key ) _pico_free( t->param.grad.key ); + if ( t->param.grad.ikey ) _pico_free( t->param.grad.ikey ); + break; + } + _pico_free( t ); + } +} + + +/* +====================================================================== +lwFreeSurface() + +Free the memory used by an lwSurface. +====================================================================== */ + +void lwFreeSurface( lwSurface *surf ) +{ + if ( surf ) { + if ( surf->name ) _pico_free( surf->name ); + if ( surf->srcname ) _pico_free( surf->srcname ); + + lwListFree( surf->shader, (void *) lwFreePlugin ); + + lwListFree( surf->color.tex, (void *) lwFreeTexture ); + lwListFree( surf->luminosity.tex, (void *) lwFreeTexture ); + lwListFree( surf->diffuse.tex, (void *) lwFreeTexture ); + lwListFree( surf->specularity.tex, (void *) lwFreeTexture ); + lwListFree( surf->glossiness.tex, (void *) lwFreeTexture ); + lwListFree( surf->reflection.val.tex, (void *) lwFreeTexture ); + lwListFree( surf->transparency.val.tex, (void *) lwFreeTexture ); + lwListFree( surf->eta.tex, (void *) lwFreeTexture ); + lwListFree( surf->translucency.tex, (void *) lwFreeTexture ); + lwListFree( surf->bump.tex, (void *) lwFreeTexture ); + + _pico_free( surf ); + } +} + + +/* +====================================================================== +lwGetTHeader() + +Read a texture map header from a SURF.BLOK in an LWO2 file. This is +the first subchunk in a BLOK, and its contents are common to all three +texture types. +====================================================================== */ + +int lwGetTHeader( picoMemStream_t *fp, int hsz, lwTexture *tex ) +{ + unsigned int id; + unsigned short sz; + int pos, rlen; + + + /* remember where we started */ + + set_flen( 0 ); + pos = _pico_memstream_tell( fp ); + + /* ordinal string */ + + tex->ord = getS0( fp ); + + /* first subchunk header */ + + id = getU4( fp ); + sz = getU2( fp ); + if ( 0 > get_flen() ) return 0; + + /* process subchunks as they're encountered */ + + while ( 1 ) { + sz += sz & 1; + set_flen( 0 ); + + switch ( id ) { + case ID_CHAN: + tex->chan = getU4( fp ); + break; + + case ID_OPAC: + tex->opac_type = getU2( fp ); + tex->opacity.val = getF4( fp ); + tex->opacity.eindex = getVX( fp ); + break; + + case ID_ENAB: + tex->enabled = getU2( fp ); + break; + + case ID_NEGA: + tex->negative = getU2( fp ); + break; + + case ID_AXIS: + tex->axis = getU2( fp ); + break; + + default: + break; + } + + /* error while reading current subchunk? */ + + rlen = get_flen(); + if ( rlen < 0 || rlen > sz ) return 0; + + /* skip unread parts of the current subchunk */ + + if ( rlen < sz ) + _pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR ); + + /* end of the texture header subchunk? */ + + if ( hsz <= _pico_memstream_tell( fp ) - pos ) + break; + + /* get the next subchunk header */ + + set_flen( 0 ); + id = getU4( fp ); + sz = getU2( fp ); + if ( 6 != get_flen() ) return 0; + } + + set_flen( _pico_memstream_tell( fp ) - pos ); + return 1; +} + + +/* +====================================================================== +lwGetTMap() + +Read a texture map from a SURF.BLOK in an LWO2 file. The TMAP +defines the mapping from texture to world or object coordinates. +====================================================================== */ + +int lwGetTMap( picoMemStream_t *fp, int tmapsz, lwTMap *tmap ) +{ + unsigned int id; + unsigned short sz; + int rlen, pos, i; + + pos = _pico_memstream_tell( fp ); + id = getU4( fp ); + sz = getU2( fp ); + if ( 0 > get_flen() ) return 0; + + while ( 1 ) { + sz += sz & 1; + set_flen( 0 ); + + switch ( id ) { + case ID_SIZE: + for ( i = 0; i < 3; i++ ) + tmap->size.val[ i ] = getF4( fp ); + tmap->size.eindex = getVX( fp ); + break; + + case ID_CNTR: + for ( i = 0; i < 3; i++ ) + tmap->center.val[ i ] = getF4( fp ); + tmap->center.eindex = getVX( fp ); + break; + + case ID_ROTA: + for ( i = 0; i < 3; i++ ) + tmap->rotate.val[ i ] = getF4( fp ); + tmap->rotate.eindex = getVX( fp ); + break; + + case ID_FALL: + tmap->fall_type = getU2( fp ); + for ( i = 0; i < 3; i++ ) + tmap->falloff.val[ i ] = getF4( fp ); + tmap->falloff.eindex = getVX( fp ); + break; + + case ID_OREF: + tmap->ref_object = getS0( fp ); + break; + + case ID_CSYS: + tmap->coord_sys = getU2( fp ); + break; + + default: + break; + } + + /* error while reading the current subchunk? */ + + rlen = get_flen(); + if ( rlen < 0 || rlen > sz ) return 0; + + /* skip unread parts of the current subchunk */ + + if ( rlen < sz ) + _pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR ); + + /* end of the TMAP subchunk? */ + + if ( tmapsz <= _pico_memstream_tell( fp ) - pos ) + break; + + /* get the next subchunk header */ + + set_flen( 0 ); + id = getU4( fp ); + sz = getU2( fp ); + if ( 6 != get_flen() ) return 0; + } + + set_flen( _pico_memstream_tell( fp ) - pos ); + return 1; +} + + +/* +====================================================================== +lwGetImageMap() + +Read an lwImageMap from a SURF.BLOK in an LWO2 file. +====================================================================== */ + +int lwGetImageMap( picoMemStream_t *fp, int rsz, lwTexture *tex ) +{ + unsigned int id; + unsigned short sz; + int rlen, pos; + + pos = _pico_memstream_tell( fp ); + id = getU4( fp ); + sz = getU2( fp ); + if ( 0 > get_flen() ) return 0; + + while ( 1 ) { + sz += sz & 1; + set_flen( 0 ); + + switch ( id ) { + case ID_TMAP: + if ( !lwGetTMap( fp, sz, &tex->tmap )) return 0; + break; + + case ID_PROJ: + tex->param.imap.projection = getU2( fp ); + break; + + case ID_VMAP: + tex->param.imap.vmap_name = getS0( fp ); + break; + + case ID_AXIS: + tex->param.imap.axis = getU2( fp ); + break; + + case ID_IMAG: + tex->param.imap.cindex = getVX( fp ); + break; + + case ID_WRAP: + tex->param.imap.wrapw_type = getU2( fp ); + tex->param.imap.wraph_type = getU2( fp ); + break; + + case ID_WRPW: + tex->param.imap.wrapw.val = getF4( fp ); + tex->param.imap.wrapw.eindex = getVX( fp ); + break; + + case ID_WRPH: + tex->param.imap.wraph.val = getF4( fp ); + tex->param.imap.wraph.eindex = getVX( fp ); + break; + + case ID_AAST: + tex->param.imap.aas_flags = getU2( fp ); + tex->param.imap.aa_strength = getF4( fp ); + break; + + case ID_PIXB: + tex->param.imap.pblend = getU2( fp ); + break; + + case ID_STCK: + tex->param.imap.stck.val = getF4( fp ); + tex->param.imap.stck.eindex = getVX( fp ); + break; + + case ID_TAMP: + tex->param.imap.amplitude.val = getF4( fp ); + tex->param.imap.amplitude.eindex = getVX( fp ); + break; + + default: + break; + } + + /* error while reading the current subchunk? */ + + rlen = get_flen(); + if ( rlen < 0 || rlen > sz ) return 0; + + /* skip unread parts of the current subchunk */ + + if ( rlen < sz ) + _pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR ); + + /* end of the image map? */ + + if ( rsz <= _pico_memstream_tell( fp ) - pos ) + break; + + /* get the next subchunk header */ + + set_flen( 0 ); + id = getU4( fp ); + sz = getU2( fp ); + if ( 6 != get_flen() ) return 0; + } + + set_flen( _pico_memstream_tell( fp ) - pos ); + return 1; +} + + +/* +====================================================================== +lwGetProcedural() + +Read an lwProcedural from a SURF.BLOK in an LWO2 file. +====================================================================== */ + +int lwGetProcedural( picoMemStream_t *fp, int rsz, lwTexture *tex ) +{ + unsigned int id; + unsigned short sz; + int rlen, pos; + + pos = _pico_memstream_tell( fp ); + id = getU4( fp ); + sz = getU2( fp ); + if ( 0 > get_flen() ) return 0; + + while ( 1 ) { + sz += sz & 1; + set_flen( 0 ); + + switch ( id ) { + case ID_TMAP: + if ( !lwGetTMap( fp, sz, &tex->tmap )) return 0; + break; + + case ID_AXIS: + tex->param.proc.axis = getU2( fp ); + break; + + case ID_VALU: + tex->param.proc.value[ 0 ] = getF4( fp ); + if ( sz >= 8 ) tex->param.proc.value[ 1 ] = getF4( fp ); + if ( sz >= 12 ) tex->param.proc.value[ 2 ] = getF4( fp ); + break; + + case ID_FUNC: + tex->param.proc.name = getS0( fp ); + rlen = get_flen(); + tex->param.proc.data = getbytes( fp, sz - rlen ); + break; + + default: + break; + } + + /* error while reading the current subchunk? */ + + rlen = get_flen(); + if ( rlen < 0 || rlen > sz ) return 0; + + /* skip unread parts of the current subchunk */ + + if ( rlen < sz ) + _pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR ); + + /* end of the procedural block? */ + + if ( rsz <= _pico_memstream_tell( fp ) - pos ) + break; + + /* get the next subchunk header */ + + set_flen( 0 ); + id = getU4( fp ); + sz = getU2( fp ); + if ( 6 != get_flen() ) return 0; + } + + set_flen( _pico_memstream_tell( fp ) - pos ); + return 1; +} + + +/* +====================================================================== +lwGetGradient() + +Read an lwGradient from a SURF.BLOK in an LWO2 file. +====================================================================== */ + +int lwGetGradient( picoMemStream_t *fp, int rsz, lwTexture *tex ) +{ + unsigned int id; + unsigned short sz; + int rlen, pos, i, j, nkeys; + + pos = _pico_memstream_tell( fp ); + id = getU4( fp ); + sz = getU2( fp ); + if ( 0 > get_flen() ) return 0; + + while ( 1 ) { + sz += sz & 1; + set_flen( 0 ); + + switch ( id ) { + case ID_TMAP: + if ( !lwGetTMap( fp, sz, &tex->tmap )) return 0; + break; + + case ID_PNAM: + tex->param.grad.paramname = getS0( fp ); + break; + + case ID_INAM: + tex->param.grad.itemname = getS0( fp ); + break; + + case ID_GRST: + tex->param.grad.start = getF4( fp ); + break; + + case ID_GREN: + tex->param.grad.end = getF4( fp ); + break; + + case ID_GRPT: + tex->param.grad.repeat = getU2( fp ); + break; + + case ID_FKEY: + nkeys = sz / sizeof( lwGradKey ); + tex->param.grad.key = _pico_calloc( nkeys, sizeof( lwGradKey )); + if ( !tex->param.grad.key ) return 0; + for ( i = 0; i < nkeys; i++ ) { + tex->param.grad.key[ i ].value = getF4( fp ); + for ( j = 0; j < 4; j++ ) + tex->param.grad.key[ i ].rgba[ j ] = getF4( fp ); + } + break; + + case ID_IKEY: + nkeys = sz / 2; + tex->param.grad.ikey = _pico_calloc( nkeys, sizeof( short )); + if ( !tex->param.grad.ikey ) return 0; + for ( i = 0; i < nkeys; i++ ) + tex->param.grad.ikey[ i ] = getU2( fp ); + break; + + default: + break; + } + + /* error while reading the current subchunk? */ + + rlen = get_flen(); + if ( rlen < 0 || rlen > sz ) return 0; + + /* skip unread parts of the current subchunk */ + + if ( rlen < sz ) + _pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR ); + + /* end of the gradient? */ + + if ( rsz <= _pico_memstream_tell( fp ) - pos ) + break; + + /* get the next subchunk header */ + + set_flen( 0 ); + id = getU4( fp ); + sz = getU2( fp ); + if ( 6 != get_flen() ) return 0; + } + + set_flen( _pico_memstream_tell( fp ) - pos ); + return 1; +} + + +/* +====================================================================== +lwGetTexture() + +Read an lwTexture from a SURF.BLOK in an LWO2 file. +====================================================================== */ + +lwTexture *lwGetTexture( picoMemStream_t *fp, int bloksz, unsigned int type ) +{ + lwTexture *tex; + unsigned short sz; + int ok; + + tex = _pico_calloc( 1, sizeof( lwTexture )); + if ( !tex ) return NULL; + + tex->type = type; + tex->tmap.size.val[ 0 ] = + tex->tmap.size.val[ 1 ] = + tex->tmap.size.val[ 2 ] = 1.0f; + tex->opacity.val = 1.0f; + tex->enabled = 1; + + sz = getU2( fp ); + if ( !lwGetTHeader( fp, sz, tex )) { + _pico_free( tex ); + return NULL; + } + + sz = bloksz - sz - 6; + switch ( type ) { + case ID_IMAP: ok = lwGetImageMap( fp, sz, tex ); break; + case ID_PROC: ok = lwGetProcedural( fp, sz, tex ); break; + case ID_GRAD: ok = lwGetGradient( fp, sz, tex ); break; + default: + ok = !_pico_memstream_seek( fp, sz, PICO_SEEK_CUR ); + } + + if ( !ok ) { + lwFreeTexture( tex ); + return NULL; + } + + set_flen( bloksz ); + return tex; +} + + +/* +====================================================================== +lwGetShader() + +Read a shader record from a SURF.BLOK in an LWO2 file. +====================================================================== */ + +lwPlugin *lwGetShader( picoMemStream_t *fp, int bloksz ) +{ + lwPlugin *shdr; + unsigned int id; + unsigned short sz; + int hsz, rlen, pos; + + shdr = _pico_calloc( 1, sizeof( lwPlugin )); + if ( !shdr ) return NULL; + + pos = _pico_memstream_tell( fp ); + set_flen( 0 ); + hsz = getU2( fp ); + shdr->ord = getS0( fp ); + id = getU4( fp ); + sz = getU2( fp ); + if ( 0 > get_flen() ) goto Fail; + + while ( hsz > 0 ) { + sz += sz & 1; + hsz -= sz; + if ( id == ID_ENAB ) { + shdr->flags = getU2( fp ); + break; + } + else { + _pico_memstream_seek( fp, sz, PICO_SEEK_CUR ); + id = getU4( fp ); + sz = getU2( fp ); + } + } + + id = getU4( fp ); + sz = getU2( fp ); + if ( 0 > get_flen() ) goto Fail; + + while ( 1 ) { + sz += sz & 1; + set_flen( 0 ); + + switch ( id ) { + case ID_FUNC: + shdr->name = getS0( fp ); + rlen = get_flen(); + shdr->data = getbytes( fp, sz - rlen ); + break; + + default: + break; + } + + /* error while reading the current subchunk? */ + + rlen = get_flen(); + if ( rlen < 0 || rlen > sz ) goto Fail; + + /* skip unread parts of the current subchunk */ + + if ( rlen < sz ) + _pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR ); + + /* end of the shader block? */ + + if ( bloksz <= _pico_memstream_tell( fp ) - pos ) + break; + + /* get the next subchunk header */ + + set_flen( 0 ); + id = getU4( fp ); + sz = getU2( fp ); + if ( 6 != get_flen() ) goto Fail; + } + + set_flen( _pico_memstream_tell( fp ) - pos ); + return shdr; + +Fail: + lwFreePlugin( shdr ); + return NULL; +} + + +/* +====================================================================== +compare_textures() +compare_shaders() + +Callbacks for the lwListInsert() function, which is called to add +textures to surface channels and shaders to surfaces. +====================================================================== */ + +static int compare_textures( lwTexture *a, lwTexture *b ) +{ + return strcmp( a->ord, b->ord ); +} + + +static int compare_shaders( lwPlugin *a, lwPlugin *b ) +{ + return strcmp( a->ord, b->ord ); +} + + +/* +====================================================================== +add_texture() + +Finds the surface channel (lwTParam or lwCParam) to which a texture is +applied, then calls lwListInsert(). +====================================================================== */ + +static int add_texture( lwSurface *surf, lwTexture *tex ) +{ + lwTexture **list; + + switch ( tex->chan ) { + case ID_COLR: list = &surf->color.tex; break; + case ID_LUMI: list = &surf->luminosity.tex; break; + case ID_DIFF: list = &surf->diffuse.tex; break; + case ID_SPEC: list = &surf->specularity.tex; break; + case ID_GLOS: list = &surf->glossiness.tex; break; + case ID_REFL: list = &surf->reflection.val.tex; break; + case ID_TRAN: list = &surf->transparency.val.tex; break; + case ID_RIND: list = &surf->eta.tex; break; + case ID_TRNL: list = &surf->translucency.tex; break; + case ID_BUMP: list = &surf->bump.tex; break; + default: return 0; + } + + lwListInsert( (void **) list, tex, ( void *) compare_textures ); + return 1; +} + + +/* +====================================================================== +lwDefaultSurface() + +Allocate and initialize a surface. +====================================================================== */ + +lwSurface *lwDefaultSurface( void ) +{ + lwSurface *surf; + + surf = _pico_calloc( 1, sizeof( lwSurface )); + if ( !surf ) return NULL; + + surf->color.rgb[ 0 ] = 0.78431f; + surf->color.rgb[ 1 ] = 0.78431f; + surf->color.rgb[ 2 ] = 0.78431f; + surf->diffuse.val = 1.0f; + surf->glossiness.val = 0.4f; + surf->bump.val = 1.0f; + surf->eta.val = 1.0f; + surf->sideflags = 1; + + return surf; +} + + +/* +====================================================================== +lwGetSurface() + +Read an lwSurface from an LWO2 file. +====================================================================== */ + +lwSurface *lwGetSurface( picoMemStream_t *fp, int cksize ) +{ + lwSurface *surf; + lwTexture *tex; + lwPlugin *shdr; + unsigned int id, type; + unsigned short sz; + int pos, rlen; + + + /* allocate the Surface structure */ + + surf = _pico_calloc( 1, sizeof( lwSurface )); + if ( !surf ) goto Fail; + + /* non-zero defaults */ + + surf->color.rgb[ 0 ] = 0.78431f; + surf->color.rgb[ 1 ] = 0.78431f; + surf->color.rgb[ 2 ] = 0.78431f; + surf->diffuse.val = 1.0f; + surf->glossiness.val = 0.4f; + surf->bump.val = 1.0f; + surf->eta.val = 1.0f; + surf->sideflags = 1; + + /* remember where we started */ + + set_flen( 0 ); + pos = _pico_memstream_tell( fp ); + + /* names */ + + surf->name = getS0( fp ); + surf->srcname = getS0( fp ); + + /* first subchunk header */ + + id = getU4( fp ); + sz = getU2( fp ); + if ( 0 > get_flen() ) goto Fail; + + /* process subchunks as they're encountered */ + + while ( 1 ) { + sz += sz & 1; + set_flen( 0 ); + + switch ( id ) { + case ID_COLR: + surf->color.rgb[ 0 ] = getF4( fp ); + surf->color.rgb[ 1 ] = getF4( fp ); + surf->color.rgb[ 2 ] = getF4( fp ); + surf->color.eindex = getVX( fp ); + break; + + case ID_LUMI: + surf->luminosity.val = getF4( fp ); + surf->luminosity.eindex = getVX( fp ); + break; + + case ID_DIFF: + surf->diffuse.val = getF4( fp ); + surf->diffuse.eindex = getVX( fp ); + break; + + case ID_SPEC: + surf->specularity.val = getF4( fp ); + surf->specularity.eindex = getVX( fp ); + break; + + case ID_GLOS: + surf->glossiness.val = getF4( fp ); + surf->glossiness.eindex = getVX( fp ); + break; + + case ID_REFL: + surf->reflection.val.val = getF4( fp ); + surf->reflection.val.eindex = getVX( fp ); + break; + + case ID_RFOP: + surf->reflection.options = getU2( fp ); + break; + + case ID_RIMG: + surf->reflection.cindex = getVX( fp ); + break; + + case ID_RSAN: + surf->reflection.seam_angle = getF4( fp ); + break; + + case ID_TRAN: + surf->transparency.val.val = getF4( fp ); + surf->transparency.val.eindex = getVX( fp ); + break; + + case ID_TROP: + surf->transparency.options = getU2( fp ); + break; + + case ID_TIMG: + surf->transparency.cindex = getVX( fp ); + break; + + case ID_RIND: + surf->eta.val = getF4( fp ); + surf->eta.eindex = getVX( fp ); + break; + + case ID_TRNL: + surf->translucency.val = getF4( fp ); + surf->translucency.eindex = getVX( fp ); + break; + + case ID_BUMP: + surf->bump.val = getF4( fp ); + surf->bump.eindex = getVX( fp ); + break; + + case ID_SMAN: + surf->smooth = getF4( fp ); + break; + + case ID_SIDE: + surf->sideflags = getU2( fp ); + break; + + case ID_CLRH: + surf->color_hilite.val = getF4( fp ); + surf->color_hilite.eindex = getVX( fp ); + break; + + case ID_CLRF: + surf->color_filter.val = getF4( fp ); + surf->color_filter.eindex = getVX( fp ); + break; + + case ID_ADTR: + surf->add_trans.val = getF4( fp ); + surf->add_trans.eindex = getVX( fp ); + break; + + case ID_SHRP: + surf->dif_sharp.val = getF4( fp ); + surf->dif_sharp.eindex = getVX( fp ); + break; + + case ID_GVAL: + surf->glow.val = getF4( fp ); + surf->glow.eindex = getVX( fp ); + break; + + case ID_LINE: + surf->line.enabled = 1; + if ( sz >= 2 ) surf->line.flags = getU2( fp ); + if ( sz >= 6 ) surf->line.size.val = getF4( fp ); + if ( sz >= 8 ) surf->line.size.eindex = getVX( fp ); + break; + + case ID_ALPH: + surf->alpha_mode = getU2( fp ); + surf->alpha = getF4( fp ); + break; + + case ID_AVAL: + surf->alpha = getF4( fp ); + break; + + case ID_BLOK: + type = getU4( fp ); + + switch ( type ) { + case ID_IMAP: + case ID_PROC: + case ID_GRAD: + tex = lwGetTexture( fp, sz - 4, type ); + if ( !tex ) goto Fail; + if ( !add_texture( surf, tex )) + lwFreeTexture( tex ); + set_flen( 4 + get_flen() ); + break; + case ID_SHDR: + shdr = lwGetShader( fp, sz - 4 ); + if ( !shdr ) goto Fail; + lwListInsert( (void **) &surf->shader, shdr, (void *) compare_shaders ); + ++surf->nshaders; + set_flen( 4 + get_flen() ); + break; + } + break; + + default: + break; + } + + /* error while reading current subchunk? */ + + rlen = get_flen(); + if ( rlen < 0 || rlen > sz ) goto Fail; + + /* skip unread parts of the current subchunk */ + + if ( rlen < sz ) + _pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR ); + + /* end of the SURF chunk? */ + + if ( cksize <= _pico_memstream_tell( fp ) - pos ) + break; + + /* get the next subchunk header */ + + set_flen( 0 ); + id = getU4( fp ); + sz = getU2( fp ); + if ( 6 != get_flen() ) goto Fail; + } + + return surf; + +Fail: + if ( surf ) lwFreeSurface( surf ); + return NULL; +} diff --git a/tools/urt/libs/picomodel/lwo/vecmath.c b/tools/urt/libs/picomodel/lwo/vecmath.c new file mode 100644 index 00000000..44d317b0 --- /dev/null +++ b/tools/urt/libs/picomodel/lwo/vecmath.c @@ -0,0 +1,37 @@ +/* +====================================================================== +vecmath.c + +Basic vector and matrix functions. + +Ernie Wright 17 Sep 00 +====================================================================== */ + +#include + + +float dot( float a[], float b[] ) +{ + return a[ 0 ] * b[ 0 ] + a[ 1 ] * b[ 1 ] + a[ 2 ] * b[ 2 ]; +} + + +void cross( float a[], float b[], float c[] ) +{ + c[ 0 ] = a[ 1 ] * b[ 2 ] - a[ 2 ] * b[ 1 ]; + c[ 1 ] = a[ 2 ] * b[ 0 ] - a[ 0 ] * b[ 2 ]; + c[ 2 ] = a[ 0 ] * b[ 1 ] - a[ 1 ] * b[ 0 ]; +} + + +void normalize( float v[] ) +{ + float r; + + r = ( float ) sqrt( dot( v, v )); + if ( r > 0 ) { + v[ 0 ] /= r; + v[ 1 ] /= r; + v[ 2 ] /= r; + } +} diff --git a/tools/urt/libs/picomodel/lwo/vmap.c b/tools/urt/libs/picomodel/lwo/vmap.c new file mode 100644 index 00000000..1a24bee0 --- /dev/null +++ b/tools/urt/libs/picomodel/lwo/vmap.c @@ -0,0 +1,243 @@ +/* +====================================================================== +vmap.c + +Vertex map functions for an LWO2 reader. + +Ernie Wright 17 Sep 00 +====================================================================== */ + +#include "../picointernal.h" +#include "lwo2.h" + + +/* +====================================================================== +lwFreeVMap() + +Free memory used by an lwVMap. +====================================================================== */ + +void lwFreeVMap( lwVMap *vmap ) +{ + if ( vmap ) { + if ( vmap->name ) _pico_free( vmap->name ); + if ( vmap->vindex ) _pico_free( vmap->vindex ); + if ( vmap->pindex ) _pico_free( vmap->pindex ); + if ( vmap->val ) { + if ( vmap->val[ 0 ] ) _pico_free( vmap->val[ 0 ] ); + _pico_free( vmap->val ); + } + _pico_free( vmap ); + } +} + + +/* +====================================================================== +lwGetVMap() + +Read an lwVMap from a VMAP or VMAD chunk in an LWO2. +====================================================================== */ + +lwVMap *lwGetVMap( picoMemStream_t *fp, int cksize, int ptoffset, int poloffset, + int perpoly ) +{ + unsigned char *buf, *bp; + lwVMap *vmap; + float *f; + int i, j, npts, rlen; + + + /* read the whole chunk */ + + set_flen( 0 ); + buf = getbytes( fp, cksize ); + if ( !buf ) return NULL; + + vmap = _pico_calloc( 1, sizeof( lwVMap )); + if ( !vmap ) { + _pico_free( buf ); + return NULL; + } + + /* initialize the vmap */ + + vmap->perpoly = perpoly; + + bp = buf; + set_flen( 0 ); + vmap->type = sgetU4( &bp ); + vmap->dim = sgetU2( &bp ); + vmap->name = sgetS0( &bp ); + rlen = get_flen(); + + /* count the vmap records */ + + npts = 0; + while ( bp < buf + cksize ) { + i = sgetVX( &bp ); + if ( perpoly ) + i = sgetVX( &bp ); + bp += vmap->dim * sizeof( float ); + ++npts; + } + + /* allocate the vmap */ + + vmap->nverts = npts; + vmap->vindex = _pico_calloc( npts, sizeof( int )); + if ( !vmap->vindex ) goto Fail; + if ( perpoly ) { + vmap->pindex = _pico_calloc( npts, sizeof( int )); + if ( !vmap->pindex ) goto Fail; + } + + if ( vmap->dim > 0 ) { + vmap->val = _pico_calloc( npts, sizeof( float * )); + if ( !vmap->val ) goto Fail; + f = _pico_alloc( npts * vmap->dim * sizeof( float )); + if ( !f ) goto Fail; + for ( i = 0; i < npts; i++ ) + vmap->val[ i ] = f + i * vmap->dim; + } + + /* fill in the vmap values */ + + bp = buf + rlen; + for ( i = 0; i < npts; i++ ) { + vmap->vindex[ i ] = sgetVX( &bp ); + if ( perpoly ) + vmap->pindex[ i ] = sgetVX( &bp ); + for ( j = 0; j < vmap->dim; j++ ) + vmap->val[ i ][ j ] = sgetF4( &bp ); + } + + _pico_free( buf ); + return vmap; + +Fail: + if ( buf ) _pico_free( buf ); + lwFreeVMap( vmap ); + return NULL; +} + + +/* +====================================================================== +lwGetPointVMaps() + +Fill in the lwVMapPt structure for each point. +====================================================================== */ + +int lwGetPointVMaps( lwPointList *point, lwVMap *vmap ) +{ + lwVMap *vm; + int i, j, n; + + /* count the number of vmap values for each point */ + + vm = vmap; + while ( vm ) { + if ( !vm->perpoly ) + for ( i = 0; i < vm->nverts; i++ ) + ++point->pt[ vm->vindex[ i ]].nvmaps; + vm = vm->next; + } + + /* allocate vmap references for each mapped point */ + + for ( i = 0; i < point->count; i++ ) { + if ( point->pt[ i ].nvmaps ) { + point->pt[ i ].vm = _pico_calloc( point->pt[ i ].nvmaps, sizeof( lwVMapPt )); + if ( !point->pt[ i ].vm ) return 0; + point->pt[ i ].nvmaps = 0; + } + } + + /* fill in vmap references for each mapped point */ + + vm = vmap; + while ( vm ) { + if ( !vm->perpoly ) { + for ( i = 0; i < vm->nverts; i++ ) { + j = vm->vindex[ i ]; + n = point->pt[ j ].nvmaps; + point->pt[ j ].vm[ n ].vmap = vm; + point->pt[ j ].vm[ n ].index = i; + ++point->pt[ j ].nvmaps; + } + } + vm = vm->next; + } + + return 1; +} + + +/* +====================================================================== +lwGetPolyVMaps() + +Fill in the lwVMapPt structure for each polygon vertex. +====================================================================== */ + +int lwGetPolyVMaps( lwPolygonList *polygon, lwVMap *vmap ) +{ + lwVMap *vm; + lwPolVert *pv; + int i, j; + + /* count the number of vmap values for each polygon vertex */ + + vm = vmap; + while ( vm ) { + if ( vm->perpoly ) { + for ( i = 0; i < vm->nverts; i++ ) { + for ( j = 0; j < polygon->pol[ vm->pindex[ i ]].nverts; j++ ) { + pv = &polygon->pol[ vm->pindex[ i ]].v[ j ]; + if ( vm->vindex[ i ] == pv->index ) { + ++pv->nvmaps; + break; + } + } + } + } + vm = vm->next; + } + + /* allocate vmap references for each mapped vertex */ + + for ( i = 0; i < polygon->count; i++ ) { + for ( j = 0; j < polygon->pol[ i ].nverts; j++ ) { + pv = &polygon->pol[ i ].v[ j ]; + if ( pv->nvmaps ) { + pv->vm = _pico_calloc( pv->nvmaps, sizeof( lwVMapPt )); + if ( !pv->vm ) return 0; + pv->nvmaps = 0; + } + } + } + + /* fill in vmap references for each mapped point */ + + vm = vmap; + while ( vm ) { + if ( vm->perpoly ) { + for ( i = 0; i < vm->nverts; i++ ) { + for ( j = 0; j < polygon->pol[ vm->pindex[ i ]].nverts; j++ ) { + pv = &polygon->pol[ vm->pindex[ i ]].v[ j ]; + if ( vm->vindex[ i ] == pv->index ) { + pv->vm[ pv->nvmaps ].vmap = vm; + pv->vm[ pv->nvmaps ].index = i; + ++pv->nvmaps; + break; + } + } + } + } + vm = vm->next; + } + + return 1; +} diff --git a/tools/urt/libs/picomodel/picointernal.c b/tools/urt/libs/picomodel/picointernal.c new file mode 100644 index 00000000..68dbf905 --- /dev/null +++ b/tools/urt/libs/picomodel/picointernal.c @@ -0,0 +1,1356 @@ +/* ----------------------------------------------------------------------------- + +PicoModel Library + +Copyright (c) 2002, Randy Reddig & seaw0lf +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list +of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +Neither the names of the copyright holders nor the names of its contributors may +be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +----------------------------------------------------------------------------- */ + + + +/* marker */ +#define PICOINTERNAL_C + + + +/* todo: + * - fix p->curLine for parser routines. increased twice + */ + +/* dependencies */ +#include +#include "picointernal.h" + + + +/* function pointers */ +void *(*_pico_ptr_malloc )( size_t ) = malloc; +void (*_pico_ptr_free )( void* ) = free; +void (*_pico_ptr_load_file )( char*, unsigned char**, int* ) = NULL; +void (*_pico_ptr_free_file )( void* ) = NULL; +void (*_pico_ptr_print )( int, const char* ) = NULL; + +typedef union +{ + float f; + char c[4]; +} +floatSwapUnion; + +/* _pico_alloc: + * kludged memory allocation wrapper + */ +void *_pico_alloc( size_t size ) +{ + void *ptr; + + /* some sanity checks */ + if( size == 0 ) + return NULL; + if (_pico_ptr_malloc == NULL) + return NULL; + + /* allocate memory */ + ptr = _pico_ptr_malloc(size); + if (ptr == NULL) + return NULL; + + /* zero out allocated memory */ + memset(ptr,0,size); + + /* return pointer to allocated memory */ + return ptr; +} + +/* _pico_calloc: + * _pico_calloc wrapper + */ +void *_pico_calloc( size_t num, size_t size ) +{ + void *ptr; + + /* some sanity checks */ + if( num == 0 || size == 0 ) + return NULL; + if (_pico_ptr_malloc == NULL) + return NULL; + + /* allocate memory */ + ptr = _pico_ptr_malloc(num*size); + if (ptr == NULL) + return NULL; + + /* zero out allocated memory */ + memset(ptr,0,num*size); + + /* return pointer to allocated memory */ + return ptr; +} + +/* _pico_realloc: + * memory reallocation wrapper (note: only grows, + * but never shrinks or frees) + */ +void *_pico_realloc( void **ptr, size_t oldSize, size_t newSize ) +{ + void *ptr2; + + /* sanity checks */ + if( ptr == NULL ) + return NULL; + if( newSize < oldSize ) + return *ptr; + if (_pico_ptr_malloc == NULL) + return NULL; + + /* allocate new pointer */ + ptr2 = _pico_alloc( newSize ); + if( ptr2 == NULL ) + return NULL; + + /* copy */ + if( *ptr != NULL ) + { + memcpy( ptr2, *ptr, oldSize ); + _pico_free( *ptr ); + } + + /* fix up and return */ + *ptr = ptr2; + return *ptr; +} + +/* _pico_clone_alloc: + * handy function for quick string allocation/copy. it clones + * the given string and returns a pointer to the new allocated + * clone (which must be freed by caller of course) or returns + * NULL on memory alloc or param errors. if 'size' is -1 the + * length of the input string is used, otherwise 'size' is used + * as custom clone size (the string is cropped to fit into mem + * if needed). -sea + */ +char *_pico_clone_alloc( const char *str ) +{ + char* cloned; + + /* sanity check */ + if (str == NULL) + return NULL; + + /* allocate memory */ + cloned = _pico_alloc( strlen(str) + 1 ); + if (cloned == NULL) + return NULL; + + /* copy input string to cloned string */ + strcpy( cloned, str ); + + /* return ptr to cloned string */ + return cloned; +} + +/* _pico_free: + * wrapper around the free function pointer + */ +void _pico_free( void *ptr ) +{ + /* sanity checks */ + if( ptr == NULL ) + return; + if (_pico_ptr_free == NULL) + return; + + /* free the allocated memory */ + _pico_ptr_free( ptr ); +} + +/* _pico_load_file: + * wrapper around the loadfile function pointer + */ +void _pico_load_file( char *name, unsigned char **buffer, int *bufSize ) +{ + /* sanity checks */ + if( name == NULL ) + { + *bufSize = -1; + return; + } + if (_pico_ptr_load_file == NULL) + { + *bufSize = -1; + return; + } + /* do the actual call to read in the file; */ + /* BUFFER IS ALLOCATED BY THE EXTERNAL LOADFILE FUNC */ + _pico_ptr_load_file( name,buffer,bufSize ); +} + +/* _pico_free_file: + * wrapper around the file free function pointer + */ +void _pico_free_file( void *buffer ) +{ + /* sanity checks */ + if( buffer == NULL ) + return; + + /* use default free */ + if( _pico_ptr_free_file == NULL ) + { + free( buffer ); + return; + } + /* free the allocated file */ + _pico_ptr_free_file( buffer ); +} + +/* _pico_printf: + * wrapper around the print function pointer -sea + */ +void _pico_printf( int level, const char *format, ...) +{ + char str[4096]; + va_list argptr; + + /* sanity checks */ + if( format == NULL ) + return; + if (_pico_ptr_print == NULL) + return; + + /* format string */ + va_start( argptr,format ); + vsprintf( str,format,argptr ); + va_end( argptr ); + + /* remove linefeeds */ + if (str[ strlen(str)-1 ] == '\n') + str[ strlen(str)-1 ] = '\0'; + + /* do the actual call */ + _pico_ptr_print( level,str ); +} + +/* _pico_first_token: + * trims everything after the first whitespace-delimited token + */ + +void _pico_first_token( char *str ) +{ + if( !str || !*str ) + return; + while( *str && !isspace( *str ) ) + *str++; + *str = '\0'; +} + +/* _pico_strltrim: + * left trims the given string -sea + */ +char *_pico_strltrim( char *str ) +{ + char *str1 = str, *str2 = str; + + while (isspace(*str2)) str2++; + if( str2 != str ) + while( *str2 != '\0' ) /* fix: ydnar */ + *str1++ = *str2++; + return str; +} + +/* _pico_strrtrim: + * right trims the given string -sea + */ +char *_pico_strrtrim( char *str ) +{ + if (str && *str) + { + char *str1 = str; + int allspace = 1; + + while (*str1) + { + if (allspace && !isspace(*str1)) allspace = 0; + str1++; + } + if (allspace) *str = '\0'; + else { + str1--; + while ((isspace(*str1)) && (str1 >= str)) + *str1-- = '\0'; + } + } + return str; +} + +/* _pico_strlwr: + * pico internal string-to-lower routine. + */ +char *_pico_strlwr( char *str ) +{ + char *cp; + for (cp=str; *cp; ++cp) + { + if ('A' <= *cp && *cp <= 'Z') + { + *cp += ('a' - 'A'); + } + } + return str; +} + +/* _pico_strchcount: + * counts how often the given char appears in str. -sea + */ +int _pico_strchcount( char *str, int ch ) +{ + int count = 0; + while (*str++) if (*str == ch) count++; + return count; +} + +void _pico_zero_bounds( picoVec3_t mins, picoVec3_t maxs ) +{ + int i; + for (i=0; i<3; i++) + { + mins[i] = +999999; + maxs[i] = -999999; + } +} + +void _pico_expand_bounds( picoVec3_t p, picoVec3_t mins, picoVec3_t maxs ) +{ + int i; + for (i=0; i<3; i++) + { + float value = p[i]; + if (value < mins[i]) mins[i] = value; + if (value > maxs[i]) maxs[i] = value; + } +} + +void _pico_zero_vec( picoVec3_t vec ) +{ + vec[ 0 ] = vec[ 1 ] = vec[ 2 ] = 0; +} + +void _pico_zero_vec2( picoVec2_t vec ) +{ + vec[ 0 ] = vec[ 1 ] = 0; +} + +void _pico_zero_vec4( picoVec4_t vec ) +{ + vec[ 0 ] = vec[ 1 ] = vec[ 2 ] = vec[ 3 ] = 0; +} + +void _pico_set_vec( picoVec3_t v, float a, float b, float c ) +{ + v[ 0 ] = a; + v[ 1 ] = b; + v[ 2 ] = c; +} + +void _pico_set_vec4( picoVec4_t v, float a, float b, float c, float d ) +{ + v[ 0 ] = a; + v[ 1 ] = b; + v[ 2 ] = c; + v[ 3 ] = d; +} + +void _pico_copy_vec( picoVec3_t src, picoVec3_t dest ) +{ + dest[ 0 ] = src[ 0 ]; + dest[ 1 ] = src[ 1 ]; + dest[ 2 ] = src[ 2 ]; +} + +void _pico_copy_vec2( picoVec2_t src, picoVec2_t dest ) +{ + dest[ 0 ] = src[ 0 ]; + dest[ 1 ] = src[ 1 ]; +} + +void _pico_copy_vec4( picoVec4_t src, picoVec4_t dest ) +{ + dest[ 0 ] = src[ 0 ]; + dest[ 1 ] = src[ 1 ]; + dest[ 2 ] = src[ 2 ]; + dest[ 3 ] = src[ 3 ]; +} + +/* ydnar */ +picoVec_t _pico_normalize_vec( picoVec3_t vec ) +{ + double len, ilen; + + len = sqrt( vec[ 0 ] * vec[ 0 ] + vec[ 1 ] * vec[ 1 ] + vec[ 2 ] * vec[ 2 ] ); + if( len == 0.0 ) return 0.0; + ilen = 1.0 / len; + vec[ 0 ] *= (picoVec_t) ilen; + vec[ 1 ] *= (picoVec_t) ilen; + vec[ 2 ] *= (picoVec_t) ilen; + return (picoVec_t) len; +} + +void _pico_add_vec( picoVec3_t a, picoVec3_t b, picoVec3_t dest ) +{ + dest[ 0 ] = a[ 0 ] + b[ 0 ]; + dest[ 1 ] = a[ 1 ] + b[ 1 ]; + dest[ 2 ] = a[ 2 ] + b[ 2 ]; +} + +void _pico_subtract_vec( picoVec3_t a, picoVec3_t b, picoVec3_t dest ) +{ + dest[ 0 ] = a[ 0 ] - b[ 0 ]; + dest[ 1 ] = a[ 1 ] - b[ 1 ]; + dest[ 2 ] = a[ 2 ] - b[ 2 ]; +} + +void _pico_scale_vec( picoVec3_t v, float scale, picoVec3_t dest ) +{ + dest[ 0 ] = v[ 0 ] * scale; + dest[ 1 ] = v[ 1 ] * scale; + dest[ 2 ] = v[ 2 ] * scale; +} + +void _pico_scale_vec4( picoVec4_t v, float scale, picoVec4_t dest ) +{ + dest[ 0 ] = v[ 0 ] * scale; + dest[ 1 ] = v[ 1 ] * scale; + dest[ 2 ] = v[ 2 ] * scale; + dest[ 3 ] = v[ 3 ] * scale; +} + +picoVec_t _pico_dot_vec( picoVec3_t a, picoVec3_t b ) +{ + return a[ 0 ] * b[ 0 ] + a[ 1 ] * b[ 1 ] + a[ 2 ] * b[ 2 ]; +} + +void _pico_cross_vec( picoVec3_t a, picoVec3_t b, picoVec3_t dest ) +{ + dest[ 0 ] = a[ 1 ] * b[ 2 ] - a[ 2 ] * b[ 1 ]; + dest[ 1 ] = a[ 2 ] * b[ 0 ] - a[ 0 ] * b[ 2 ]; + dest[ 2 ] = a[ 0 ] * b[ 1 ] - a[ 1 ] * b[ 0 ]; +} + +picoVec_t _pico_calc_plane( picoVec4_t plane, picoVec3_t a, picoVec3_t b, picoVec3_t c ) +{ + picoVec3_t ba, ca; + + _pico_subtract_vec( b, a, ba ); + _pico_subtract_vec( c, a, ca ); + _pico_cross_vec( ca, ba, plane ); + plane[ 3 ] = _pico_dot_vec( a, plane ); + return _pico_normalize_vec( plane ); +} + +/* separate from _pico_set_vec4 */ +void _pico_set_color( picoColor_t c, int r, int g, int b, int a ) +{ + c[ 0 ] = r; + c[ 1 ] = g; + c[ 2 ] = b; + c[ 3 ] = a; +} + +void _pico_copy_color( picoColor_t src, picoColor_t dest ) +{ + dest[ 0 ] = src[ 0 ]; + dest[ 1 ] = src[ 1 ]; + dest[ 2 ] = src[ 2 ]; + dest[ 3 ] = src[ 3 ]; +} + +#ifdef __BIG_ENDIAN__ + +int _pico_big_long ( int src ) { return src; } +short _pico_big_short( short src ) { return src; } +float _pico_big_float( float src ) { return src; } + +int _pico_little_long( int src ) +{ + return ((src & 0xFF000000) >> 24) | + ((src & 0x00FF0000) >> 8) | + ((src & 0x0000FF00) << 8) | + ((src & 0x000000FF) << 24); +} + +short _pico_little_short( short src ) +{ + return ((src & 0xFF00) >> 8) | + ((src & 0x00FF) << 8); +} + +float _pico_little_float( float src ) +{ + floatSwapUnion in,out; + in.f = src; + out.c[ 0 ] = in.c[ 3 ]; + out.c[ 1 ] = in.c[ 2 ]; + out.c[ 2 ] = in.c[ 1 ]; + out.c[ 3 ] = in.c[ 0 ]; + return out.f; +} +#else /*__BIG_ENDIAN__*/ + +int _pico_little_long ( int src ) { return src; } +short _pico_little_short( short src ) { return src; } +float _pico_little_float( float src ) { return src; } + +int _pico_big_long( int src ) +{ + return ((src & 0xFF000000) >> 24) | + ((src & 0x00FF0000) >> 8) | + ((src & 0x0000FF00) << 8) | + ((src & 0x000000FF) << 24); +} + +short _pico_big_short( short src ) +{ + return ((src & 0xFF00) >> 8) | + ((src & 0x00FF) << 8); +} + +float _pico_big_float( float src ) +{ + floatSwapUnion in,out; + in.f = src; + out.c[ 0 ] = in.c[ 3 ]; + out.c[ 1 ] = in.c[ 2 ]; + out.c[ 2 ] = in.c[ 1 ]; + out.c[ 3 ] = in.c[ 0 ]; + return out.f; +} +#endif /*__BIG_ENDIAN__*/ + +/* _pico_stristr: + * case-insensitive strstr. -sea + */ +char *_pico_stristr( char *str, const char *substr ) +{ + const size_t sublen = strlen(substr); + while (*str) + { + if (!_pico_strnicmp(str,substr,sublen)) break; + str++; + } + if (!(*str)) str = NULL; + return str; +} + +/* +_pico_unixify() +changes dos \ style path separators to / +*/ + +void _pico_unixify( char *path ) +{ + if( path == NULL ) + return; + while( *path ) + { + if( *path == '\\' ) + *path = '/'; + path++; + } +} + +/* _pico_nofname: + * removes file name portion from given file path and converts + * the directory separators to un*x style. returns 1 on success + * or 0 when 'destSize' was exceeded. -sea + */ +int _pico_nofname( const char *path, char *dest, int destSize ) +{ + int left = destSize; + char *temp = dest; + + while ((*dest = *path) != '\0') + { + if (*dest == '/' || *dest == '\\') + { + temp = (dest + 1); + *dest = '/'; + } + dest++; path++; + + if (--left < 1) + { + *temp = '\0'; + return 0; + } + } + *temp = '\0'; + return 1; +} + +/* _pico_nopath: + * returns ptr to filename portion in given path or an empty + * string otherwise. given 'path' is not altered. -sea + */ +const char *_pico_nopath( const char *path ) +{ + const char *src; + src = path + (strlen(path) - 1); + + if (path == NULL) return ""; + if (!strchr(path,'/') && !strchr(path,'\\')) + return (path); + + while ((src--) != path) + { + if (*src == '/' || *src == '\\') + return (++src); + } + return ""; +} + +/* _pico_setfext: + * sets/changes the file extension for the given filename + * or filepath's filename portion. the given 'path' *is* + * altered. leave 'ext' empty to remove extension. -sea + */ +char *_pico_setfext( char *path, const char *ext ) +{ + char *src; + int remfext = 0; + + src = path + (strlen(path) - 1); + + if (ext == NULL) ext = ""; + if (strlen(ext ) < 1) remfext = 1; + if (strlen(path) < 1) + return path; + + while ((src--) != path) + { + if (*src == '/' || *src == '\\') + return path; + + if (*src == '.') + { + if (remfext) + { + *src = '\0'; + return path; + } + *(++src) = '\0'; + break; + } + } + strcat(path,ext); + return path; +} + +/* _pico_getline: + * extracts one line from the given buffer and stores it in dest. + * returns -1 on error or the length of the line on success. i've + * removed string trimming here. this can be done manually by the + * calling func. + */ +int _pico_getline( char *buf, int bufsize, char *dest, int destsize ) +{ + int pos; + + /* check output */ + if (dest == NULL || destsize < 1) return -1; + memset( dest,0,destsize ); + + /* check input */ + if (buf == NULL || bufsize < 1) + return -1; + + /* get next line */ + for (pos=0; poscursor == NULL) + return; + + /* skin white spaces */ + while( 1 ) + { + /* sanity checks */ + if (p->cursor < p->buffer || + p->cursor >= p->max) + { + return; + } + /* break for chars other than white spaces */ + if (*p->cursor > 0x20) break; + if (*p->cursor == 0x00) return; + + /* a bit of linefeed handling */ + if (*p->cursor == '\n') + { + *hasLFs = 1; + p->curLine++; + } + /* go to next character */ + p->cursor++; + } +} + +/* _pico_new_parser: + * allocates a new ascii parser object. + */ +picoParser_t *_pico_new_parser( picoByte_t *buffer, int bufSize ) +{ + picoParser_t *p; + + /* sanity check */ + if( buffer == NULL || bufSize <= 0 ) + return NULL; + + /* allocate reader */ + p = _pico_alloc( sizeof(picoParser_t) ); + if (p == NULL) return NULL; + memset( p,0,sizeof(picoParser_t) ); + + /* allocate token space */ + p->tokenSize = 0; + p->tokenMax = 1024; + p->token = _pico_alloc( p->tokenMax ); + if( p->token == NULL ) + { + _pico_free( p ); + return NULL; + } + /* setup */ + p->buffer = buffer; + p->cursor = buffer; + p->bufSize = bufSize; + p->max = p->buffer + bufSize; + p->curLine = 1; /* sea: new */ + + /* return ptr to parser */ + return p; +} + +/* _pico_free_parser: + * frees an existing pico parser object. + */ +void _pico_free_parser( picoParser_t *p ) +{ + /* sanity check */ + if (p == NULL) return; + + /* free the parser */ + if (p->token != NULL) + { + _pico_free( p->token ); + } + _pico_free( p ); +} + +/* _pico_parse_ex: + * reads the next token from given pico parser object. if param + * 'allowLFs' is 1 it will read beyond linefeeds and return 0 when + * the EOF is reached. if 'allowLFs' is 0 it will return 0 when + * the EOL is reached. if 'handleQuoted' is 1 the parser function + * will handle "quoted" strings and return the data between the + * quotes as token. returns 0 on end/error or 1 on success. -sea + */ +int _pico_parse_ex( picoParser_t *p, int allowLFs, int handleQuoted ) +{ + int hasLFs = 0; + char *old; + + /* sanity checks */ + if( p == NULL || p->buffer == NULL || + p->cursor < p->buffer || + p->cursor >= p->max ) + { + return 0; + } + /* clear parser token */ + p->tokenSize = 0; + p->token[ 0 ] = '\0'; + old = p->cursor; + + /* skip whitespaces */ + while( p->cursor < p->max && *p->cursor <= 32 ) + { + if (*p->cursor == '\n') + { + p->curLine++; + hasLFs++; + } + p->cursor++; + } + /* return if we're not allowed to go beyond lfs */ + if ((hasLFs > 0) && !allowLFs) + { + p->cursor = old; + return 0; + } + /* get next quoted string */ + if (*p->cursor == '\"' && handleQuoted) + { + p->cursor++; + while (p->cursor < p->max && *p->cursor) + { + if (*p->cursor == '\\') + { + if (*(p->cursor+1) == '"') + { + p->cursor++; + } + p->token[ p->tokenSize++ ] = *p->cursor++; + continue; + } + else if (*p->cursor == '\"') + { + p->cursor++; + break; + } + else if (*p->cursor == '\n') + { + p->curLine++; + } + p->token[ p->tokenSize++ ] = *p->cursor++; + } + /* terminate token */ + p->token[ p->tokenSize ] = '\0'; + return 1; + } + /* otherwise get next word */ + while( p->cursor < p->max && *p->cursor > 32 ) + { + if (*p->cursor == '\n') + { + p->curLine++; + } + p->token[ p->tokenSize++ ] = *p->cursor++; + } + /* terminate token */ + p->token[ p->tokenSize ] = '\0'; + return 1; +} + +/* _pico_parse_first: + * reads the first token from the next line and returns + * a pointer to it. returns NULL on EOL or EOF. -sea + */ +char *_pico_parse_first( picoParser_t *p ) +{ + /* sanity check */ + if (p == NULL) return NULL; + + /* try to read next token (with lfs & quots) */ + if (!_pico_parse_ex( p,1,1 )) + return NULL; + + /* return ptr to the token string */ + return p->token; +} + +/* _pico_parse: + * reads the next token from the parser and returns a pointer + * to it. quoted strings are handled as usual. returns NULL + * on EOL or EOF. -sea + */ +char *_pico_parse( picoParser_t *p, int allowLFs ) +{ + /* sanity check */ + if (p == NULL) return NULL; + + /* try to read next token (with quots) */ + if (!_pico_parse_ex( p,allowLFs,1 )) + return NULL; + + /* return ptr to the token string */ + return p->token; +} + +/* _pico_parse_skip_rest: + * skips the rest of the current line in parser. + */ +void _pico_parse_skip_rest( picoParser_t *p ) +{ + while( _pico_parse_ex( p,0,0 ) ) ; +} + +/* _pico_parse_skip_braced: + * parses/skips over a braced section. returns 1 on success + * or 0 on error (when there was no closing bracket and the + * end of buffer was reached or when the opening bracket was + * missing). + */ +int _pico_parse_skip_braced( picoParser_t *p ) +{ + int firstToken = 1; + int level; + + /* sanity check */ + if (p == NULL) return 0; + + /* set the initial level for parsing */ + level = 0; + + /* skip braced section */ + while( 1 ) + { + /* read next token (lfs allowed) */ + if (!_pico_parse_ex( p,1,1 )) + { + /* end of parser buffer reached */ + return 0; + } + /* first token must be an opening bracket */ + if (firstToken && p->token[0] != '{') + { + /* opening bracket missing */ + return 0; + } + /* we only check this once */ + firstToken = 0; + + /* update level */ + if (p->token[1] == '\0') + { + if (p->token[0] == '{') level++; + if (p->token[0] == '}') level--; + } + /* break if we're back at our starting level */ + if (level == 0) break; + } + /* successfully skipped braced section */ + return 1; +} + +int _pico_parse_check( picoParser_t *p, int allowLFs, char *str ) +{ + if (!_pico_parse_ex( p,allowLFs,1 )) + return 0; + if (!strcmp(p->token,str)) + return 1; + return 0; +} + +int _pico_parse_checki( picoParser_t *p, int allowLFs, char *str ) +{ + if (!_pico_parse_ex( p,allowLFs,1 )) + return 0; + if (!_pico_stricmp(p->token,str)) + return 1; + return 0; +} + +int _pico_parse_int( picoParser_t *p, int *out ) +{ + char *token; + + /* sanity checks */ + if (p == NULL || out == NULL) + return 0; + + /* get token and turn it into an integer */ + *out = 0; + token = _pico_parse( p,0 ); + if (token == NULL) return 0; + *out = atoi( token ); + + /* success */ + return 1; +} + +int _pico_parse_int_def( picoParser_t *p, int *out, int def ) +{ + char *token; + + /* sanity checks */ + if (p == NULL || out == NULL) + return 0; + + /* get token and turn it into an integer */ + *out = def; + token = _pico_parse( p,0 ); + if (token == NULL) return 0; + *out = atoi( token ); + + /* success */ + return 1; +} + +int _pico_parse_float( picoParser_t *p, float *out ) +{ + char *token; + + /* sanity checks */ + if (p == NULL || out == NULL) + return 0; + + /* get token and turn it into a float */ + *out = 0.0f; + token = _pico_parse( p,0 ); + if (token == NULL) return 0; + *out = (float) atof( token ); + + /* success */ + return 1; +} + +int _pico_parse_float_def( picoParser_t *p, float *out, float def ) +{ + char *token; + + /* sanity checks */ + if (p == NULL || out == NULL) + return 0; + + /* get token and turn it into a float */ + *out = def; + token = _pico_parse( p,0 ); + if (token == NULL) return 0; + *out = (float) atof( token ); + + /* success */ + return 1; +} + +int _pico_parse_vec( picoParser_t *p, picoVec3_t out ) +{ + char *token; + int i; + + /* sanity checks */ + if (p == NULL || out == NULL) + return 0; + + /* zero out outination vector */ + _pico_zero_vec( out ); + + /* parse three vector components */ + for (i=0; i<3; i++) + { + token = _pico_parse( p,0 ); + if (token == NULL) + { + _pico_zero_vec( out ); + return 0; + } + out[ i ] = (float) atof( token ); + } + /* success */ + return 1; +} + +int _pico_parse_vec_def( picoParser_t *p, picoVec3_t out, picoVec3_t def ) +{ + char *token; + int i; + + /* sanity checks */ + if (p == NULL || out == NULL) + return 0; + + /* assign default vector value */ + _pico_copy_vec( def,out ); + + /* parse three vector components */ + for (i=0; i<3; i++) + { + token = _pico_parse( p,0 ); + if (token == NULL) + { + _pico_copy_vec( def,out ); + return 0; + } + out[ i ] = (float) atof( token ); + } + /* success */ + return 1; +} + +int _pico_parse_vec2( picoParser_t *p, picoVec2_t out ) +{ + char *token; + int i; + + /* sanity checks */ + if (p == NULL || out == NULL) + return 0; + + /* zero out outination vector */ + _pico_zero_vec2( out ); + + /* parse two vector components */ + for (i=0; i<2; i++) + { + token = _pico_parse( p,0 ); + if (token == NULL) + { + _pico_zero_vec2( out ); + return 0; + } + out[ i ] = (float) atof( token ); + } + /* success */ + return 1; +} + +int _pico_parse_vec2_def( picoParser_t *p, picoVec2_t out, picoVec2_t def ) +{ + char *token; + int i; + + /* sanity checks */ + if (p == NULL || out == NULL) + return 0; + + /* assign default vector value */ + _pico_copy_vec2( def,out ); + + /* parse two vector components */ + for (i=0; i<2; i++) + { + token = _pico_parse( p,0 ); + if (token == NULL) + { + _pico_copy_vec2( def,out ); + return 0; + } + out[ i ] = (float) atof( token ); + } + /* success */ + return 1; +} + +int _pico_parse_vec4( picoParser_t *p, picoVec4_t out ) +{ + char *token; + int i; + + /* sanity checks */ + if (p == NULL || out == NULL) + return 0; + + /* zero out outination vector */ + _pico_zero_vec4( out ); + + /* parse four vector components */ + for (i=0; i<4; i++) + { + token = _pico_parse( p,0 ); + if (token == NULL) + { + _pico_zero_vec4( out ); + return 0; + } + out[ i ] = (float) atof( token ); + } + /* success */ + return 1; +} + +int _pico_parse_vec4_def( picoParser_t *p, picoVec4_t out, picoVec4_t def ) +{ + char *token; + int i; + + /* sanity checks */ + if (p == NULL || out == NULL) + return 0; + + /* assign default vector value */ + _pico_copy_vec4( def,out ); + + /* parse four vector components */ + for (i=0; i<4; i++) + { + token = _pico_parse( p,0 ); + if (token == NULL) + { + _pico_copy_vec4( def,out ); + return 0; + } + out[ i ] = (float) atof( token ); + } + /* success */ + return 1; +} + +/* _pico_new_memstream: + * allocates a new memorystream object. + */ +picoMemStream_t *_pico_new_memstream( picoByte_t *buffer, int bufSize ) +{ + picoMemStream_t *s; + + /* sanity check */ + if( buffer == NULL || bufSize <= 0 ) + return NULL; + + /* allocate stream */ + s = _pico_alloc( sizeof(picoMemStream_t) ); + if (s == NULL) return NULL; + memset( s,0,sizeof(picoMemStream_t) ); + + /* setup */ + s->buffer = buffer; + s->curPos = buffer; + s->bufSize = bufSize; + s->flag = 0; + + /* return ptr to stream */ + return s; +} + +/* _pico_free_memstream: + * frees an existing pico memorystream object. + */ +void _pico_free_memstream( picoMemStream_t *s ) +{ + /* sanity check */ + if (s == NULL) return; + + /* free the stream */ + _pico_free( s ); +} + +/* _pico_memstream_read: + * reads data from a pico memorystream into a buffer. + */ +int _pico_memstream_read( picoMemStream_t *s, void *buffer, int len ) +{ + int ret = 1; + + /* sanity checks */ + if (s == NULL || buffer == NULL) + return 0; + + if (s->curPos + len > s->buffer + s->bufSize) + { + s->flag |= PICO_IOEOF; + len = s->buffer + s->bufSize - s->curPos; + ret = 0; + } + + /* read the data */ + memcpy( buffer, s->curPos, len ); + s->curPos += len; + return ret; +} + +/* _pico_memstream_read: + * reads a character from a pico memorystream + */ +int _pico_memstream_getc( picoMemStream_t *s ) +{ + int c = 0; + + /* sanity check */ + if (s == NULL) + return -1; + + /* read the character */ + if (_pico_memstream_read( s, &c, 1) == 0) + return -1; + + return c; +} + +/* _pico_memstream_seek: + * sets the current read position to a different location + */ +int _pico_memstream_seek( picoMemStream_t *s, long offset, int origin ) +{ + int overflow; + + /* sanity check */ + if (s == NULL) + return -1; + + if (origin == PICO_SEEK_SET) + { + s->curPos = s->buffer + offset; + overflow = s->curPos - ( s->buffer + s->bufSize ); + if (overflow > 0) + { + s->curPos = s->buffer + s->bufSize; + return offset - overflow; + } + return 0; + } + else if (origin == PICO_SEEK_CUR) + { + s->curPos += offset; + overflow = s->curPos - ( s->buffer + s->bufSize ); + if (overflow > 0) + { + s->curPos = s->buffer + s->bufSize; + return offset - overflow; + } + return 0; + } + else if (origin == PICO_SEEK_END) + { + s->curPos = ( s->buffer + s->bufSize ) - offset; + overflow = s->buffer - s->curPos; + if (overflow > 0) + { + s->curPos = s->buffer; + return offset - overflow; + } + return 0; + } + + return -1; +} + +/* _pico_memstream_tell: + * returns the current read position in the pico memorystream + */ +long _pico_memstream_tell( picoMemStream_t *s ) +{ + /* sanity check */ + if (s == NULL) + return -1; + + return s->curPos - s->buffer; +} diff --git a/tools/urt/libs/picomodel/picointernal.h b/tools/urt/libs/picomodel/picointernal.h new file mode 100644 index 00000000..50b24bc6 --- /dev/null +++ b/tools/urt/libs/picomodel/picointernal.h @@ -0,0 +1,206 @@ +/* ----------------------------------------------------------------------------- + +PicoModel Library + +Copyright (c) 2002, Randy Reddig & seaw0lf +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list +of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +Neither the names of the copyright holders nor the names of its contributors may +be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +----------------------------------------------------------------------------- */ + + + +/* marker */ +#ifndef PICOINTERNAL_H +#define PICOINTERNAL_H + +#ifdef __cplusplus +extern "C" +{ +#endif + + +/* dependencies */ +#include +#include +#include +#include +#include +#include + +#include "picomodel.h" + + +/* os dependent replacements */ +#if WIN32 || _WIN32 + #define _pico_stricmp stricmp + #define _pico_strnicmp strnicmp +#else + #define _pico_stricmp strcasecmp + #define _pico_strnicmp strncasecmp +#endif + + +/* constants */ +#define PICO_PI 3.14159265358979323846 + +#define PICO_SEEK_SET 0 +#define PICO_SEEK_CUR 1 +#define PICO_SEEK_END 2 + +#define PICO_IOEOF 1 +#define PICO_IOERR 2 + +/* types */ +typedef struct picoParser_s +{ + char *buffer; + int bufSize; + char *token; + int tokenSize; + int tokenMax; + char *cursor; + char *max; + int curLine; +} +picoParser_t; + +typedef struct picoMemStream_s +{ + picoByte_t *buffer; + int bufSize; + picoByte_t *curPos; + int flag; +} +picoMemStream_t; + + +/* variables */ +extern const picoModule_t *picoModules[]; + +extern void *(*_pico_ptr_malloc)( size_t ); +extern void (*_pico_ptr_free)( void* ); +extern void (*_pico_ptr_load_file)( char*, unsigned char**, int* ); +extern void (*_pico_ptr_free_file)( void* ); +extern void (*_pico_ptr_print)( int, const char* ); + + + +/* prototypes */ + +/* memory */ +void *_pico_alloc( size_t size ); +void *_pico_calloc( size_t num, size_t size ); +void *_pico_realloc( void **ptr, size_t oldSize, size_t newSize ); +char *_pico_clone_alloc( const char *str ); +void _pico_free( void *ptr ); + +/* files */ +void _pico_load_file( char *name, unsigned char **buffer, int *bufSize ); +void _pico_free_file( void *buffer ); + +/* strings */ +void _pico_first_token( char *str ); +char *_pico_strltrim( char *str ); +char *_pico_strrtrim( char *str ); +int _pico_strchcount( char *str, int ch ); +void _pico_printf( int level, const char *format, ... ); +char *_pico_stristr( char *str, const char *substr ); +void _pico_unixify( char *path ); +int _pico_nofname( const char *path, char *dest, int destSize ); +const char *_pico_nopath( const char *path ); +char *_pico_setfext( char *path, const char *ext ); +int _pico_getline( char *buf, int bufsize, char *dest, int destsize ); +char *_pico_strlwr( char *str ); + +/* vectors */ +void _pico_zero_bounds( picoVec3_t mins, picoVec3_t maxs ); +void _pico_expand_bounds( picoVec3_t p, picoVec3_t mins, picoVec3_t maxs ); +void _pico_zero_vec( picoVec3_t vec ); +void _pico_zero_vec2( picoVec2_t vec ); +void _pico_zero_vec4( picoVec4_t vec ); +void _pico_set_vec( picoVec3_t v, float a, float b, float c ); +void _pico_set_vec4( picoVec4_t v, float a, float b, float c, float d ); +void _pico_set_color( picoColor_t c, int r, int g, int b, int a ); +void _pico_copy_color( picoColor_t src, picoColor_t dest ); +void _pico_copy_vec( picoVec3_t src, picoVec3_t dest ); +void _pico_copy_vec2( picoVec2_t src, picoVec2_t dest ); +picoVec_t _pico_normalize_vec( picoVec3_t vec ); +void _pico_add_vec( picoVec3_t a, picoVec3_t b, picoVec3_t dest ); +void _pico_subtract_vec( picoVec3_t a, picoVec3_t b, picoVec3_t dest ); +picoVec_t _pico_dot_vec( picoVec3_t a, picoVec3_t b ); +void _pico_cross_vec( picoVec3_t a, picoVec3_t b, picoVec3_t dest ); +picoVec_t _pico_calc_plane( picoVec4_t plane, picoVec3_t a, picoVec3_t b, picoVec3_t c ); +void _pico_scale_vec( picoVec3_t v, float scale, picoVec3_t dest ); +void _pico_scale_vec4( picoVec4_t v, float scale, picoVec4_t dest ); + +/* endian */ +int _pico_big_long( int src ); +short _pico_big_short( short src ); +float _pico_big_float( float src ); + +int _pico_little_long( int src ); +short _pico_little_short( short src ); +float _pico_little_float( float src ); + +/* pico ascii parser */ +picoParser_t *_pico_new_parser( picoByte_t *buffer, int bufSize ); +void _pico_free_parser( picoParser_t *p ); +int _pico_parse_ex( picoParser_t *p, int allowLFs, int handleQuoted ); +char *_pico_parse_first( picoParser_t *p ); +char *_pico_parse( picoParser_t *p, int allowLFs ); +void _pico_parse_skip_rest( picoParser_t *p ); +int _pico_parse_skip_braced( picoParser_t *p ); +int _pico_parse_check( picoParser_t *p, int allowLFs, char *str ); +int _pico_parse_checki( picoParser_t *p, int allowLFs, char *str ); +int _pico_parse_int( picoParser_t *p, int *out ); +int _pico_parse_int_def( picoParser_t *p, int *out, int def ); +int _pico_parse_float( picoParser_t *p, float *out ); +int _pico_parse_float_def( picoParser_t *p, float *out, float def ); +int _pico_parse_vec( picoParser_t *p, picoVec3_t out); +int _pico_parse_vec_def( picoParser_t *p, picoVec3_t out, picoVec3_t def); +int _pico_parse_vec2( picoParser_t *p, picoVec2_t out ); +int _pico_parse_vec2_def( picoParser_t *p, picoVec2_t out, picoVec2_t def ); +int _pico_parse_vec4( picoParser_t *p, picoVec4_t out); +int _pico_parse_vec4_def( picoParser_t *p, picoVec4_t out, picoVec4_t def); + +/* pico memory stream */ +picoMemStream_t *_pico_new_memstream( picoByte_t *buffer, int bufSize ); +void _pico_free_memstream( picoMemStream_t *s ); +int _pico_memstream_read( picoMemStream_t *s, void *buffer, int len ); +int _pico_memstream_getc( picoMemStream_t *s ); +int _pico_memstream_seek( picoMemStream_t *s, long offset, int origin ); +long _pico_memstream_tell( picoMemStream_t *s ); +#define _pico_memstream_eof( _pico_memstream ) ((_pico_memstream)->flag & PICO_IOEOF) +#define _pico_memstream_error( _pico_memstream ) ((_pico_memstream)->flag & PICO_IOERR) + +/* end marker */ +#ifdef __cplusplus +} +#endif + +#endif diff --git a/tools/urt/libs/picomodel/picomodel.c b/tools/urt/libs/picomodel/picomodel.c new file mode 100644 index 00000000..24736798 --- /dev/null +++ b/tools/urt/libs/picomodel/picomodel.c @@ -0,0 +1,2279 @@ +/* ----------------------------------------------------------------------------- + +PicoModel Library + +Copyright (c) 2002, Randy Reddig & seaw0lf +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list +of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +Neither the names of the copyright holders nor the names of its contributors may +be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +----------------------------------------------------------------------------- */ + + + +/* marker */ +#define PICOMODEL_C + + + +/* dependencies */ +#include "picointernal.h" + + + +/* +PicoInit() +initializes the picomodel library +*/ + +int PicoInit( void ) +{ + /* successfully initialized -sea */ + return 1; +} + + + +/* +PicoShutdown() +shuts the pico model library down +*/ + +void PicoShutdown( void ) +{ + /* do something interesting here in the future */ + return; +} + + + +/* +PicoError() +returns last picomodel error code (see PME_* defines) +*/ + +int PicoError( void ) +{ + /* todo: do something here */ + return 0; +} + + + +/* +PicoSetMallocFunc() +sets the ptr to the malloc function +*/ + +void PicoSetMallocFunc( void *(*func)( size_t ) ) +{ + if( func != NULL ) + _pico_ptr_malloc = func; +} + + + +/* +PicoSetFreeFunc() +sets the ptr to the free function +*/ + +void PicoSetFreeFunc( void (*func)( void* ) ) +{ + if( func != NULL ) + _pico_ptr_free = func; +} + + + +/* +PicoSetLoadFileFunc() +sets the ptr to the file load function +*/ + +void PicoSetLoadFileFunc( void (*func)( char*, unsigned char**, int* ) ) +{ + if( func != NULL ) + _pico_ptr_load_file = func; +} + + + +/* +PicoSetFreeFileFunc() +sets the ptr to the free function +*/ + +void PicoSetFreeFileFunc( void (*func)( void* ) ) +{ + if( func != NULL ) + _pico_ptr_free_file = func; +} + + + +/* +PicoSetPrintFunc() +sets the ptr to the print function +*/ + +void PicoSetPrintFunc( void (*func)( int, const char* ) ) +{ + if( func != NULL ) + _pico_ptr_print = func; +} + + + +picoModel_t *PicoModuleLoadModel( const picoModule_t* pm, char* fileName, picoByte_t* buffer, int bufSize, int frameNum ) +{ + char *modelFileName, *remapFileName; + + /* see whether this module can load the model file or not */ + if( pm->canload( fileName, buffer, bufSize ) == PICO_PMV_OK ) + { + /* use loader provided by module to read the model data */ + picoModel_t* model = pm->load( fileName, frameNum, buffer, bufSize ); + if( model == NULL ) + { + _pico_free_file( buffer ); + return NULL; + } + + /* assign pointer to file format module */ + model->module = pm; + + /* get model file name */ + modelFileName = PicoGetModelFileName( model ); + + /* apply model remappings from .remap */ + if( strlen( modelFileName ) ) + { + /* alloc copy of model file name */ + remapFileName = _pico_alloc( strlen( modelFileName ) + 20 ); + if( remapFileName != NULL ) + { + /* copy model file name and change extension */ + strcpy( remapFileName, modelFileName ); + _pico_setfext( remapFileName, "remap" ); + + /* try to remap model; we don't handle the result */ + PicoRemapModel( model, remapFileName ); + + /* free the remap file name string */ + _pico_free( remapFileName ); + } + } + + return model; + } + + return NULL; +} + +/* +PicoLoadModel() +the meat and potatoes function +*/ + +picoModel_t *PicoLoadModel( char *fileName, int frameNum ) +{ + const picoModule_t **modules, *pm; + picoModel_t *model; + picoByte_t *buffer; + int bufSize; + + + /* init */ + model = NULL; + + /* make sure we've got a file name */ + if( fileName == NULL ) + { + _pico_printf( PICO_ERROR, "PicoLoadModel: No filename given (fileName == NULL)" ); + return NULL; + } + + /* load file data (buffer is allocated by host app) */ + _pico_load_file( fileName, &buffer, &bufSize ); + if( bufSize < 0 ) + { + _pico_printf( PICO_ERROR, "PicoLoadModel: Failed loading model %s", fileName ); + return NULL; + } + + /* get ptr to list of supported modules */ + modules = PicoModuleList( NULL ); + + /* run it through the various loader functions and try */ + /* to find a loader that fits the given file data */ + for( ; *modules != NULL; modules++ ) + { + /* get module */ + pm = *modules; + + /* sanity check */ + if( pm == NULL) + break; + + /* module must be able to load */ + if( pm->canload == NULL || pm->load == NULL ) + continue; + + model = PicoModuleLoadModel(pm, fileName, buffer, bufSize, frameNum); + if(model != NULL) + { + /* model was loaded, so break out of loop */ + break; + } + } + + /* free memory used by file buffer */ + if( buffer) + _pico_free_file( buffer ); + + /* return */ + return model; +} + +picoModel_t *PicoModuleLoadModelStream( const picoModule_t* module, void* inputStream, PicoInputStreamReadFunc inputStreamRead, size_t streamLength, int frameNum ) +{ + picoModel_t *model; + picoByte_t *buffer; + int bufSize; + + + /* init */ + model = NULL; + + if( inputStream == NULL ) + { + _pico_printf( PICO_ERROR, "PicoLoadModel: invalid input stream (inputStream == NULL)" ); + return NULL; + } + + if( inputStreamRead == NULL ) + { + _pico_printf( PICO_ERROR, "PicoLoadModel: invalid input stream (inputStreamRead == NULL)" ); + return NULL; + } + + buffer = _pico_alloc(streamLength + 1); + + bufSize = (int)inputStreamRead(inputStream, buffer, streamLength); + buffer[bufSize] = '\0'; + + { + // dummy filename + char fileName[128]; + fileName[0] = '.'; + strncpy(fileName + 1, module->defaultExts[0], 126); + fileName[127] = '\0'; + model = PicoModuleLoadModel(module, fileName, buffer, bufSize, frameNum); + } + + _pico_free(buffer); + + /* return */ + return model; +} + + +/* ---------------------------------------------------------------------------- +models +---------------------------------------------------------------------------- */ + +/* +PicoNewModel() +creates a new pico model +*/ + +picoModel_t *PicoNewModel( void ) +{ + picoModel_t *model; + + /* allocate */ + model = _pico_alloc( sizeof(picoModel_t) ); + if( model == NULL ) + return NULL; + + /* clear */ + memset( model,0,sizeof(picoModel_t) ); + + /* model set up */ + _pico_zero_bounds( model->mins,model->maxs ); + + /* set initial frame count to 1 -sea */ + model->numFrames = 1; + + /* return ptr to new model */ + return model; +} + + + +/* +PicoFreeModel() +frees a model and all associated data +*/ + +void PicoFreeModel( picoModel_t *model ) +{ + int i; + + + /* sanity check */ + if( model == NULL ) + return; + + /* free bits */ + if( model->name ) + _pico_free( model->name ); + + if( model->fileName ) + _pico_free( model->fileName ); + + /* free shaders */ + for( i = 0; i < model->numShaders; i++ ) + PicoFreeShader( model->shader[ i ] ); + free( model->shader ); + + /* free surfaces */ + for( i = 0; i < model->numSurfaces; i++ ) + PicoFreeSurface( model->surface[ i ] ); + free( model->surface ); + + /* free the model */ + _pico_free( model ); +} + + + +/* +PicoAdjustModel() +adjusts a models's memory allocations to handle the requested sizes. +will always grow, never shrink +*/ + +int PicoAdjustModel( picoModel_t *model, int numShaders, int numSurfaces ) +{ + /* dummy check */ + if( model == NULL ) + return 0; + + /* bare minimums */ + /* sea: null surface/shader fix (1s=>0s) */ + if( numShaders < 0 ) + numShaders = 0; + if( numSurfaces < 0 ) + numSurfaces = 0; + + /* additional shaders? */ + while( numShaders > model->maxShaders ) + { + model->maxShaders += PICO_GROW_SHADERS; + if( !_pico_realloc( (void *) &model->shader, model->numShaders * sizeof( *model->shader ), model->maxShaders * sizeof( *model->shader ) ) ) + return 0; + } + + /* set shader count to higher */ + if( numShaders > model->numShaders ) + model->numShaders = numShaders; + + /* additional surfaces? */ + while( numSurfaces > model->maxSurfaces ) + { + model->maxSurfaces += PICO_GROW_SURFACES; + if( !_pico_realloc( (void *) &model->surface, model->numSurfaces * sizeof( *model->surface ), model->maxSurfaces * sizeof( *model->surface ) ) ) + return 0; + } + + /* set shader count to higher */ + if( numSurfaces > model->numSurfaces ) + model->numSurfaces = numSurfaces; + + /* return ok */ + return 1; +} + + + +/* ---------------------------------------------------------------------------- +shaders +---------------------------------------------------------------------------- */ + +/* +PicoNewShader() +creates a new pico shader and returns its index. -sea +*/ + +picoShader_t *PicoNewShader( picoModel_t *model ) +{ + picoShader_t *shader; + + + /* allocate and clear */ + shader = _pico_alloc( sizeof(picoShader_t) ); + if( shader == NULL ) + return NULL; + memset( shader, 0, sizeof(picoShader_t) ); + + /* attach it to the model */ + if( model != NULL ) + { + /* adjust model */ + if( !PicoAdjustModel( model, model->numShaders + 1, 0 ) ) + { + _pico_free( shader ); + return NULL; + } + + /* attach */ + model->shader[ model->numShaders - 1 ] = shader; + shader->model = model; + } + + /* setup default shader colors */ + _pico_set_color( shader->ambientColor,0,0,0,0 ); + _pico_set_color( shader->diffuseColor,255,255,255,1 ); + _pico_set_color( shader->specularColor,0,0,0,0 ); + + /* no need to do this, but i do it anyway */ + shader->transparency = 0; + shader->shininess = 0; + + /* return the newly created shader */ + return shader; +} + + + +/* +PicoFreeShader() +frees a shader and all associated data -sea +*/ + +void PicoFreeShader( picoShader_t *shader ) +{ + /* dummy check */ + if( shader == NULL ) + return; + + /* free bits */ + if( shader->name ) + _pico_free( shader->name ); + if( shader->mapName ) + _pico_free( shader->mapName ); + + /* free the shader */ + _pico_free( shader ); +} + + + +/* +PicoFindShader() +finds a named shader in a model +*/ + +picoShader_t *PicoFindShader( picoModel_t *model, char *name, int caseSensitive ) +{ + int i; + + + /* sanity checks */ + if( model == NULL || name == NULL ) /* sea: null name fix */ + return NULL; + + /* walk list */ + for( i = 0; i < model->numShaders; i++ ) + { + /* skip null shaders or shaders with null names */ + if( model->shader[ i ] == NULL || + model->shader[ i ]->name == NULL ) + continue; + + /* compare the shader name with name we're looking for */ + if( caseSensitive ) + { + if( !strcmp( name, model->shader[ i ]->name ) ) + return model->shader[ i ]; + } + else if( !_pico_stricmp( name, model->shader[ i ]->name ) ) + return model->shader[ i ]; + } + + /* named shader not found */ + return NULL; +} + + + +/* ---------------------------------------------------------------------------- +surfaces +---------------------------------------------------------------------------- */ + +/* +PicoNewSurface() +creates a new pico surface +*/ + +picoSurface_t *PicoNewSurface( picoModel_t *model ) +{ + picoSurface_t *surface; + char surfaceName[64]; + + /* allocate and clear */ + surface = _pico_alloc( sizeof( *surface ) ); + if( surface == NULL ) + return NULL; + memset( surface, 0, sizeof( *surface ) ); + + /* attach it to the model */ + if( model != NULL ) + { + /* adjust model */ + if( !PicoAdjustModel( model, 0, model->numSurfaces + 1 ) ) + { + _pico_free( surface ); + return NULL; + } + + /* attach */ + model->surface[ model->numSurfaces - 1 ] = surface; + surface->model = model; + + /* set default name */ + sprintf( surfaceName, "Unnamed_%d", model->numSurfaces ); + PicoSetSurfaceName( surface, surfaceName ); + } + + /* return */ + return surface; +} + + + +/* +PicoFreeSurface() +frees a surface and all associated data +*/ +void PicoFreeSurface( picoSurface_t *surface ) +{ + int i; + + + /* dummy check */ + if( surface == NULL ) + return; + + /* free bits */ + _pico_free( surface->xyz ); + _pico_free( surface->normal ); + _pico_free( surface->smoothingGroup ); + _pico_free( surface->index ); + _pico_free( surface->faceNormal ); + + if( surface->name ) + _pico_free( surface->name ); + + /* free arrays */ + for( i = 0; i < surface->numSTArrays; i++ ) + _pico_free( surface->st[ i ] ); + free( surface->st ); + for( i = 0; i < surface->numColorArrays; i++ ) + _pico_free( surface->color[ i ] ); + free( surface->color ); + + /* free the surface */ + _pico_free( surface ); +} + + + +/* +PicoAdjustSurface() +adjusts a surface's memory allocations to handle the requested sizes. +will always grow, never shrink +*/ + +int PicoAdjustSurface( picoSurface_t *surface, int numVertexes, int numSTArrays, int numColorArrays, int numIndexes, int numFaceNormals ) +{ + int i; + + + /* dummy check */ + if( surface == NULL ) + return 0; + + /* bare minimums */ + if( numVertexes < 1 ) + numVertexes = 1; + if( numSTArrays < 1 ) + numSTArrays = 1; + if( numColorArrays < 1 ) + numColorArrays = 1; + if( numIndexes < 1 ) + numIndexes = 1; + + /* additional vertexes? */ + while( numVertexes > surface->maxVertexes ) /* fix */ + { + surface->maxVertexes += PICO_GROW_VERTEXES; + if( !_pico_realloc( (void *) &surface->xyz, surface->numVertexes * sizeof( *surface->xyz ), surface->maxVertexes * sizeof( *surface->xyz ) ) ) + return 0; + if( !_pico_realloc( (void *) &surface->normal, surface->numVertexes * sizeof( *surface->normal ), surface->maxVertexes * sizeof( *surface->normal ) ) ) + return 0; + if( !_pico_realloc( (void *) &surface->smoothingGroup, surface->numVertexes * sizeof( *surface->smoothingGroup ), surface->maxVertexes * sizeof( *surface->smoothingGroup ) ) ) + return 0; + for( i = 0; i < surface->numSTArrays; i++ ) + if( !_pico_realloc( (void*) &surface->st[ i ], surface->numVertexes * sizeof( *surface->st[ i ] ), surface->maxVertexes * sizeof( *surface->st[ i ] ) ) ) + return 0; + for( i = 0; i < surface->numColorArrays; i++ ) + if( !_pico_realloc( (void*) &surface->color[ i ], surface->numVertexes * sizeof( *surface->color[ i ] ), surface->maxVertexes * sizeof( *surface->color[ i ] ) ) ) + return 0; + } + + /* set vertex count to higher */ + if( numVertexes > surface->numVertexes ) + surface->numVertexes = numVertexes; + + /* additional st arrays? */ + while( numSTArrays > surface->maxSTArrays ) /* fix */ + { + surface->maxSTArrays += PICO_GROW_ARRAYS; + if( !_pico_realloc( (void*) &surface->st, surface->numSTArrays * sizeof( *surface->st ), surface->maxSTArrays * sizeof( *surface->st ) ) ) + return 0; + while( surface->numSTArrays < numSTArrays ) + { + surface->st[ surface->numSTArrays ] = _pico_alloc( surface->maxVertexes * sizeof( *surface->st[ 0 ] ) ); + memset( surface->st[ surface->numSTArrays ], 0, surface->maxVertexes * sizeof( *surface->st[ 0 ] ) ); + surface->numSTArrays++; + } + } + + /* additional color arrays? */ + while( numColorArrays > surface->maxColorArrays ) /* fix */ + { + surface->maxColorArrays += PICO_GROW_ARRAYS; + if( !_pico_realloc( (void*) &surface->color, surface->numColorArrays * sizeof( *surface->color ), surface->maxColorArrays * sizeof( *surface->color ) ) ) + return 0; + while( surface->numColorArrays < numColorArrays ) + { + surface->color[ surface->numColorArrays ] = _pico_alloc( surface->maxVertexes * sizeof( *surface->color[ 0 ] ) ); + memset( surface->color[ surface->numColorArrays ], 0, surface->maxVertexes * sizeof( *surface->color[ 0 ] ) ); + surface->numColorArrays++; + } + } + + /* additional indexes? */ + while( numIndexes > surface->maxIndexes ) /* fix */ + { + surface->maxIndexes += PICO_GROW_INDEXES; + if( !_pico_realloc( (void*) &surface->index, surface->numIndexes * sizeof( *surface->index ), surface->maxIndexes * sizeof( *surface->index ) ) ) + return 0; + } + + /* set index count to higher */ + if( numIndexes > surface->numIndexes ) + surface->numIndexes = numIndexes; + + /* additional face normals? */ + while( numFaceNormals > surface->maxFaceNormals ) /* fix */ + { + surface->maxFaceNormals += PICO_GROW_FACES; + if( !_pico_realloc( (void *) &surface->faceNormal, surface->numFaceNormals * sizeof( *surface->faceNormal ), surface->maxFaceNormals * sizeof( *surface->faceNormal ) ) ) + return 0; + } + + /* set face normal count to higher */ + if( numFaceNormals > surface->numFaceNormals ) + surface->numFaceNormals = numFaceNormals; + + /* return ok */ + return 1; +} + + +/* PicoFindSurface: + * Finds first matching named surface in a model. + */ +picoSurface_t *PicoFindSurface( + picoModel_t *model, char *name, int caseSensitive ) +{ + int i; + + /* sanity check */ + if( model == NULL || name == NULL ) + return NULL; + + /* walk list */ + for( i = 0; i < model->numSurfaces; i++ ) + { + /* skip null surfaces or surfaces with null names */ + if( model->surface[ i ] == NULL || + model->surface[ i ]->name == NULL ) + continue; + + /* compare the surface name with name we're looking for */ + if (caseSensitive) { + if( !strcmp(name,model->surface[ i ]->name) ) + return model->surface[ i ]; + } else { + if( !_pico_stricmp(name,model->surface[ i ]->name) ) + return model->surface[ i ]; + } + } + /* named surface not found */ + return NULL; +} + + + +/*---------------------------------------------------------------------------- + PicoSet*() Setter Functions +----------------------------------------------------------------------------*/ + +void PicoSetModelName( picoModel_t *model, char *name ) +{ + if( model == NULL || name == NULL ) + return; + if( model->name != NULL ) + _pico_free( model->name ); + + model->name = _pico_clone_alloc( name ); +} + + + +void PicoSetModelFileName( picoModel_t *model, char *fileName ) +{ + if( model == NULL || fileName == NULL ) + return; + if( model->fileName != NULL ) + _pico_free( model->fileName ); + + model->fileName = _pico_clone_alloc( fileName ); +} + + + +void PicoSetModelFrameNum( picoModel_t *model, int frameNum ) +{ + if( model == NULL ) + return; + model->frameNum = frameNum; +} + + + +void PicoSetModelNumFrames( picoModel_t *model, int numFrames ) +{ + if( model == NULL ) + return; + model->numFrames = numFrames; +} + + + +void PicoSetModelData( picoModel_t *model, void *data ) +{ + if( model == NULL ) + return; + model->data = data; +} + + + +void PicoSetShaderName( picoShader_t *shader, char *name ) +{ + if( shader == NULL || name == NULL ) + return; + if( shader->name != NULL ) + _pico_free( shader->name ); + + shader->name = _pico_clone_alloc( name ); +} + + + +void PicoSetShaderMapName( picoShader_t *shader, char *mapName ) +{ + if( shader == NULL || mapName == NULL ) + return; + if( shader->mapName != NULL ) + _pico_free( shader->mapName ); + + shader->mapName = _pico_clone_alloc( mapName ); +} + + + +void PicoSetShaderAmbientColor( picoShader_t *shader, picoColor_t color ) +{ + if( shader == NULL || color == NULL ) + return; + shader->ambientColor[ 0 ] = color[ 0 ]; + shader->ambientColor[ 1 ] = color[ 1 ]; + shader->ambientColor[ 2 ] = color[ 2 ]; + shader->ambientColor[ 3 ] = color[ 3 ]; +} + + + +void PicoSetShaderDiffuseColor( picoShader_t *shader, picoColor_t color ) +{ + if( shader == NULL || color == NULL ) + return; + shader->diffuseColor[ 0 ] = color[ 0 ]; + shader->diffuseColor[ 1 ] = color[ 1 ]; + shader->diffuseColor[ 2 ] = color[ 2 ]; + shader->diffuseColor[ 3 ] = color[ 3 ]; +} + + + +void PicoSetShaderSpecularColor( picoShader_t *shader, picoColor_t color ) +{ + if( shader == NULL || color == NULL ) + return; + shader->specularColor[ 0 ] = color[ 0 ]; + shader->specularColor[ 1 ] = color[ 1 ]; + shader->specularColor[ 2 ] = color[ 2 ]; + shader->specularColor[ 3 ] = color[ 3 ]; +} + + + +void PicoSetShaderTransparency( picoShader_t *shader, float value ) +{ + if( shader == NULL ) + return; + shader->transparency = value; + + /* cap to 0..1 range */ + if (shader->transparency < 0.0) + shader->transparency = 0.0; + if (shader->transparency > 1.0) + shader->transparency = 1.0; +} + + + +void PicoSetShaderShininess( picoShader_t *shader, float value ) +{ + if( shader == NULL ) + return; + shader->shininess = value; + + /* cap to 0..127 range */ + if (shader->shininess < 0.0) + shader->shininess = 0.0; + if (shader->shininess > 127.0) + shader->shininess = 127.0; +} + + + +void PicoSetSurfaceData( picoSurface_t *surface, void *data ) +{ + if( surface == NULL ) + return; + surface->data = data; +} + + + +void PicoSetSurfaceType( picoSurface_t *surface, picoSurfaceType_t type ) +{ + if( surface == NULL ) + return; + surface->type = type; +} + + + +void PicoSetSurfaceName( picoSurface_t *surface, char *name ) +{ + if( surface == NULL || name == NULL ) + return; + if( surface->name != NULL ) + _pico_free( surface->name ); + + surface->name = _pico_clone_alloc( name ); +} + + + +void PicoSetSurfaceShader( picoSurface_t *surface, picoShader_t *shader ) +{ + if( surface == NULL ) + return; + surface->shader = shader; +} + + + +void PicoSetSurfaceXYZ( picoSurface_t *surface, int num, picoVec3_t xyz ) +{ + if( surface == NULL || num < 0 || xyz == NULL ) + return; + if( !PicoAdjustSurface( surface, num + 1, 0, 0, 0, 0 ) ) + return; + _pico_copy_vec( xyz, surface->xyz[ num ] ); + if( surface->model != NULL ) + _pico_expand_bounds( xyz, surface->model->mins, surface->model->maxs ); +} + + + +void PicoSetSurfaceNormal( picoSurface_t *surface, int num, picoVec3_t normal ) +{ + if( surface == NULL || num < 0 || normal == NULL ) + return; + if( !PicoAdjustSurface( surface, num + 1, 0, 0, 0, 0 ) ) + return; + _pico_copy_vec( normal, surface->normal[ num ] ); +} + + + +void PicoSetSurfaceST( picoSurface_t *surface, int array, int num, picoVec2_t st ) +{ + if( surface == NULL || num < 0 || st == NULL ) + return; + if( !PicoAdjustSurface( surface, num + 1, array + 1, 0, 0, 0 ) ) + return; + surface->st[ array ][ num ][ 0 ] = st[ 0 ]; + surface->st[ array ][ num ][ 1 ] = st[ 1 ]; +} + + + +void PicoSetSurfaceColor( picoSurface_t *surface, int array, int num, picoColor_t color ) +{ + if( surface == NULL || num < 0 || color == NULL ) + return; + if( !PicoAdjustSurface( surface, num + 1, 0, array + 1, 0, 0 ) ) + return; + surface->color[ array ][ num ][ 0 ] = color[ 0 ]; + surface->color[ array ][ num ][ 1 ] = color[ 1 ]; + surface->color[ array ][ num ][ 2 ] = color[ 2 ]; + surface->color[ array ][ num ][ 3 ] = color[ 3 ]; +} + + + +void PicoSetSurfaceIndex( picoSurface_t *surface, int num, picoIndex_t index ) +{ + if( surface == NULL || num < 0 ) + return; + if( !PicoAdjustSurface( surface, 0, 0, 0, num + 1, 0 ) ) + return; + surface->index[ num ] = index; +} + + + +void PicoSetSurfaceIndexes( picoSurface_t *surface, int num, picoIndex_t *index, int count ) +{ + if( num < 0 || index == NULL || count < 1 ) + return; + if( !PicoAdjustSurface( surface, 0, 0, 0, num + count, 0 ) ) + return; + memcpy( &surface->index[ num ], index, count * sizeof( surface->index[ num ] ) ); +} + + + +void PicoSetFaceNormal( picoSurface_t *surface, int num, picoVec3_t normal ) +{ + if( surface == NULL || num < 0 || normal == NULL ) + return; + if( !PicoAdjustSurface( surface, 0, 0, 0, 0, num + 1 ) ) + return; + _pico_copy_vec( normal, surface->faceNormal[ num ] ); +} + + +void PicoSetSurfaceSmoothingGroup( picoSurface_t *surface, int num, picoIndex_t smoothingGroup ) +{ + if( num < 0 ) + return; + if( !PicoAdjustSurface( surface, num + 1, 0, 0, 0, 0 ) ) + return; + surface->smoothingGroup[ num ] = smoothingGroup; +} + + +void PicoSetSurfaceSpecial( picoSurface_t *surface, int num, int special ) +{ + if( surface == NULL || num < 0 || num >= PICO_MAX_SPECIAL ) + return; + surface->special[ num ] = special; +} + + + +/*---------------------------------------------------------------------------- + PicoGet*() Getter Functions +----------------------------------------------------------------------------*/ + +char *PicoGetModelName( picoModel_t *model ) +{ + if( model == NULL ) + return NULL; + if( model->name == NULL) + return (char*) ""; + return model->name; +} + + + +char *PicoGetModelFileName( picoModel_t *model ) +{ + if( model == NULL ) + return NULL; + if( model->fileName == NULL) + return (char*) ""; + return model->fileName; +} + + + +int PicoGetModelFrameNum( picoModel_t *model ) +{ + if( model == NULL ) + return 0; + return model->frameNum; +} + + + +int PicoGetModelNumFrames( picoModel_t *model ) +{ + if( model == NULL ) + return 0; + return model->numFrames; +} + + + +void *PicoGetModelData( picoModel_t *model ) +{ + if( model == NULL ) + return NULL; + return model->data; +} + + + +int PicoGetModelNumShaders( picoModel_t *model ) +{ + if( model == NULL ) + return 0; + return model->numShaders; +} + + + +picoShader_t *PicoGetModelShader( picoModel_t *model, int num ) +{ + /* a few sanity checks */ + if( model == NULL ) + return NULL; + if( model->shader == NULL) + return NULL; + if( num < 0 || num >= model->numShaders ) + return NULL; + + /* return the shader */ + return model->shader[ num ]; +} + + + +int PicoGetModelNumSurfaces( picoModel_t *model ) +{ + if( model == NULL ) + return 0; + return model->numSurfaces; +} + + + +picoSurface_t *PicoGetModelSurface( picoModel_t *model, int num ) +{ + /* a few sanity checks */ + if( model == NULL ) + return NULL; + if( model->surface == NULL) + return NULL; + if( num < 0 || num >= model->numSurfaces ) + return NULL; + + /* return the surface */ + return model->surface[ num ]; +} + + + +int PicoGetModelTotalVertexes( picoModel_t *model ) +{ + int i, count; + + + if( model == NULL ) + return 0; + if( model->surface == NULL ) + return 0; + + count = 0; + for( i = 0; i < model->numSurfaces; i++ ) + count += PicoGetSurfaceNumVertexes( model->surface[ i ] ); + + return count; +} + + + +int PicoGetModelTotalIndexes( picoModel_t *model ) +{ + int i, count; + + + if( model == NULL ) + return 0; + if( model->surface == NULL ) + return 0; + + count = 0; + for( i = 0; i < model->numSurfaces; i++ ) + count += PicoGetSurfaceNumIndexes( model->surface[ i ] ); + + return count; +} + + + +char *PicoGetShaderName( picoShader_t *shader ) +{ + if( shader == NULL ) + return NULL; + if( shader->name == NULL) + return (char*) ""; + return shader->name; +} + + + +char *PicoGetShaderMapName( picoShader_t *shader ) +{ + if( shader == NULL ) + return NULL; + if( shader->mapName == NULL) + return (char*) ""; + return shader->mapName; +} + + + +picoByte_t *PicoGetShaderAmbientColor( picoShader_t *shader ) +{ + if( shader == NULL ) + return NULL; + return shader->ambientColor; +} + + + +picoByte_t *PicoGetShaderDiffuseColor( picoShader_t *shader ) +{ + if( shader == NULL ) + return NULL; + return shader->diffuseColor; +} + + + +picoByte_t *PicoGetShaderSpecularColor( picoShader_t *shader ) +{ + if( shader == NULL ) + return NULL; + return shader->specularColor; +} + + + +float PicoGetShaderTransparency( picoShader_t *shader ) +{ + if( shader == NULL ) + return 0.0f; + return shader->transparency; +} + + + +float PicoGetShaderShininess( picoShader_t *shader ) +{ + if( shader == NULL ) + return 0.0f; + return shader->shininess; +} + + + +void *PicoGetSurfaceData( picoSurface_t *surface ) +{ + if( surface == NULL ) + return NULL; + return surface->data; +} + + + +picoSurfaceType_t PicoGetSurfaceType( picoSurface_t *surface ) +{ + if( surface == NULL ) + return PICO_BAD; + return surface->type; +} + + + +char *PicoGetSurfaceName( picoSurface_t *surface ) +{ + if( surface == NULL ) + return NULL; + if( surface->name == NULL ) + return (char*) ""; + return surface->name; +} + + + +picoShader_t *PicoGetSurfaceShader( picoSurface_t *surface ) +{ + if( surface == NULL ) + return NULL; + return surface->shader; +} + + + +int PicoGetSurfaceNumVertexes( picoSurface_t *surface ) +{ + if( surface == NULL ) + return 0; + return surface->numVertexes; +} + + + +picoVec_t *PicoGetSurfaceXYZ( picoSurface_t *surface, int num ) +{ + if( surface == NULL || num < 0 || num > surface->numVertexes ) + return NULL; + return surface->xyz[ num ]; +} + + + +picoVec_t *PicoGetSurfaceNormal( picoSurface_t *surface, int num ) +{ + if( surface == NULL || num < 0 || num > surface->numVertexes ) + return NULL; + return surface->normal[ num ]; +} + + + +picoVec_t *PicoGetSurfaceST( picoSurface_t *surface, int array, int num ) +{ + if( surface == NULL || array < 0 || array > surface->numSTArrays || num < 0 || num > surface->numVertexes ) + return NULL; + return surface->st[ array ][ num ]; +} + + + +picoByte_t *PicoGetSurfaceColor( picoSurface_t *surface, int array, int num ) +{ + if( surface == NULL || array < 0 || array > surface->numColorArrays || num < 0 || num > surface->numVertexes ) + return NULL; + return surface->color[ array ][ num ]; +} + + + +int PicoGetSurfaceNumIndexes( picoSurface_t *surface ) +{ + if( surface == NULL ) + return 0; + return surface->numIndexes; +} + + + +picoIndex_t PicoGetSurfaceIndex( picoSurface_t *surface, int num ) +{ + if( surface == NULL || num < 0 || num > surface->numIndexes ) + return 0; + return surface->index[ num ]; +} + + + +picoIndex_t *PicoGetSurfaceIndexes( picoSurface_t *surface, int num ) +{ + if( surface == NULL || num < 0 || num > surface->numIndexes ) + return NULL; + return &surface->index[ num ]; +} + + +picoVec_t *PicoGetFaceNormal( picoSurface_t *surface, int num ) +{ + if( surface == NULL || num < 0 || num > surface->numFaceNormals ) + return NULL; + return surface->faceNormal[ num ]; +} + +picoIndex_t PicoGetSurfaceSmoothingGroup( picoSurface_t *surface, int num ) +{ + if( surface == NULL || num < 0 || num > surface->numVertexes ) + return -1; + return surface->smoothingGroup[ num ]; +} + + +int PicoGetSurfaceSpecial( picoSurface_t *surface, int num ) +{ + if( surface == NULL || num < 0 || num >= PICO_MAX_SPECIAL ) + return 0; + return surface->special[ num ]; +} + + + +/* ---------------------------------------------------------------------------- +hashtable related functions +---------------------------------------------------------------------------- */ + +/* hashtable code for faster vertex lookups */ +//#define HASHTABLE_SIZE 32768 // 2048 /* power of 2, use & */ +#define HASHTABLE_SIZE 7919 // 32749 // 2039 /* prime, use % */ + +int PicoGetHashTableSize( void ) +{ + return HASHTABLE_SIZE; +} + +#define HASH_USE_EPSILON + +#ifdef HASH_USE_EPSILON +#define HASH_XYZ_EPSILON 0.01f +#define HASH_XYZ_EPSILONSPACE_MULTIPLIER 1.f / HASH_XYZ_EPSILON +#define HASH_ST_EPSILON 0.0001f +#define HASH_NORMAL_EPSILON 0.02f +#endif + +unsigned int PicoVertexCoordGenerateHash( picoVec3_t xyz ) +{ + unsigned int hash = 0; + +#ifndef HASH_USE_EPSILON + hash += ~(*((unsigned int*) &xyz[ 0 ]) << 15); + hash ^= (*((unsigned int*) &xyz[ 0 ]) >> 10); + hash += (*((unsigned int*) &xyz[ 1 ]) << 3); + hash ^= (*((unsigned int*) &xyz[ 1 ]) >> 6); + hash += ~(*((unsigned int*) &xyz[ 2 ]) << 11); + hash ^= (*((unsigned int*) &xyz[ 2 ]) >> 16); +#else + picoVec3_t xyz_epsilonspace; + + _pico_scale_vec( xyz, HASH_XYZ_EPSILONSPACE_MULTIPLIER, xyz_epsilonspace ); + xyz_epsilonspace[ 0 ] = (float)floor(xyz_epsilonspace[ 0 ]); + xyz_epsilonspace[ 1 ] = (float)floor(xyz_epsilonspace[ 1 ]); + xyz_epsilonspace[ 2 ] = (float)floor(xyz_epsilonspace[ 2 ]); + + hash += ~(*((unsigned int*) &xyz_epsilonspace[ 0 ]) << 15); + hash ^= (*((unsigned int*) &xyz_epsilonspace[ 0 ]) >> 10); + hash += (*((unsigned int*) &xyz_epsilonspace[ 1 ]) << 3); + hash ^= (*((unsigned int*) &xyz_epsilonspace[ 1 ]) >> 6); + hash += ~(*((unsigned int*) &xyz_epsilonspace[ 2 ]) << 11); + hash ^= (*((unsigned int*) &xyz_epsilonspace[ 2 ]) >> 16); +#endif + + //hash = hash & (HASHTABLE_SIZE-1); + hash = hash % (HASHTABLE_SIZE); + return hash; +} + +picoVertexCombinationHash_t **PicoNewVertexCombinationHashTable( void ) +{ + picoVertexCombinationHash_t **hashTable = _pico_alloc( HASHTABLE_SIZE * sizeof(picoVertexCombinationHash_t*) ); + + memset( hashTable, 0, HASHTABLE_SIZE * sizeof(picoVertexCombinationHash_t*) ); + + return hashTable; +} + +void PicoFreeVertexCombinationHashTable( picoVertexCombinationHash_t **hashTable ) +{ + int i; + picoVertexCombinationHash_t *vertexCombinationHash; + picoVertexCombinationHash_t *nextVertexCombinationHash; + + /* dummy check */ + if (hashTable == NULL) + return; + + for( i = 0; i < HASHTABLE_SIZE; i++ ) + { + if (hashTable[ i ]) + { + nextVertexCombinationHash = NULL; + + for( vertexCombinationHash = hashTable[ i ]; vertexCombinationHash; vertexCombinationHash = nextVertexCombinationHash ) + { + nextVertexCombinationHash = vertexCombinationHash->next; + if (vertexCombinationHash->data != NULL) + { + _pico_free( vertexCombinationHash->data ); + } + _pico_free( vertexCombinationHash ); + } + } + } + + _pico_free( hashTable ); +} + +picoVertexCombinationHash_t *PicoFindVertexCombinationInHashTable( picoVertexCombinationHash_t **hashTable, picoVec3_t xyz, picoVec3_t normal, picoVec3_t st, picoColor_t color ) +{ + unsigned int hash; + picoVertexCombinationHash_t *vertexCombinationHash; + + /* dumy check */ + if (hashTable == NULL || xyz == NULL || normal == NULL || st == NULL || color == NULL ) + return NULL; + + hash = PicoVertexCoordGenerateHash( xyz ); + + for( vertexCombinationHash = hashTable[ hash ]; vertexCombinationHash; vertexCombinationHash = vertexCombinationHash->next ) + { +#ifndef HASH_USE_EPSILON + /* check xyz */ + if( (vertexCombinationHash->vcd.xyz[ 0 ] != xyz[ 0 ] || vertexCombinationHash->vcd.xyz[ 1 ] != xyz[ 1 ] || vertexCombinationHash->vcd.xyz[ 2 ] != xyz[ 2 ]) ) + continue; + + /* check normal */ + if( (vertexCombinationHash->vcd.normal[ 0 ] != normal[ 0 ] || vertexCombinationHash->vcd.normal[ 1 ] != normal[ 1 ] || vertexCombinationHash->vcd.normal[ 2 ] != normal[ 2 ]) ) + continue; + + /* check st */ + if( vertexCombinationHash->vcd.st[ 0 ] != st[ 0 ] || vertexCombinationHash->vcd.st[ 1 ] != st[ 1 ] ) + continue; +#else + /* check xyz */ + if( ( fabs(xyz[ 0 ] - vertexCombinationHash->vcd.xyz[ 0 ]) ) > HASH_XYZ_EPSILON || + ( fabs(xyz[ 1 ] - vertexCombinationHash->vcd.xyz[ 1 ]) ) > HASH_XYZ_EPSILON || + ( fabs(xyz[ 2 ] - vertexCombinationHash->vcd.xyz[ 2 ]) ) > HASH_XYZ_EPSILON ) + continue; + + /* check normal */ + if( ( fabs(normal[ 0 ] - vertexCombinationHash->vcd.normal[ 0 ]) ) > HASH_NORMAL_EPSILON || + ( fabs(normal[ 1 ] - vertexCombinationHash->vcd.normal[ 1 ]) ) > HASH_NORMAL_EPSILON || + ( fabs(normal[ 2 ] - vertexCombinationHash->vcd.normal[ 2 ]) ) > HASH_NORMAL_EPSILON ) + continue; + + /* check st */ + if( ( fabs(st[ 0 ] - vertexCombinationHash->vcd.st[ 0 ]) ) > HASH_ST_EPSILON || + ( fabs(st[ 1 ] - vertexCombinationHash->vcd.st[ 1 ]) ) > HASH_ST_EPSILON ) + continue; +#endif + + /* check color */ + if( *((int*) vertexCombinationHash->vcd.color) != *((int*) color) ) + continue; + + /* gotcha */ + return vertexCombinationHash; + } + + return NULL; +} + +picoVertexCombinationHash_t *PicoAddVertexCombinationToHashTable( picoVertexCombinationHash_t **hashTable, picoVec3_t xyz, picoVec3_t normal, picoVec3_t st, picoColor_t color, picoIndex_t index ) +{ + unsigned int hash; + picoVertexCombinationHash_t *vertexCombinationHash; + + /* dumy check */ + if (hashTable == NULL || xyz == NULL || normal == NULL || st == NULL || color == NULL ) + return NULL; + + vertexCombinationHash = _pico_alloc( sizeof(picoVertexCombinationHash_t) ); + + if (!vertexCombinationHash) + return NULL; + + hash = PicoVertexCoordGenerateHash( xyz ); + + _pico_copy_vec( xyz, vertexCombinationHash->vcd.xyz ); + _pico_copy_vec( normal, vertexCombinationHash->vcd.normal ); + _pico_copy_vec2( st, vertexCombinationHash->vcd.st ); + _pico_copy_color( color, vertexCombinationHash->vcd.color ); + vertexCombinationHash->index = index; + vertexCombinationHash->data = NULL; + vertexCombinationHash->next = hashTable[ hash ]; + hashTable[ hash ] = vertexCombinationHash; + + return vertexCombinationHash; +} + +/* ---------------------------------------------------------------------------- +specialized routines +---------------------------------------------------------------------------- */ + +/* +PicoFindSurfaceVertex() +finds a vertex matching the set parameters +fixme: needs non-naive algorithm +*/ + +int PicoFindSurfaceVertexNum( picoSurface_t *surface, picoVec3_t xyz, picoVec3_t normal, int numSTs, picoVec2_t *st, int numColors, picoColor_t *color, picoIndex_t smoothingGroup) +{ + int i, j; + +// Sys_Printf(" %f %f %f\n", normal[0] , normal[1] , normal[2] ); + + /* dummy check */ + if( surface == NULL || surface->numVertexes <= 0 ) + return -1; + + /* walk vertex list */ + for( i = 0; i < surface->numVertexes; i++ ) + { + /* check xyz */ + if( xyz != NULL && (surface->xyz[ i ][ 0 ] != xyz[ 0 ] || surface->xyz[ i ][ 1 ] != xyz[ 1 ] || surface->xyz[ i ][ 2 ] != xyz[ 2 ]) ) + continue; + + /* check normal */ + if( normal != NULL && (surface->normal[ i ][ 0 ] != normal[ 0 ] || surface->normal[ i ][ 1 ] != normal[ 1 ] || surface->normal[ i ][ 2 ] != normal[ 2 ]) ) + continue; + + /* check normal */ + if( surface->smoothingGroup[ i ] != smoothingGroup ) + continue; + + /* check st */ + if( numSTs > 0 && st != NULL ) + { + for( j = 0; j < numSTs; j++ ) + { + if( surface->st[ j ][ i ][ 0 ] != st[ j ][ 0 ] || surface->st[ j ][ i ][ 1 ] != st[ j ][ 1 ] ) + break; + } + if( j != numSTs ) + continue; + } + + /* check color */ + if( numColors > 0 && color != NULL ) + { + for( j = 0; j < numSTs; j++ ) + { + if( *((int*) surface->color[ j ]) != *((int*) color[ j ]) ) + break; + } + if( j != numColors ) + continue; + } + + /* vertex matches */ + return i; + } + + /* nada */ + return -1; +} + + + + +typedef struct _IndexArray IndexArray; +struct _IndexArray +{ + picoIndex_t* data; + picoIndex_t* last; +}; + +void indexarray_push_back(IndexArray* self, picoIndex_t value) +{ + *self->last++ = value; +} + +size_t indexarray_size(IndexArray* self) +{ + return self->last - self->data; +} + +void indexarray_reserve(IndexArray* self, size_t size) +{ + self->data = self->last = _pico_calloc(size, sizeof(picoIndex_t)); +} + +void indexarray_clear(IndexArray* self) +{ + _pico_free(self->data); +} + +typedef struct _BinaryTreeNode BinaryTreeNode; +struct _BinaryTreeNode +{ + picoIndex_t left; + picoIndex_t right; +}; + +typedef struct _BinaryTree BinaryTree; +struct _BinaryTree +{ + BinaryTreeNode* data; + BinaryTreeNode* last; +}; + +void binarytree_extend(BinaryTree* self) +{ + self->last->left = 0; + self->last->right = 0; + ++self->last; +} + +size_t binarytree_size(BinaryTree* self) +{ + return self->last - self->data; +} + +void binarytree_reserve(BinaryTree* self, size_t size) +{ + self->data = self->last = _pico_calloc(size, sizeof(BinaryTreeNode)); +} + +void binarytree_clear(BinaryTree* self) +{ + _pico_free(self->data); +} + +typedef int (*LessFunc)(void*, picoIndex_t, picoIndex_t); + +typedef struct _UniqueIndices UniqueIndices; +struct _UniqueIndices +{ + BinaryTree tree; + IndexArray indices; + LessFunc lessFunc; + void* lessData; +}; + +size_t UniqueIndices_size(UniqueIndices* self) +{ + return binarytree_size(&self->tree); +} + +void UniqueIndices_reserve(UniqueIndices* self, size_t size) +{ + binarytree_reserve(&self->tree, size); + indexarray_reserve(&self->indices, size); +} + +void UniqueIndices_init(UniqueIndices* self, LessFunc lessFunc, void* lessData) +{ + self->lessFunc = lessFunc; + self->lessData = lessData; +} + +void UniqueIndices_destroy(UniqueIndices* self) +{ + binarytree_clear(&self->tree); + indexarray_clear(&self->indices); +} + + +picoIndex_t UniqueIndices_find_or_insert(UniqueIndices* self, picoIndex_t value) +{ + picoIndex_t index = 0; + + for(;;) + { + if(self->lessFunc(self->lessData, value, self->indices.data[index])) + { + BinaryTreeNode* node = self->tree.data + index; + if(node->left != 0) + { + index = node->left; + continue; + } + else + { + node->left = (picoIndex_t)binarytree_size(&self->tree); + binarytree_extend(&self->tree); + indexarray_push_back(&self->indices, value); + return node->left; + } + } + if(self->lessFunc(self->lessData, self->indices.data[index], value)) + { + BinaryTreeNode* node = self->tree.data + index; + if(node->right != 0) + { + index = node->right; + continue; + } + else + { + node->right = (picoIndex_t)binarytree_size(&self->tree); + binarytree_extend(&self->tree); + indexarray_push_back(&self->indices, value); + return node->right; + } + } + + return index; + } +} + +picoIndex_t UniqueIndices_insert(UniqueIndices* self, picoIndex_t value) +{ + if(self->tree.data == self->tree.last) + { + binarytree_extend(&self->tree); + indexarray_push_back(&self->indices, value); + return 0; + } + else + { + return UniqueIndices_find_or_insert(self, value); + } +} + +typedef struct picoSmoothVertices_s picoSmoothVertices_t; +struct picoSmoothVertices_s +{ + picoVec3_t* xyz; + picoIndex_t* smoothingGroups; +}; + +int lessSmoothVertex(void* data, picoIndex_t first, picoIndex_t second) +{ + picoSmoothVertices_t* smoothVertices = data; + + if(smoothVertices->xyz[first][0] != smoothVertices->xyz[second][0]) + { + return smoothVertices->xyz[first][0] < smoothVertices->xyz[second][0]; + } + if(smoothVertices->xyz[first][1] != smoothVertices->xyz[second][1]) + { + return smoothVertices->xyz[first][1] < smoothVertices->xyz[second][1]; + } + if(smoothVertices->xyz[first][2] != smoothVertices->xyz[second][2]) + { + return smoothVertices->xyz[first][2] < smoothVertices->xyz[second][2]; + } + if(smoothVertices->smoothingGroups[first] != smoothVertices->smoothingGroups[second]) + { + return smoothVertices->smoothingGroups[first] < smoothVertices->smoothingGroups[second]; + } + return 0; +} + +void _pico_vertices_combine_shared_normals(picoVec3_t* xyz, picoIndex_t* smoothingGroups, picoVec3_t* normals, picoIndex_t numVertices) +{ + UniqueIndices vertices; + IndexArray indices; + picoSmoothVertices_t smoothVertices = { xyz, smoothingGroups }; + UniqueIndices_init(&vertices, lessSmoothVertex, &smoothVertices); + UniqueIndices_reserve(&vertices, numVertices); + indexarray_reserve(&indices, numVertices); + + + { + picoIndex_t i = 0; + for(; i < numVertices; ++i) + { + size_t size = UniqueIndices_size(&vertices); + picoIndex_t index = UniqueIndices_insert(&vertices, i); + if((size_t)index != size) + { + float* normal = normals[vertices.indices.data[index]]; + _pico_add_vec(normal, normals[i], normal); + } + indexarray_push_back(&indices, index); + } + } + + { + picoIndex_t maxIndex = 0; + picoIndex_t* i = indices.data; + for(; i != indices.last; ++i) + { + if(*i <= maxIndex) + { + _pico_copy_vec(normals[vertices.indices.data[*i]], normals[i - indices.data]); + } + else + { + maxIndex = *i; + } + } + } + + UniqueIndices_destroy(&vertices); + indexarray_clear(&indices); +} + +typedef picoVec3_t* picoNormalIter_t; +typedef picoIndex_t* picoIndexIter_t; + +void _pico_triangles_generate_weighted_normals(picoIndexIter_t first, picoIndexIter_t end, picoVec3_t* xyz, picoVec3_t* normals) +{ + for(; first != end; first += 3) + { + picoVec3_t weightedNormal; + { + float* a = xyz[*(first + 0)]; + float* b = xyz[*(first + 1)]; + float* c = xyz[*(first + 2)]; + picoVec3_t ba, ca; + _pico_subtract_vec( b, a, ba ); + _pico_subtract_vec( c, a, ca ); + _pico_cross_vec( ca, ba, weightedNormal ); + } + { + int j = 0; + for(; j < 3; ++j) + { + float* normal = normals[*(first + j)]; + _pico_add_vec(weightedNormal, normal, normal); + } + } + } +} + +void _pico_normals_zero(picoNormalIter_t first, picoNormalIter_t last) +{ + for(; first != last; ++first) + { + _pico_zero_vec(*first); + } +} + +void _pico_normals_normalize(picoNormalIter_t first, picoNormalIter_t last) +{ + for(; first != last; ++first) + { + _pico_normalize_vec(*first); + } +} + +double _pico_length_vec( picoVec3_t vec ) +{ + return sqrt( vec[ 0 ] * vec[ 0 ] + vec[ 1 ] * vec[ 1 ] + vec[ 2 ] * vec[ 2 ] ); +} + +#define NORMAL_UNIT_LENGTH_EPSILON 0.01 +#define FLOAT_EQUAL_EPSILON(f, other, epsilon) (fabs(f - other) < epsilon) + +int _pico_normal_is_unit_length(picoVec3_t normal) +{ + return FLOAT_EQUAL_EPSILON(_pico_length_vec(normal), 1.0, NORMAL_UNIT_LENGTH_EPSILON); +} + +int _pico_normal_within_tolerance(picoVec3_t normal, picoVec3_t other) +{ + return _pico_dot_vec(normal, other) > 0.0f; +} + + +void _pico_normals_assign_generated_normals(picoNormalIter_t first, picoNormalIter_t last, picoNormalIter_t generated) +{ + for(; first != last; ++first, ++generated) + { + //27 - fix for badly generated normals thing. + // if(!_pico_normal_is_unit_length(*first) || !_pico_normal_within_tolerance(*first, *generated)) + { + _pico_copy_vec(*generated, *first); + } + } +} + +void PicoFixSurfaceNormals(picoSurface_t* surface) +{ + picoVec3_t* normals = (picoVec3_t*)_pico_calloc(surface->numVertexes, sizeof(picoVec3_t)); + + _pico_normals_zero(normals, normals + surface->numVertexes); + + //Just build standard no sg normals for now + _pico_triangles_generate_weighted_normals(surface->index, surface->index + surface->numIndexes, surface->xyz, normals); + _pico_vertices_combine_shared_normals(surface->xyz, surface->smoothingGroup, normals, surface->numVertexes); + + _pico_normals_normalize(normals, normals + surface->numVertexes); + + _pico_normals_assign_generated_normals(surface->normal, surface->normal + surface->numVertexes, normals); + + _pico_free(normals); +} + + +/* +PicoRemapModel() - sea +remaps model material/etc. information using the remappings +contained in the given 'remapFile' (full path to the ascii file to open) +returns 1 on success or 0 on error +*/ + +#define _prm_error_return \ +{ \ + _pico_free_parser( p ); \ + _pico_free_file( remapBuffer ); \ + return 0; \ +} + +int PicoRemapModel( picoModel_t *model, char *remapFile ) +{ + picoParser_t *p; + picoByte_t *remapBuffer; + int remapBufSize; + + + /* sanity checks */ + if( model == NULL || remapFile == NULL ) + return 0; + + /* load remap file contents */ + _pico_load_file( remapFile,&remapBuffer,&remapBufSize ); + + /* check result */ + if( remapBufSize == 0 ) + return 1; /* file is empty: no error */ + if( remapBufSize < 0 ) + return 0; /* load failed: error */ + + /* create a new pico parser */ + p = _pico_new_parser( remapBuffer, remapBufSize ); + if (p == NULL) + { + /* ram is really cheap nowadays... */ + _prm_error_return; + } + + /* doo teh parse */ + while( 1 ) + { + /* get next token in remap file */ + if (!_pico_parse( p,1 )) + break; + + /* skip over c++ style comment lines */ + if (!_pico_stricmp(p->token,"//")) + { + _pico_parse_skip_rest( p ); + continue; + } + + /* block for quick material shader name remapping */ + /* materials { "m" (=>|->|=) "s" } */ + if( !_pico_stricmp(p->token, "materials" ) ) + { + int level = 1; + + /* check bracket */ + if (!_pico_parse_check( p,1,"{" )) + _prm_error_return; + + /* process assignments */ + while( 1 ) + { + picoShader_t *shader; + char *materialName; + + + /* get material name */ + if (_pico_parse( p,1 ) == NULL) break; + if (!strlen(p->token)) continue; + materialName = _pico_clone_alloc( p->token ); + if (materialName == NULL) + _prm_error_return; + + /* handle levels */ + if (p->token[0] == '{') level++; + if (p->token[0] == '}') level--; + if (!level) break; + + /* get next token (assignment token or shader name) */ + if (!_pico_parse( p,0 )) + { + _pico_free( materialName ); + _prm_error_return; + } + /* skip assignment token (if present) */ + if (!strcmp(p->token,"=>") || + !strcmp(p->token,"->") || + !strcmp(p->token,"=")) + { + /* simply grab the next token */ + if (!_pico_parse( p,0 )) + { + _pico_free( materialName ); + _prm_error_return; + } + } + /* try to find material by name */ + shader = PicoFindShader( model,materialName,0 ); + + /* we've found a material matching the name */ + if (shader != NULL) + { + PicoSetShaderName( shader,p->token ); + } + /* free memory used by material name */ + _pico_free( materialName ); + + /* skip rest */ + _pico_parse_skip_rest( p ); + } + } + /* block for detailed single material remappings */ + /* materials[ "m" ] { key data... } */ + else if (!_pico_stricmp(p->token,"materials[")) + { + picoShader_t *shader; + char *tempMaterialName; + int level = 1; + + /* get material name */ + if (!_pico_parse( p,0 )) + _prm_error_return; + + /* temporary copy of material name */ + tempMaterialName = _pico_clone_alloc( p->token ); + if (tempMaterialName == NULL) + _prm_error_return; + + /* check square closing bracket */ + if (!_pico_parse_check( p,0,"]" )) + _prm_error_return; + + /* try to find material by name */ + shader = PicoFindShader( model,tempMaterialName,0 ); + + /* free memory used by temporary material name */ + _pico_free( tempMaterialName ); + + /* we haven't found a material matching the name */ + /* so we simply skip the braced section now and */ + /* continue parsing with the next main token */ + if (shader == NULL) + { + _pico_parse_skip_braced( p ); + continue; + } + /* check opening bracket */ + if (!_pico_parse_check( p,1,"{" )) + _prm_error_return; + + /* process material info keys */ + while( 1 ) + { + /* get key name */ + if (_pico_parse( p,1 ) == NULL) break; + if (!strlen(p->token)) continue; + + /* handle levels */ + if (p->token[0] == '{') level++; + if (p->token[0] == '}') level--; + if (!level) break; + + /* remap shader name */ + if (!_pico_stricmp(p->token,"shader")) + { + if (!_pico_parse( p,0 )) _prm_error_return; + PicoSetShaderName( shader,p->token ); + } + /* remap shader map name */ + else if (!_pico_stricmp(p->token,"mapname")) + { + if (!_pico_parse( p,0 )) _prm_error_return; + PicoSetShaderMapName( shader,p->token ); + } + /* remap shader's ambient color */ + else if (!_pico_stricmp(p->token,"ambient")) + { + picoColor_t color; + picoVec3_t v; + + /* get vector from parser */ + if (!_pico_parse_vec( p,v )) _prm_error_return; + + /* store as color */ + color[ 0 ] = (picoByte_t)v[ 0 ]; + color[ 1 ] = (picoByte_t)v[ 1 ]; + color[ 2 ] = (picoByte_t)v[ 2 ]; + + /* set new ambient color */ + PicoSetShaderAmbientColor( shader,color ); + } + /* remap shader's diffuse color */ + else if (!_pico_stricmp(p->token,"diffuse")) + { + picoColor_t color; + picoVec3_t v; + + /* get vector from parser */ + if (!_pico_parse_vec( p,v )) _prm_error_return; + + /* store as color */ + color[ 0 ] = (picoByte_t)v[ 0 ]; + color[ 1 ] = (picoByte_t)v[ 1 ]; + color[ 2 ] = (picoByte_t)v[ 2 ]; + + /* set new ambient color */ + PicoSetShaderDiffuseColor( shader,color ); + } + /* remap shader's specular color */ + else if (!_pico_stricmp(p->token,"specular")) + { + picoColor_t color; + picoVec3_t v; + + /* get vector from parser */ + if (!_pico_parse_vec( p,v )) _prm_error_return; + + /* store as color */ + color[ 0 ] = (picoByte_t)v[ 0 ]; + color[ 1 ] = (picoByte_t)v[ 1 ]; + color[ 2 ] = (picoByte_t)v[ 2 ]; + + /* set new ambient color */ + PicoSetShaderSpecularColor( shader,color ); + } + /* skip rest */ + _pico_parse_skip_rest( p ); + } + } + /* end 'materials[' */ + } + + /* free both parser and file buffer */ + _pico_free_parser( p ); + _pico_free_file( remapBuffer ); + + /* return with success */ + return 1; +} + + +/* +PicoAddTriangleToModel() - jhefty +A nice way to add individual triangles to the model. +Chooses an appropriate surface based on the shader, or adds a new surface if necessary +*/ + +void PicoAddTriangleToModel( picoModel_t *model, picoVec3_t** xyz, picoVec3_t** normals, + int numSTs, picoVec2_t **st, int numColors, picoColor_t **colors, + picoShader_t* shader, picoIndex_t* smoothingGroup,int submodel ) +{ + int i,j; + int vertDataIndex; + picoSurface_t* workSurface = NULL; + + /* see if a surface already has the shader */ + for ( i = 0 ; i < model->numSurfaces ; i++ ) + { + workSurface = model->surface[i]; + if (( workSurface->shader == shader )&&(workSurface->submodel==submodel)) + { + break; + } + } + + /* no surface uses this shader yet, so create a new surface */ + if ( !workSurface || i >=model->numSurfaces ) + { + /* create a new surface in the model for the unique shader */ + workSurface = PicoNewSurface(model); + if ( !workSurface ) + { + _pico_printf ( PICO_ERROR , "Could not allocate a new surface!\n" ); + return; + } + + /* do surface setup */ + PicoSetSurfaceType( workSurface, PICO_TRIANGLES ); + PicoSetSurfaceName( workSurface, shader->name ); + PicoSetSurfaceShader( workSurface, shader ); + workSurface->submodel=submodel; + } + + /* add the triangle data to the surface */ + for ( i = 0 ; i < 3 ; i++ ) + { + /* get the next free spot in the index array */ + int newVertIndex = PicoGetSurfaceNumIndexes ( workSurface ); + + /* get the index of the vertex that we're going to store at newVertIndex */ + vertDataIndex = -1;// PicoFindSurfaceVertexNum ( workSurface , *xyz[i] , *normals[i] , numSTs , st[i] , numColors , colors[i], smoothingGroup[i]); + + /* the vertex wasn't found, so create a new vertex in the pool from the data we have */ + if ( vertDataIndex == -1 ) + { + /* find the next spot for a new vertex */ + vertDataIndex = PicoGetSurfaceNumVertexes ( workSurface ); + + /* assign the data to it */ + PicoSetSurfaceXYZ ( workSurface ,vertDataIndex , *xyz[i] ); + PicoSetSurfaceNormal ( workSurface , vertDataIndex , *normals[i] ); + + /* make sure to copy over all available ST's and colors for the vertex */ + for ( j = 0 ; j < numColors ; j++ ) + { + PicoSetSurfaceColor( workSurface , j , vertDataIndex , colors[i][j] ); + } + for ( j = 0 ; j < numSTs ; j++ ) + { + PicoSetSurfaceST ( workSurface , j , vertDataIndex , st[i][j] ); + } + + PicoSetSurfaceSmoothingGroup ( workSurface , vertDataIndex , smoothingGroup[i] ); + } + + /* add this vertex to the triangle */ + PicoSetSurfaceIndex ( workSurface , newVertIndex , vertDataIndex ); + } +} + + diff --git a/tools/urt/libs/picomodel/picomodel.dsp b/tools/urt/libs/picomodel/picomodel.dsp new file mode 100644 index 00000000..8d722777 --- /dev/null +++ b/tools/urt/libs/picomodel/picomodel.dsp @@ -0,0 +1,210 @@ +# Microsoft Developer Studio Project File - Name="picomodel" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=picomodel - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "picomodel.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "picomodel.mak" CFG="picomodel - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "picomodel - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "picomodel - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "picomodel" +# PROP Scc_LocalPath ".." +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "picomodel - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +MTL=midl.exe +F90=df.exe +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c +# ADD CPP /nologo /MD /W3 /Zi /O2 /I ".." /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FR /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "picomodel - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +MTL=midl.exe +F90=df.exe +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /ZI /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FR /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "picomodel - Win32 Release" +# Name "picomodel - Win32 Debug" +# Begin Group "src" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Group "lwo" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\lwo\clip.c +# End Source File +# Begin Source File + +SOURCE=.\lwo\envelope.c +# End Source File +# Begin Source File + +SOURCE=.\lwo\list.c +# End Source File +# Begin Source File + +SOURCE=.\lwo\lwio.c +# End Source File +# Begin Source File + +SOURCE=.\lwo\lwo2.c +# End Source File +# Begin Source File + +SOURCE=.\lwo\lwob.c +# End Source File +# Begin Source File + +SOURCE=.\lwo\pntspols.c +# End Source File +# Begin Source File + +SOURCE=.\lwo\surface.c +# End Source File +# Begin Source File + +SOURCE=.\lwo\vecmath.c +# End Source File +# Begin Source File + +SOURCE=.\lwo\vmap.c +# End Source File +# End Group +# Begin Source File + +SOURCE=.\picointernal.c +# End Source File +# Begin Source File + +SOURCE=.\picomodel.c +# End Source File +# Begin Source File + +SOURCE=.\picomodules.c +# End Source File +# Begin Source File + +SOURCE=.\pm_3ds.c +# End Source File +# Begin Source File + +SOURCE=.\pm_ase.c +# End Source File +# Begin Source File + +SOURCE=.\pm_fm.c +# End Source File +# Begin Source File + +SOURCE=.\pm_lwo.c +# End Source File +# Begin Source File + +SOURCE=.\pm_md2.c +# End Source File +# Begin Source File + +SOURCE=.\pm_md3.c +# End Source File +# Begin Source File + +SOURCE=.\pm_mdc.c +# End Source File +# Begin Source File + +SOURCE=.\pm_ms3d.c +# End Source File +# Begin Source File + +SOURCE=.\pm_obj.c +# End Source File +# Begin Source File + +SOURCE=.\pm_terrain.c +# End Source File +# End Group +# Begin Group "include" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\lwo\lwo2.h +# End Source File +# Begin Source File + +SOURCE=.\picointernal.h +# End Source File +# Begin Source File + +SOURCE=..\picomodel.h +# End Source File +# Begin Source File + +SOURCE=.\pm_fm.h +# End Source File +# End Group +# End Target +# End Project diff --git a/tools/urt/libs/picomodel/picomodel.plg b/tools/urt/libs/picomodel/picomodel.plg new file mode 100644 index 00000000..c8a6fffe --- /dev/null +++ b/tools/urt/libs/picomodel/picomodel.plg @@ -0,0 +1,16 @@ + + +
+

Build Log

+

+--------------------Configuration: picomodel - Win32 Debug-------------------- +

+

Command Lines

+ + + +

Results

+picomodel.lib - 0 error(s), 0 warning(s) +
+ + diff --git a/tools/urt/libs/picomodel/picomodel.vcproj b/tools/urt/libs/picomodel/picomodel.vcproj new file mode 100644 index 00000000..442e02c4 --- /dev/null +++ b/tools/urt/libs/picomodel/picomodel.vcproj @@ -0,0 +1,713 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/urt/libs/picomodel/picomodules.c b/tools/urt/libs/picomodel/picomodules.c new file mode 100644 index 00000000..f5632a44 --- /dev/null +++ b/tools/urt/libs/picomodel/picomodules.c @@ -0,0 +1,94 @@ +/* ----------------------------------------------------------------------------- + +PicoModel Library + +Copyright (c) 2002, Randy Reddig & seaw0lf +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list +of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +Neither the names of the copyright holders nor the names of its contributors may +be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +----------------------------------------------------------------------------- */ + + + +/* marker */ +#define PICOMODULES_C + + + +/* dependencies */ +#include "picointernal.h" + + + +/* external modules */ +extern const picoModule_t picoModuleMD3; +extern const picoModule_t picoModule3DS; +extern const picoModule_t picoModuleASE; +extern const picoModule_t picoModuleOBJ; +extern const picoModule_t picoModuleMS3D; +extern const picoModule_t picoModuleMDC; +extern const picoModule_t picoModuleMD2; +extern const picoModule_t picoModuleFM; +extern const picoModule_t picoModuleLWO; +extern const picoModule_t picoModuleTerrain; + + + +/* list of all supported file format modules */ +const picoModule_t *picoModules[] = +{ + &picoModuleMD3, /* quake3 arena md3 */ + &picoModule3DS, /* autodesk 3ds */ + &picoModuleASE, /* autodesk ase */ + &picoModuleMS3D, /* milkshape3d */ + &picoModuleMDC, /* return to castle wolfenstein mdc */ + &picoModuleMD2, /* quake2 md2 */ + &picoModuleFM, /* heretic2 fm */ + &picoModuleLWO, /* lightwave object */ + &picoModuleTerrain, /* picoterrain object */ + &picoModuleOBJ, /* wavefront object */ + NULL /* arnold */ +}; + + + +/* +PicoModuleList() +returns a pointer to the module list and optionally stores +the number of supported modules in 'numModules'. Note that +this param can be NULL when the count is not needed. +*/ + +const picoModule_t **PicoModuleList( int *numModules ) +{ + /* get module count */ + if( numModules != NULL ) + for( (*numModules) = 0; picoModules[ *numModules ] != NULL; (*numModules)++ ); + + /* return list of modules */ + return (const picoModule_t**) picoModules; +} diff --git a/tools/urt/libs/picomodel/pm_3ds.c b/tools/urt/libs/picomodel/pm_3ds.c new file mode 100644 index 00000000..2b19f10e --- /dev/null +++ b/tools/urt/libs/picomodel/pm_3ds.c @@ -0,0 +1,777 @@ +/* ----------------------------------------------------------------------------- + +PicoModel Library + +Copyright (c) 2002, Randy Reddig & seaw0lf +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list +of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +Neither the names of the copyright holders nor the names of its contributors may +be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +----------------------------------------------------------------------------- */ + + + +/* marker */ +#define PM_3DS_C + +/* dependencies */ +#include "picointernal.h" + +/* ydnar */ +static picoColor_t white = { 255,255,255,255 }; + +/* remarks: + * - 3ds file version is stored in pico special field 0 on load (ydnar: removed) + * todo: + * - sometimes there is one unnamed surface 0 having 0 verts as + * well as 0 faces. this error occurs since pm 0.6 (ydnar?) + */ +/* uncomment when debugging this module */ +/* #define DEBUG_PM_3DS +#define DEBUG_PM_3DS_EX */ + +/* structure holding persistent 3ds loader specific data used */ +/* to store formerly static vars to keep the module reentrant */ +/* safe. put everything that needs to be static in here. */ +typedef struct S3dsLoaderPers +{ + picoModel_t *model; /* ptr to output model */ + picoSurface_t *surface; /* ptr to current surface */ + picoShader_t *shader; /* ptr to current shader */ + picoByte_t *bufptr; /* ptr to raw data */ + char *basename; /* ptr to model base name (eg. jeep) */ + int cofs; + int maxofs; +} +T3dsLoaderPers; + +/* 3ds chunk types that we use */ +enum { + /* primary chunk */ + CHUNK_MAIN = 0x4D4D, + + /* main chunks */ + CHUNK_VERSION = 0x0002, + CHUNK_EDITOR_CONFIG = 0x3D3E, + CHUNK_EDITOR_DATA = 0x3D3D, + CHUNK_KEYFRAME_DATA = 0xB000, + + /* editor data sub chunks */ + CHUNK_MATERIAL = 0xAFFF, + CHUNK_OBJECT = 0x4000, + + /* material sub chunks */ + CHUNK_MATNAME = 0xA000, + CHUNK_MATDIFFUSE = 0xA020, + CHUNK_MATMAP = 0xA200, + CHUNK_MATMAPFILE = 0xA300, + + /* lets us know we're reading a new object */ + CHUNK_OBJECT_MESH = 0x4100, + + /* object mesh sub chunks */ + CHUNK_OBJECT_VERTICES = 0x4110, + CHUNK_OBJECT_FACES = 0x4120, + CHUNK_OBJECT_MATERIAL = 0x4130, + CHUNK_OBJECT_UV = 0x4140, +}; +#ifdef DEBUG_PM_3DS +static struct +{ + int id; + char *name; +} +debugChunkNames[] = +{ + { CHUNK_MAIN , "CHUNK_MAIN" }, + { CHUNK_VERSION , "CHUNK_VERSION" }, + { CHUNK_EDITOR_CONFIG , "CHUNK_EDITOR_CONFIG" }, + { CHUNK_EDITOR_DATA , "CHUNK_EDITOR_DATA" }, + { CHUNK_KEYFRAME_DATA , "CHUNK_KEYFRAME_DATA" }, + { CHUNK_MATERIAL , "CHUNK_MATERIAL" }, + { CHUNK_OBJECT , "CHUNK_OBJECT" }, + { CHUNK_MATNAME , "CHUNK_MATNAME" }, + { CHUNK_MATDIFFUSE , "CHUNK_MATDIFFUSE" }, + { CHUNK_MATMAP , "CHUNK_MATMAP" }, + { CHUNK_MATMAPFILE , "CHUNK_MATMAPFILE" }, + { CHUNK_OBJECT_MESH , "CHUNK_OBJECT_MESH" }, + { CHUNK_OBJECT_VERTICES , "CHUNK_OBJECT_VERTICES" }, + { CHUNK_OBJECT_FACES , "CHUNK_OBJECT_FACES" }, + { CHUNK_OBJECT_MATERIAL , "CHUNK_OBJECT_MATERIAL" }, + { CHUNK_OBJECT_UV , "CHUNK_OBJECT_UV" }, + { 0 , NULL } +}; +static char *DebugGetChunkName (int id) +{ + int i,max; /* imax? ;) */ + max = sizeof(debugChunkNames) / sizeof(debugChunkNames[0]); + + for (i=0; ilen)) + return PICO_PMV_ERROR_SIZE; + + /* check 3ds magic */ + if (_pico_little_short(chunk->id) != CHUNK_MAIN) + return PICO_PMV_ERROR_IDENT; + + /* file seems to be a valid 3ds */ + return PICO_PMV_OK; +} + +static T3dsChunk *GetChunk (T3dsLoaderPers *pers) +{ + T3dsChunk *chunk; + + /* sanity check */ + if (pers->cofs > pers->maxofs) return 0; + +#ifdef DEBUG_PM_3DS +/* printf("GetChunk: pers->cofs %x\n",pers->cofs); */ +#endif + /* fill in pointer to chunk */ + chunk = (T3dsChunk *)&pers->bufptr[ pers->cofs ]; + if (!chunk) return NULL; + + chunk->id = _pico_little_short(chunk->id ); + chunk->len = _pico_little_long (chunk->len); + + /* advance in buffer */ + pers->cofs += sizeof(T3dsChunk); + + /* this means yay */ + return chunk; +} + +static int GetASCIIZ (T3dsLoaderPers *pers, char *dest, int max) +{ + int pos = 0; + int ch; + + for (;;) + { + ch = pers->bufptr[ pers->cofs++ ]; + if (ch == '\0') break; + if (pers->cofs >= pers->maxofs) + { + dest[ pos ] = '\0'; + return 0; + } + dest[ pos++ ] = ch; + if (pos >= max) break; + } + dest[ pos ] = '\0'; + return 1; +} + +static picoByte_t GetByte (T3dsLoaderPers *pers) +{ + picoByte_t *value; + + /* sanity check */ + if (pers->cofs > pers->maxofs) return 0; + + /* get and return value */ + value = (picoByte_t *)(pers->bufptr + pers->cofs); + pers->cofs += 1; + return *value; +} + +static int GetWord (T3dsLoaderPers *pers) +{ + unsigned short *value; + + /* sanity check */ + if (pers->cofs > pers->maxofs) return 0; + + /* get and return value */ + value = (unsigned short *)(pers->bufptr + pers->cofs); + pers->cofs += 2; + return _pico_little_short(*value); +} + +static float GetFloat (T3dsLoaderPers *pers) +{ + float *value; + + /* sanity check */ + if (pers->cofs > pers->maxofs) return 0; + + /* get and return value */ + value = (float *)(pers->bufptr + pers->cofs); + pers->cofs += 4; + return _pico_little_float(*value); +} + +static int GetMeshVertices (T3dsLoaderPers *pers) +{ + int numVerts; + int i; + + /* get number of verts for this surface */ + numVerts = GetWord(pers); + +#ifdef DEBUG_PM_3DS + printf("GetMeshVertices: numverts %d\n",numVerts); +#endif + /* read in vertices for current surface */ + for (i=0; isurface,i,v ); + PicoSetSurfaceColor( pers->surface,0,i,white ); /* ydnar */ + +#ifdef DEBUG_PM_3DS_EX + printf("Vertex: x: %f y: %f z: %f\n",v[0],v[1],v[2]); +#endif + } + /* success (no errors occured) */ + return 1; +} + +static int GetMeshFaces (T3dsLoaderPers *pers) +{ + int numFaces; + int i; + + /* get number of faces for this surface */ + numFaces = GetWord(pers); + +#ifdef DEBUG_PM_3DS + printf("GetMeshFaces: numfaces %d\n",numFaces); +#endif + /* read in vertex indices for current surface */ + for (i=0; isurface, (i * 3 + 0), (picoIndex_t)face.a ); + PicoSetSurfaceIndex( pers->surface, (i * 3 + 1), (picoIndex_t)face.b ); + PicoSetSurfaceIndex( pers->surface, (i * 3 + 2), (picoIndex_t)face.c ); + +#ifdef DEBUG_PM_3DS_EX + printf("Face: a: %d b: %d c: %d (%d)\n",face.a,face.b,face.c,face.visible); +#endif + } + /* success (no errors occured) */ + return 1; +} + +static int GetMeshTexCoords (T3dsLoaderPers *pers) +{ + int numTexCoords; + int i; + + /* get number of uv coords for this surface */ + numTexCoords = GetWord(pers); + +#ifdef DEBUG_PM_3DS + printf("GetMeshTexCoords: numcoords %d\n",numTexCoords); +#endif + /* read in uv coords for current surface */ + for (i=0; isurface == NULL) + continue; + + /* add current uv */ + PicoSetSurfaceST( pers->surface,0,i,uv ); + +#ifdef DEBUG_PM_3DS_EX + printf("u: %f v: %f\n",uv[0],uv[1]); +#endif + } + /* success (no errors occured) */ + return 1; +} + +static int GetMeshShader (T3dsLoaderPers *pers) +{ + char shaderName[255] = { 0 }; + picoShader_t *shader; + int numSharedVerts; + int setShaderName = 0; + int i; + + /* the shader is either the color or the texture map of the */ + /* object. it can also hold other information like the brightness, */ + /* shine, etc. stuff we don't really care about. we just want the */ + /* color, or the texture map file name really */ + + /* get in the shader name */ + if (!GetASCIIZ(pers,shaderName,sizeof(shaderName))) + return 0; + + /* ydnar: trim to first whitespace */ + _pico_first_token( shaderName ); + + /* now that we have the shader name we need to go through all of */ + /* the shaders and check the name against each shader. when we */ + /* find a shader in our shader list that matches this name we */ + /* just read in, then we assign the shader's id of the object to */ + /* that shader */ + + /* get shader id for shader name */ + shader = PicoFindShader( pers->model, shaderName, 1 ); + + /* we've found a matching shader */ + if ((shader != NULL) && pers->surface) + { + char mapName[1024+1]; + char *mapNamePtr; + memset( mapName,0,sizeof(mapName) ); + + /* get ptr to shader's map name */ + mapNamePtr = PicoGetShaderMapName( shader ); + + /* we have a valid map name ptr */ + if (mapNamePtr != NULL) + { + char temp[128]; + const char *name; + + /* copy map name to local buffer */ + strcpy( mapName,mapNamePtr ); + + /* extract file name */ + name = _pico_nopath( mapName ); + strncpy( temp, name, sizeof(temp) ); + + /* remove file extension */ + /* name = _pico_setfext( name,"" ); */ + + /* assign default name if no name available */ + if (strlen(temp) < 1) + strcpy(temp,pers->basename); + + /* build shader name */ + _pico_strlwr( temp ); /* gaynux update -sea */ + sprintf( mapName,"models/mapobjects/%s/%s",pers->basename,temp ); + + /* set shader name */ + /* PicoSetShaderName( shader,mapName ); */ /* ydnar: this will screw up the named shader */ + + /* set surface's shader index */ + PicoSetSurfaceShader( pers->surface, shader ); + + setShaderName = 1; + } + } + /* we didn't set a shader name; throw out warning */ + if (!setShaderName) + { + _pico_printf( PICO_WARNING,"3DS mesh is missing shader name"); + } + /* we don't process the list of shared vertices here; there is a */ + /* short int that gives the number of faces of the mesh concerned */ + /* by this shader, then there is the list itself of these faces. */ + /* 0000 means the first face of the (4120) face list */ + + /* get number of shared verts */ + numSharedVerts = GetWord(pers); + +#ifdef DEBUG_PM_3DS + printf("GetMeshShader: uses shader '%s' (nsv %d)\n",shaderName,numSharedVerts); +#endif + /* skip list of shared verts */ + for (i=0; ishader ) + { + PicoSetShaderDiffuseColor( pers->shader,color ); + } +#ifdef DEBUG_PM_3DS + printf("GetDiffuseColor: %d %d %d\n",color[0],color[1],color[2]); +#endif + /* success (no errors occured) */ + return 1; +} + +static int DoNextEditorDataChunk (T3dsLoaderPers *pers, long endofs) +{ + T3dsChunk *chunk; + +#ifdef DEBUG_PM_3DS_EX + printf("DoNextEditorDataChunk: endofs %d\n",endofs); +#endif + while (pers->cofs < endofs) + { + long nextofs = pers->cofs; + if ((chunk = GetChunk(pers)) == NULL) return 0; + if (!chunk->len) return 0; + nextofs += chunk->len; + +#ifdef DEBUG_PM_3DS_EX + printf("Chunk %04x (%s), len %d pers->cofs %x\n",chunk->id,DebugGetChunkName(chunk->id),chunk->len,pers->cofs); +#endif + /*** meshes ***/ + if (chunk->id == CHUNK_OBJECT) + { + picoSurface_t *surface; + char surfaceName[ 0xff ] = { 0 }; + + /* read in surface name */ + if( !GetASCIIZ(pers,surfaceName,sizeof(surfaceName)) ) + return 0; /* this is bad */ + +//PicoGetSurfaceName + /* ignore NULL name surfaces */ +// if( surfaceName + + /* allocate a pico surface */ + surface = PicoNewSurface( pers->model ); + if( surface == NULL ) + { + pers->surface = NULL; + return 0; /* this is bad too */ + } + /* assign ptr to current surface */ + pers->surface = surface; + + /* 3ds models surfaces are all triangle meshes */ + PicoSetSurfaceType( pers->surface,PICO_TRIANGLES ); + + /* set surface name */ + PicoSetSurfaceName( pers->surface,surfaceName ); + + /* continue mess with object's sub chunks */ + DoNextEditorDataChunk(pers,nextofs); + continue; + } + if (chunk->id == CHUNK_OBJECT_MESH) + { + /* continue mess with mesh's sub chunks */ + if (!DoNextEditorDataChunk(pers,nextofs)) return 0; + continue; + } + if (chunk->id == CHUNK_OBJECT_VERTICES) + { + if (!GetMeshVertices(pers)) return 0; + continue; + } + if (chunk->id == CHUNK_OBJECT_FACES) + { + if (!GetMeshFaces(pers)) return 0; + continue; + } + if (chunk->id == CHUNK_OBJECT_UV) + { + if (!GetMeshTexCoords(pers)) return 0; + continue; + } + if (chunk->id == CHUNK_OBJECT_MATERIAL) + { + if (!GetMeshShader(pers)) return 0; + continue; + } + /*** materials ***/ + if (chunk->id == CHUNK_MATERIAL) + { + /* new shader specific things should be */ + /* initialized right here */ + picoShader_t *shader; + + /* allocate a pico shader */ + shader = PicoNewShader( pers->model ); /* ydnar */ + if( shader == NULL ) + { + pers->shader = NULL; + return 0; /* this is bad too */ + } + + /* assign ptr to current shader */ + pers->shader = shader; + + /* continue and process the material's sub chunks */ + DoNextEditorDataChunk(pers,nextofs); + continue; + } + if (chunk->id == CHUNK_MATNAME) + { + /* new material's names should be stored here. note that */ + /* GetMeshMaterial returns the name of the material that */ + /* is used by the mesh. new material names are set HERE. */ + /* but for now we skip the new material's name ... */ + if (pers->shader) + { + char *name = (char*) (pers->bufptr + pers->cofs); + char *cleanedName = _pico_clone_alloc( name ); + _pico_first_token( cleanedName ); + PicoSetShaderName( pers->shader, cleanedName ); +#ifdef DEBUG_PM_3DS + printf( "NewShader: '%s'\n", cleanedName ); +#endif + _pico_free( cleanedName ); + } + } + if (chunk->id == CHUNK_MATDIFFUSE) + { + /* todo: color for last inserted new material should be */ + /* stored somewhere by GetDiffuseColor */ + if (!GetDiffuseColor(pers)) return 0; + + /* rest of chunk is skipped here */ + } + if (chunk->id == CHUNK_MATMAP) + { + /* continue and process the material map sub chunks */ + DoNextEditorDataChunk(pers,nextofs); + continue; + } + if (chunk->id == CHUNK_MATMAPFILE) + { + /* map file name for last inserted new material should */ + /* be stored here. but for now we skip this too ... */ + if( pers->shader ) + { + char *name = (char *)(pers->bufptr + pers->cofs); + PicoSetShaderMapName( pers->shader,name ); +#ifdef DEBUG_PM_3DS + printf("NewShaderMapfile: '%s'\n",name); +#endif + } + } + /*** keyframes ***/ + if (chunk->id == CHUNK_KEYFRAME_DATA) + { + /* well umm, this is a bit too much since we don't really */ + /* need model animation sequences right now. we skip this */ +#ifdef DEBUG_PM_3DS + printf("KeyframeData: len %d\n",chunk->len); +#endif + } + /* skip unknown chunk */ + pers->cofs = nextofs; + if (pers->cofs >= pers->maxofs) break; + } + return 1; +} + +static int DoNextChunk (T3dsLoaderPers *pers, int endofs) +{ + T3dsChunk *chunk; + +#ifdef DEBUG_PM_3DS + printf("DoNextChunk: endofs %d\n",endofs); +#endif + while (pers->cofs < endofs) + { + long nextofs = pers->cofs; + if ((chunk = GetChunk(pers)) == NULL) return 0; + if (!chunk->len) return 0; + nextofs += chunk->len; + +#ifdef DEBUG_PM_3DS_EX + printf("Chunk %04x (%s), len %d pers->cofs %x\n",chunk->id,DebugGetChunkName(chunk->id),chunk->len,pers->cofs); +#endif + /*** version ***/ + if (chunk->id == CHUNK_VERSION) + { + /* at this point i get the 3ds file version. since there */ + /* might be new additions to the 3ds file format in 4.0 */ + /* it might be a good idea to store the version somewhere */ + /* for later handling or message displaying */ + + /* get the version */ + int version; + version = GetWord(pers); + GetWord(pers); +#ifdef DEBUG_PM_3DS + printf("FileVersion: %d\n",version); +#endif + + /* throw out a warning for version 4 models */ + if (version == 4) + { + _pico_printf( PICO_WARNING, + "3DS version is 4. Model might load incorrectly."); + } + /* store the 3ds file version in pico special field 0 */ + /* PicoSetSurfaceSpecial(pers->surface,0,version); */ /* ydnar: this was causing a crash accessing uninitialized surface */ + + /* rest of chunk is skipped here */ + } + /*** editor data ***/ + if (chunk->id == CHUNK_EDITOR_DATA) + { + if (!DoNextEditorDataChunk(pers,nextofs)) return 0; + continue; + } + /* skip unknown chunk */ + pers->cofs = nextofs; + if (pers->cofs >= pers->maxofs) break; + } + return 1; +} + +/* _3ds_load: + * loads an autodesk 3ds model file. +*/ +static picoModel_t *_3ds_load( PM_PARAMS_LOAD ) +{ + T3dsLoaderPers pers; + picoModel_t *model; + char basename[128]; + + /* create a new pico model */ + model = PicoNewModel(); + if (model == NULL) + { + /* user must have some serious ram problems ;) */ + return NULL; + } + /* get model's base name (eg. jeep from c:\models\jeep.3ds) */ + memset( basename,0,sizeof(basename) ); + strncpy( basename,_pico_nopath(fileName),sizeof(basename) ); + _pico_setfext( basename,"" ); + + /* initialize persistant vars (formerly static) */ + pers.model = model; + pers.bufptr = (picoByte_t *)buffer; + pers.basename = (char *)basename; + pers.maxofs = bufSize; + pers.cofs = 0L; + + /* do model setup */ + PicoSetModelFrameNum( model,frameNum ); + PicoSetModelName( model,fileName ); + PicoSetModelFileName( model,fileName ); + + /* skip first chunk in file (magic) */ + GetChunk(&pers); + + /* process chunks */ + if (!DoNextChunk(&pers,pers.maxofs)) + { + /* well, bleh i guess */ + PicoFreeModel(model); + return NULL; + } + /* return allocated pico model */ + return model; +} + +/* pico file format module definition */ +const picoModule_t picoModule3DS = +{ + "0.86-b", /* module version string */ + "Autodesk 3Dstudio", /* module display name */ + "seaw0lf", /* author's name */ + "2002 seaw0lf", /* module copyright */ + { + "3ds",NULL,NULL,NULL /* default extensions to use */ + }, + _3ds_canload, /* validation routine */ + _3ds_load, /* load routine */ + NULL, /* save validation routine */ + NULL /* save routine */ +}; diff --git a/tools/urt/libs/picomodel/pm_ase.c b/tools/urt/libs/picomodel/pm_ase.c new file mode 100644 index 00000000..76699bfd --- /dev/null +++ b/tools/urt/libs/picomodel/pm_ase.c @@ -0,0 +1,1427 @@ +/* ----------------------------------------------------------------------------- + +PicoModel Library + +Copyright (c) 2002, Randy Reddig & seaw0lf +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list +of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other aseMaterialList provided with the distribution. + +Neither the names of the copyright holders nor the names of its contributors may +be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +----------------------------------------------------------------------------- */ + +void Sys_Printf (const char *format, ...); + +/* marker */ +#define PM_ASE_C + +/* uncomment when debugging this module */ +//#define DEBUG_PM_ASE +//#define DEBUG_PM_ASE_EX + + +/* dependencies */ +#include "picointernal.h" + +#ifdef DEBUG_PM_ASE +#include "time.h" +#endif + +/* plain white */ +static picoColor_t white = { 255, 255, 255, 255 }; + +/* jhefty - multi-subobject material support */ + +/* Material/SubMaterial management */ +/* A material should have 1..n submaterials assigned to it */ + +typedef struct aseSubMaterial_s +{ + struct aseSubMaterial_s* next; + int subMtlId; + picoShader_t* shader; + +} aseSubMaterial_t; + +typedef struct aseMaterial_s +{ + struct aseMaterial_s* next; + struct aseSubMaterial_s* subMtls; + int mtlId; +} aseMaterial_t; + +/* Material/SubMaterial management functions */ +static aseMaterial_t* _ase_get_material ( aseMaterial_t* list , int mtlIdParent ) +{ + aseMaterial_t* mtl = list; + + while ( mtl ) + { + if ( mtlIdParent == mtl->mtlId ) + { + break; + } + mtl = mtl->next; + } + return mtl; +} + +static aseSubMaterial_t* _ase_get_submaterial ( aseMaterial_t* list, int mtlIdParent , int subMtlId ) +{ + aseMaterial_t* parent = _ase_get_material ( list , mtlIdParent ); + aseSubMaterial_t* subMtl = NULL; + + if ( !parent ) + { + _pico_printf ( PICO_ERROR , "No ASE material exists with id %i\n" , mtlIdParent ); + return NULL; + } + + subMtl = parent->subMtls; + while ( subMtl ) + { + if ( subMtlId == subMtl->subMtlId ) + { + break; + } + subMtl = subMtl->next; + } + return subMtl; +} + +aseSubMaterial_t* _ase_get_submaterial_or_default ( aseMaterial_t* materials, int mtlIdParent , int subMtlId ) +{ + aseSubMaterial_t* subMtl = _ase_get_submaterial( materials, mtlIdParent, subMtlId ); + if(subMtl != NULL) + { + return subMtl; + } + + /* ydnar: trying default submaterial */ + subMtl = _ase_get_submaterial( materials, mtlIdParent, 0 ); + if( subMtl != NULL ) + { + return subMtl; + } + + _pico_printf( PICO_ERROR, "Could not find material/submaterial for id %d/%d\n", mtlIdParent, subMtlId ); + return NULL; +} + + + + +static aseMaterial_t* _ase_add_material( aseMaterial_t **list, int mtlIdParent ) +{ + aseMaterial_t *mtl = _pico_calloc( 1, sizeof( aseMaterial_t ) ); + mtl->mtlId = mtlIdParent; + mtl->subMtls = NULL; + mtl->next = *list; + *list = mtl; + + return mtl; +} + +static aseSubMaterial_t* _ase_add_submaterial( aseMaterial_t **list, int mtlIdParent, int subMtlId, picoShader_t* shader ) +{ + aseMaterial_t *parent = _ase_get_material( *list, mtlIdParent ); + aseSubMaterial_t *subMtl = _pico_calloc( 1, sizeof ( aseSubMaterial_t ) ); + + if ( !parent ) + { + parent = _ase_add_material ( list , mtlIdParent ); + } + + subMtl->shader = shader; + subMtl->subMtlId = subMtlId; + subMtl->next = parent->subMtls; + parent->subMtls = subMtl; + + return subMtl; +} + +static void _ase_free_materials( aseMaterial_t **list ) +{ + aseMaterial_t* mtl = *list; + aseSubMaterial_t* subMtl = NULL; + + aseMaterial_t* mtlTemp = NULL; + aseSubMaterial_t* subMtlTemp = NULL; + + while ( mtl ) + { + subMtl = mtl->subMtls; + while ( subMtl ) + { + subMtlTemp = subMtl->next; + _pico_free ( subMtl ); + subMtl = subMtlTemp; + } + mtlTemp = mtl->next; + _pico_free ( mtl ); + mtl = mtlTemp; + } + (*list) = NULL; +} + +#ifdef DEBUG_PM_ASE +static void _ase_print_materials( aseMaterial_t *list ) +{ + aseMaterial_t* mtl = list; + aseSubMaterial_t* subMtl = NULL; + + while ( mtl ) + { + _pico_printf ( PICO_NORMAL , "ASE Material %i" , mtl->mtlId ); + subMtl = mtl->subMtls; + while ( subMtl ) + { + _pico_printf ( PICO_NORMAL , " -- ASE SubMaterial %i - %s\n" , subMtl->subMtlId , subMtl->shader->name ); + subMtl = subMtl->next; + } + mtl = mtl->next; + } +} +#endif //DEBUG_PM_ASE + +/* todo: + * - apply material specific uv offsets to uv coordinates + */ + +/* _ase_canload: + * validates a 3dsmax ase model file. + */ +static int _ase_canload( PM_PARAMS_CANLOAD ) +{ + picoParser_t *p; + + + /* quick data length validation */ + if( bufSize < 80 ) + return PICO_PMV_ERROR_SIZE; + + /* keep the friggin compiler happy */ + *fileName = *fileName; + + /* create pico parser */ + p = _pico_new_parser( (picoByte_t*) buffer, bufSize ); + if( p == NULL ) + return PICO_PMV_ERROR_MEMORY; + + /* get first token */ + if( _pico_parse_first( p ) == NULL) + { + return PICO_PMV_ERROR_IDENT; + } + + /* check first token */ + if( _pico_stricmp( p->token, "*3dsmax_asciiexport" ) ) + { + _pico_free_parser( p ); + return PICO_PMV_ERROR_IDENT; + } + + /* free the pico parser object */ + _pico_free_parser( p ); + + /* file seems to be a valid ase file */ + return PICO_PMV_OK; +} + +typedef struct aseVertex_s aseVertex_t; +struct aseVertex_s +{ + picoVec3_t xyz; + picoIndex_t id; +}; + +typedef struct aseTexCoord_s aseTexCoord_t; +struct aseTexCoord_s +{ + picoVec2_t texcoord; +}; + +typedef struct aseColor_s aseColor_t; +struct aseColor_s +{ + picoColor_t color; +}; + +typedef struct aseFace_s aseFace_t; +struct aseFace_s +{ + picoIndex_t indices[9]; + picoIndex_t smoothingGroup; + picoIndex_t materialId; + picoIndex_t subMaterialId; + picoVec3_t facenormal; + picoVec3_t vertexnormal[3]; +}; +typedef aseFace_t* aseFacesIter_t; + +picoSurface_t* PicoModelFindOrAddSurface( picoModel_t *model, picoShader_t* shader ) +{ + /* see if a surface already has the shader */ + int i = 0; + for ( ; i < model->numSurfaces ; i++ ) + { + picoSurface_t* workSurface = model->surface[i]; + if ( workSurface->shader == shader ) + { + return workSurface; + } + } + + /* no surface uses this shader yet, so create a new surface */ + + { + /* create a new surface in the model for the unique shader */ + picoSurface_t* workSurface = PicoNewSurface(model); + if ( !workSurface ) + { + _pico_printf ( PICO_ERROR , "Could not allocate a new surface!\n" ); + return 0; + } + + /* do surface setup */ + PicoSetSurfaceType( workSurface, PICO_TRIANGLES ); + PicoSetSurfaceName( workSurface, shader->name ); + PicoSetSurfaceShader( workSurface, shader ); + + return workSurface; + } +} + +/* _ase_submit_triangles - jhefty + use the surface and the current face list to look up material/submaterial IDs + and submit them to the model for proper processing + +The following still holds from ydnar's _ase_make_surface: + indexes 0 1 2 = vert indexes + indexes 3 4 5 = st indexes + indexes 6 7 8 = color indexes (new) +*/ + +#if 0 +typedef picoIndex_t* picoIndexIter_t; + +typedef struct aseUniqueIndices_s aseUniqueIndices_t; +struct aseUniqueIndices_s +{ + picoIndex_t* data; + picoIndex_t* last; + + aseFace_t* faces; +}; + +size_t aseUniqueIndices_size(aseUniqueIndices_t* self) +{ + return self->last - self->data; +} + +void aseUniqueIndices_reserve(aseUniqueIndices_t* self, picoIndex_t size) +{ + self->data = self->last = (picoIndex_t*)_pico_calloc(size, sizeof(picoIndex_t)); +} + +void aseUniqueIndices_clear(aseUniqueIndices_t* self) +{ + _pico_free(self->data); +} + +void aseUniqueIndices_pushBack(aseUniqueIndices_t* self, picoIndex_t index) +{ + *self->last++ = index; +} + +picoIndex_t aseFaces_getVertexIndex(aseFace_t* faces, picoIndex_t index) +{ + return faces[index / 3].indices[index % 3]; +} + +picoIndex_t aseFaces_getTexCoordIndex(aseFace_t* faces, picoIndex_t index) +{ + return faces[index / 3].indices[(index % 3) + 3]; +} + +picoIndex_t aseFaces_getColorIndex(aseFace_t* faces, picoIndex_t index) +{ + return faces[index / 3].indices[(index % 3) + 6]; +} + +int aseUniqueIndex_equal(aseFace_t* faces, picoIndex_t index, picoIndex_t other) +{ + return aseFaces_getVertexIndex(faces, index) == aseFaces_getVertexIndex(faces, other) + && aseFaces_getTexCoordIndex(faces, index) == aseFaces_getTexCoordIndex(faces, other) + && aseFaces_getColorIndex(faces, index) == aseFaces_getColorIndex(faces, other); +} + +picoIndex_t aseUniqueIndices_insertUniqueVertex(aseUniqueIndices_t* self, picoIndex_t index) +{ + picoIndexIter_t i = self->data; + for(; i != self->last; ++i) + { + picoIndex_t other = (picoIndex_t)(i - self->data); + if(aseUniqueIndex_equal(self->faces, index, other)) + { + return other; + } + } + + aseUniqueIndices_pushBack(self, index); + return (picoIndex_t)(aseUniqueIndices_size(self) - 1); +} + +static void _ase_submit_triangles_unshared ( picoModel_t* model , aseMaterial_t* materials , aseVertex_t* vertices, aseTexCoord_t* texcoords, aseColor_t* colors, aseFace_t* faces, int numFaces, int meshHasNormals ) +{ + aseFacesIter_t i = faces, end = faces + numFaces; + + aseUniqueIndices_t indices; + aseUniqueIndices_t remap; + aseUniqueIndices_reserve(&indices, numFaces * 3); + aseUniqueIndices_reserve(&remap, numFaces * 3); + indices.faces = faces; + + for(; i != end; ++i) + { + /* look up the shader for the material/submaterial pair */ + aseSubMaterial_t* subMtl = _ase_get_submaterial_or_default( materials, (*i).materialId, (*i).subMaterialId ); + if( subMtl == NULL ) + { + return; + } + + { + picoSurface_t* surface = PicoModelFindOrAddSurface(model, subMtl->shader); + int j; + /* we pull the data from the vertex, color and texcoord arrays using the face index data */ + for ( j = 0 ; j < 3 ; j ++ ) + { + picoIndex_t index = (picoIndex_t)(((i - faces) * 3) + j); + picoIndex_t size = (picoIndex_t)aseUniqueIndices_size(&indices); + picoIndex_t unique = aseUniqueIndices_insertUniqueVertex(&indices, index); + + picoIndex_t numVertexes = PicoGetSurfaceNumVertexes(surface); + picoIndex_t numIndexes = PicoGetSurfaceNumIndexes(surface); + + aseUniqueIndices_pushBack(&remap, numIndexes); + + PicoSetSurfaceIndex(surface, numIndexes, remap.data[unique]); + + if(unique == size) + { + PicoSetSurfaceXYZ(surface, numVertexes, vertices[(*i).indices[j]].xyz); + PicoSetSurfaceNormal(surface, numVertexes, vertices[(*i).indices[j]].normal); + PicoSetSurfaceST(surface, 0, numVertexes, texcoords[(*i).indices[j + 3]].texcoord); + + if ( (*i).indices[j + 6] >= 0 ) + { + PicoSetSurfaceColor(surface, 0, numVertexes, colors[(*i).indices[j + 6]].color); + } + else + { + PicoSetSurfaceColor(surface, 0, numVertexes, white); + } + + PicoSetSurfaceSmoothingGroup(surface, numVertexes, (vertices[(*i).indices[j]].id * (1 << 16)) + (*i).smoothingGroup); + } + } + } + } + + aseUniqueIndices_clear(&indices); + aseUniqueIndices_clear(&remap); +} + +#endif + +static int VectorCompareExtn( picoVec3_t n1, picoVec3_t n2, float epsilon ) +{ + int i; + + + /* test */ + for( i= 0; i < 3; i++ ) + if( fabs( n1[ i ] - n2[ i ]) > epsilon ) + return -1; + return 1; +} + +#define CrossProductTemp(a,b,c) ((c)[0]=(a)[1]*(b)[2]-(a)[2]*(b)[1],(c)[1]=(a)[2]*(b)[0]-(a)[0]*(b)[2],(c)[2]=(a)[0]*(b)[1]-(a)[1]*(b)[0]) + + +#define MAX_FACEREFS 64 +//maximum number of faces that can share a vert. likely 1-4, but who knows. +typedef struct +{ + int count; + aseFace_t* faces[MAX_FACEREFS]; +} faceref_t; + +static void _ase_submit_triangles( picoModel_t* model , aseMaterial_t* materials , aseVertex_t* vertices, aseTexCoord_t* texcoords, aseColor_t* colors, aseFace_t* faces, int numFaces,int numVerts, int submodel ) +{ + + picoVec3_t accum; + int index; + int counter; + faceref_t* faceref; + int *normalsC; + int fc=0; + + aseFacesIter_t i = faces, end = faces + numFaces; + counter=0; + + + //allocate room for sg optimization + faceref=(faceref_t*)malloc(numVerts*sizeof(faceref_t)); + memset(faceref,0,numVerts*sizeof(faceref_t)); + + //rebuild face normals + for(i=faces; i != end; ++i) + { + + picoVec3_t a,b,c; + picoVec3_t v1,v2,v3; + int j; + counter++; + + for (j=0;j<3;j++) + { + a[j] = vertices[(*i).indices[0]].xyz[j]; + b[j] = vertices[(*i).indices[1]].xyz[j]; + c[j] = vertices[(*i).indices[2]].xyz[j]; + } + for (j=0;j<3;j++) + { + v1[j]=a[j]-b[j]; + v2[j]=c[j]-b[j]; + } + + CrossProductTemp(v1,v2,v3); + _pico_normalize_vec(v3); + (*i).facenormal[0]=v3[0]; + (*i).facenormal[1]=v3[1]; + (*i).facenormal[2]=v3[2]; + + + //throw this face into the index pools + for ( j = 0 ; j < 3 ; j ++ ) + { + index=(*i).indices[j]; + if (faceref[index].count>=MAX_FACEREFS-1) + { + + } + else + { + faceref[index].faces[faceref[index].count++]=i; + } + } + + } + + //if (counter>0) Sys_Printf( "Rebuilding %d Normals\n", counter * 3 ); + for(i=faces; i != end; ++i) + { + /* look up the shader for the material/submaterial pair */ + aseSubMaterial_t* subMtl = _ase_get_submaterial_or_default( materials, (*i).materialId, (*i).subMaterialId ); + + if( subMtl == NULL ) + { + continue; + } + + { + picoVec3_t* xyz[3]; + picoVec3_t *a[3]; + picoVec3_t* normal[3]; + picoVec2_t* st[3]; + picoColor_t* color[3]; + picoIndex_t smooth[3]; + + int j,z; + + + + /* we pull the data from the vertex, color and texcoord arrays using the face index data */ + for ( j = 0 ; j < 3 ; j ++ ) + { + aseFacesIter_t q = faces; + aseFacesIter_t qend = faces + numFaces; + + xyz[j] = &vertices[(*i).indices[j]].xyz; + + // Use Face normal + normal[j] = &(*i).facenormal; + + + //Oooor we can use the smoothing group + + //Slow method, but testing + //Find All faces that use this vertex, average their facenormals. + // skip where smoothgroups both equal 0, or don't have any shared bits (x & y) + index=(*i).indices[j]; + + accum[0]=(*i).facenormal[0]; + accum[1]=(*i).facenormal[1]; + accum[2]=(*i).facenormal[2]; + counter=1; + + + z=0; + + for (fc=0;fc0 || + VectorCompareExtn(*a[1],*xyz[j],0.01f)>0 || + VectorCompareExtn(*a[2],*xyz[j],0.01f)>0 + ) + { + if ( (*i).smoothingGroup==0 && (*q).smoothingGroup ==0 ) + continue; + + if ( (*i).smoothingGroup & (*q).smoothingGroup ) + { + accum[0]+=(*q).facenormal[0]; + accum[1]+=(*q).facenormal[1]; + accum[2]+=(*q).facenormal[2]; + + counter++; + + } + } + } + _pico_normalize_vec(accum); + + (*i).vertexnormal[j][0]=accum[0]; + (*i).vertexnormal[j][1]=accum[1]; + (*i).vertexnormal[j][2]=accum[2]; + normal[j]=&(*i).vertexnormal[j]; + + st[j] = &texcoords[(*i).indices[j + 3]].texcoord; + + if( colors != NULL && (*i).indices[j + 6] >= 0 ) + { + color[j] = &colors[(*i).indices[j + 6]].color; + } + else + { + color[j] = &white; + } + + smooth[j] = 0;// (vertices[(*i).indices[j]].id * (1 << 16)) + (*i).smoothingGroup; /* don't merge vertices */ + + } + + /* submit the triangle to the model */ + PicoAddTriangleToModel ( model , xyz , normal , 1 , st , 1 , color , subMtl->shader, smooth, submodel ); + } + + } + free(faceref); +} + + +/* _ase_load: + * loads a 3dsmax ase model file. +*/ +static picoModel_t *_ase_load( PM_PARAMS_LOAD ) +{ + picoModel_t *model; + picoParser_t *p; + char lastNodeName[ 1024 ]; + + aseVertex_t* vertices = NULL; + aseTexCoord_t* texcoords = NULL; + aseColor_t* colors = NULL; + aseFace_t* faces = NULL; + int numVertices = 0; + int numFaces = 0; + int numTextureVertices = 0; + int numTextureVertexFaces = 0; + int numColorVertices = 0; + int numColorVertexFaces = 0; + int vertexId = 0; + int currentVertexFace=0; + int currentVertexIndex=0; + int counter=0; + int submodel=0; + + aseMaterial_t* materials = NULL; + +#ifdef DEBUG_PM_ASE + clock_t start, finish; + double elapsed; + start = clock(); +#endif + + /* helper */ + #define _ase_error_return(m) \ + { \ + _pico_printf( PICO_ERROR,"%s in ASE, line %d.",m,p->curLine); \ + _pico_free_parser( p ); \ + PicoFreeModel( model ); \ + return NULL; \ + } + /* create a new pico parser */ + p = _pico_new_parser( (picoByte_t *)buffer,bufSize ); + if (p == NULL) return NULL; + + /* create a new pico model */ + model = PicoNewModel(); + if (model == NULL) + { + _pico_free_parser( p ); + return NULL; + } + /* do model setup */ + PicoSetModelFrameNum( model, frameNum ); + PicoSetModelName( model, fileName ); + PicoSetModelFileName( model, fileName ); + + /* initialize some stuff */ + memset( lastNodeName,0,sizeof(lastNodeName) ); + + /* parse ase model file */ + while( 1 ) + { + /* get first token on line */ + if (_pico_parse_first( p ) == NULL) + break; + + /* we just skip empty lines */ + if (p->token == NULL || !strlen( p->token )) + continue; + + /* we skip invalid ase statements */ + if (p->token[0] != '*' && p->token[0] != '{' && p->token[0] != '}') + { + _pico_parse_skip_rest( p ); + continue; + } + /* remember node name */ + if (!_pico_stricmp(p->token,"*node_name")) + { + /* read node name */ + char *ptr = _pico_parse( p,0 ); + if (ptr == NULL) + _ase_error_return("Node name parse error"); + + /* remember node name */ + strncpy( lastNodeName,ptr,sizeof(lastNodeName) ); + } + /* model mesh (originally contained within geomobject) */ + else if (!_pico_stricmp(p->token,"*mesh")) + { + /* finish existing surface */ + _ase_submit_triangles(model, materials, vertices, texcoords, colors, faces, numFaces,numVertices,submodel++); + _pico_free(faces); + _pico_free(vertices); + _pico_free(texcoords); + _pico_free(colors); + } + else if (!_pico_stricmp(p->token,"*mesh_numvertex")) + { + if (!_pico_parse_int( p, &numVertices) ) + _ase_error_return("Missing MESH_NUMVERTEX value"); + + vertices = _pico_calloc(numVertices, sizeof(aseVertex_t)); + currentVertexIndex=0; + } + else if (!_pico_stricmp(p->token,"*mesh_numfaces")) + { + if (!_pico_parse_int( p, &numFaces) ) + _ase_error_return("Missing MESH_NUMFACES value"); + + faces = _pico_calloc(numFaces, sizeof(aseFace_t)); + + } + else if (!_pico_stricmp(p->token,"*mesh_numtvertex")) + { + if (!_pico_parse_int( p, &numTextureVertices) ) + _ase_error_return("Missing MESH_NUMTVERTEX value"); + + texcoords = _pico_calloc(numTextureVertices, sizeof(aseTexCoord_t)); + } + else if (!_pico_stricmp(p->token,"*mesh_numtvfaces")) + { + if (!_pico_parse_int( p, &numTextureVertexFaces) ) + _ase_error_return("Missing MESH_NUMTVFACES value"); + } + else if (!_pico_stricmp(p->token,"*mesh_numcvertex")) + { + if (!_pico_parse_int( p, &numColorVertices) ) + _ase_error_return("Missing MESH_NUMCVERTEX value"); + + colors = _pico_calloc(numColorVertices, sizeof(aseColor_t)); + memset( colors, 255, numColorVertices * sizeof( aseColor_t ) ); /* ydnar: force colors to white initially */ + } + else if (!_pico_stricmp(p->token,"*mesh_numcvfaces")) + { + if (!_pico_parse_int( p, &numColorVertexFaces) ) + _ase_error_return("Missing MESH_NUMCVFACES value"); + } + /* mesh material reference. this usually comes at the end of */ + /* geomobjects after the mesh blocks. we must assume that the */ + /* new mesh was already created so all we can do here is assign */ + /* the material reference id (shader index) now. */ + else if (!_pico_stricmp(p->token,"*material_ref")) + { + int mtlId; + + /* get the material ref (0..n) */ + if (!_pico_parse_int( p,&mtlId) ) + _ase_error_return("Missing material reference ID"); + + { + int i = 0; + /* fix up all of the aseFaceList in the surface to point to the parent material */ + /* we've already saved off their subMtl */ + for(; i < numFaces; ++i) + { + faces[i].materialId = mtlId; + } + } + } + /* model mesh vertex */ + else if (!_pico_stricmp(p->token,"*mesh_vertex")) + { + int index; + + if( numVertices == 0 ) + _ase_error_return("Vertex parse error"); + + /* get vertex data (orig: index +y -x +z) */ + if (!_pico_parse_int( p,&index )) + _ase_error_return("Vertex parse error"); + if (!_pico_parse_vec( p,vertices[index].xyz )) + _ase_error_return("Vertex parse error"); + + vertices[index].id = vertexId++; + } + else if (!_pico_stricmp(p->token,"*mesh_facenormal")) + { + //Grab the faceindex for the next vertex normals. + if( numVertices == 0 ) + _ase_error_return("Vertex parse error (facenormals)"); + + if (!_pico_parse_int( p,¤tVertexFace )) + _ase_error_return("Vertex parse error"); + + if (!_pico_parse_vec( p,faces[currentVertexFace].facenormal )) + _ase_error_return("Vertex parse error"); + + } + /* model mesh vertex normal */ + else if (!_pico_stricmp(p->token,"*mesh_vertexnormal")) + { + int index; + + if( numVertices == 0 ) + _ase_error_return("Vertex parse error"); + + /* get vertex data (orig: index +y -x +z) */ + if (!_pico_parse_int( p,&index )) + _ase_error_return("Vertex parse error"); + + //^^ Index is 'wrong' in .ase models. they reference the same vert index with multiple normals.. + // I've tried, this is a lost cause. Use the SG's + // + /* + + if (!_pico_parse_vec( p,vertices[counter].normal )) + _ase_error_return("Vertex parse error"); + vertices[counter].faceid=index; + counter++; + */ + } + /* model mesh face */ + else if (!_pico_stricmp(p->token,"*mesh_normals")) + { + // counter=0; //part of the above vertex normals fix + } + + /* model mesh face */ + else if (!_pico_stricmp(p->token,"*mesh_face")) + { + picoIndex_t indexes[3]; + int index; + + if( numFaces == 0 ) + _ase_error_return("Face parse error"); + + /* get face index */ + if (!_pico_parse_int( p,&index )) + _ase_error_return("Face parse error"); + + /* get 1st vertex index */ + _pico_parse( p,0 ); + if (!_pico_parse_int( p,&indexes[0] )) + _ase_error_return("Face parse error"); + + /* get 2nd vertex index */ + _pico_parse( p,0 ); + if (!_pico_parse_int( p,&indexes[1] )) + _ase_error_return("Face parse error"); + + /* get 3rd vertex index */ + _pico_parse( p,0 ); + if (!_pico_parse_int( p,&indexes[2] )) + _ase_error_return("Face parse error"); + + /* parse to the subMaterial ID */ + while ( 1 ) + { + if (!_pico_parse (p,0)) /* EOL */ + { + break; + } + if (!_pico_stricmp (p->token,"*MESH_SMOOTHING" )) + { + int total=0; + char* point; + char* start; + _pico_parse(p,0); + + point=p->token; + start=point; + faces[index].smoothingGroup=0; + + //Super dodgy comma delimited string parse + while (*point<'A') + { + if (*point<=32 || *point==',') + { + total=atoi(start); + if (total!=0) + { + faces[index].smoothingGroup+=1<token,"*MESH_MTLID" )) + { + _pico_parse_int ( p , &faces[index].subMaterialId ); + } + } + + faces[index].materialId = 0; + faces[index].indices[0] = indexes[2]; + faces[index].indices[1] = indexes[1]; + faces[index].indices[2] = indexes[0]; + } + /* model texture vertex */ + else if (!_pico_stricmp(p->token,"*mesh_tvert")) + { + int index; + + if( numVertices == 0 ) + _ase_error_return("Vertex parse error"); + + /* get uv vertex index */ + if (!_pico_parse_int( p,&index )) + _ase_error_return("UV vertex parse error"); + + /* get uv vertex s */ + if (!_pico_parse_float( p,&texcoords[index].texcoord[0] )) + _ase_error_return("UV vertex parse error"); + + /* get uv vertex t */ + if (!_pico_parse_float( p,&texcoords[index].texcoord[1] )) + _ase_error_return("UV vertex parse error"); + + /* ydnar: invert t */ + texcoords[index].texcoord[ 1 ] = 1.0f - texcoords[index].texcoord[ 1 ]; + } + /* ydnar: model mesh texture face */ + else if( !_pico_stricmp( p->token, "*mesh_tface" ) ) + { + picoIndex_t indexes[3]; + int index; + + if( numFaces == 0 ) + _ase_error_return("Texture face parse error"); + + /* get face index */ + if (!_pico_parse_int( p,&index )) + _ase_error_return("Texture face parse error"); + + /* get 1st vertex index */ + if (!_pico_parse_int( p,&indexes[0] )) + _ase_error_return("Texture face parse error"); + + /* get 2nd vertex index */ + if (!_pico_parse_int( p,&indexes[1] )) + _ase_error_return("Texture face parse error"); + + /* get 3rd vertex index */ + if (!_pico_parse_int( p,&indexes[2] )) + _ase_error_return("Texture face parse error"); + + faces[index].indices[3] = indexes[2]; + faces[index].indices[4] = indexes[1]; + faces[index].indices[5] = indexes[0]; + } + /* model color vertex */ + else if (!_pico_stricmp(p->token,"*mesh_vertcol")) + { + int index; + float colorInput; + + if( numVertices == 0 ) + _ase_error_return("Color Vertex parse error"); + + /* get color vertex index */ + if (!_pico_parse_int( p,&index )) + _ase_error_return("Color vertex parse error"); + + /* get R component */ + if (!_pico_parse_float( p,&colorInput )) + _ase_error_return("Color vertex parse error"); + colors[index].color[0] = (picoByte_t)(colorInput * 255); + + /* get G component */ + if (!_pico_parse_float( p,&colorInput )) + _ase_error_return("Color vertex parse error"); + colors[index].color[1] = (picoByte_t)(colorInput * 255); + + /* get B component */ + if (!_pico_parse_float( p,&colorInput )) + _ase_error_return("Color vertex parse error"); + colors[index].color[2] = (picoByte_t)(colorInput * 255); + + /* leave alpha alone since we don't get any data from the ASE format */ + colors[index].color[3] = 255; + + /* 27 hack, red as alpha */ + colors[index].color[3]=colors[index].color[0]; + colors[index].color[0]=255; + colors[index].color[1]=255; + colors[index].color[2]=255; + + } + /* model color face */ + else if (!_pico_stricmp(p->token,"*mesh_cface")) + { + picoIndex_t indexes[3]; + int index; + + if( numFaces == 0 ) + _ase_error_return("Face parse error"); + + /* get face index */ + if (!_pico_parse_int( p,&index )) + _ase_error_return("Face parse error"); + + /* get 1st cvertex index */ + // _pico_parse( p,0 ); + if (!_pico_parse_int( p,&indexes[0] )) + _ase_error_return("Face parse error"); + + /* get 2nd cvertex index */ + // _pico_parse( p,0 ); + if (!_pico_parse_int( p,&indexes[1] )) + _ase_error_return("Face parse error"); + + /* get 3rd cvertex index */ + // _pico_parse( p,0 ); + if (!_pico_parse_int( p,&indexes[2] )) + _ase_error_return("Face parse error"); + + faces[index].indices[6] = indexes[2]; + faces[index].indices[7] = indexes[1]; + faces[index].indices[8] = indexes[0]; + } + /* model material */ + else if( !_pico_stricmp( p->token, "*material" ) ) + { + aseSubMaterial_t* subMaterial = NULL; + picoShader_t *shader; + int level = 1, index; + char materialName[ 1024 ]; + float transValue = 0.0f, shineValue = 1.0f; + picoColor_t ambientColor, diffuseColor, specularColor; + char *mapname = NULL; + int subMtlId, subMaterialLevel = -1; + + + /* get material index */ + _pico_parse_int( p,&index ); + + /* check brace */ + if (!_pico_parse_check(p,1,"{")) + _ase_error_return("Material missing opening brace"); + + /* parse material block */ + while( 1 ) + { + /* get next token */ + if (_pico_parse(p,1) == NULL) break; + if (!strlen(p->token)) continue; + + /* handle levels */ + if (p->token[0] == '{') level++; + if (p->token[0] == '}') level--; + if (!level) break; + + if( level == subMaterialLevel ) + { + /* set material name */ + _pico_first_token( materialName ); + PicoSetShaderName( shader, materialName); + + /* set shader's transparency */ + PicoSetShaderTransparency( shader,transValue ); + + /* set shader's ambient color */ + PicoSetShaderAmbientColor( shader,ambientColor ); + + /* set diffuse alpha to transparency */ + diffuseColor[3] = (picoByte_t)( transValue * 255.0 ); + + /* set shader's diffuse color */ + PicoSetShaderDiffuseColor( shader,diffuseColor ); + + /* set shader's specular color */ + PicoSetShaderSpecularColor( shader,specularColor ); + + /* set shader's shininess */ + PicoSetShaderShininess( shader,shineValue ); + + /* set material map name */ + PicoSetShaderMapName( shader, mapname ); + + subMaterial = _ase_add_submaterial( &materials, index, subMtlId, shader ); + subMaterialLevel = -1; + } + + /* parse submaterial index */ + if (!_pico_stricmp(p->token,"*submaterial")) + { + /* allocate new pico shader */ + _pico_parse_int( p , &subMtlId ); + + shader = PicoNewShader( model ); + if (shader == NULL) + { + PicoFreeModel( model ); + return NULL; + } + subMaterialLevel = level; + } + /* parse material name */ + else if (!_pico_stricmp(p->token,"*material_name")) + { + char* name = _pico_parse(p,0); + if ( name == NULL) + _ase_error_return("Missing material name"); + + strcpy ( materialName , name ); + /* skip rest and continue with next token */ + _pico_parse_skip_rest( p ); + continue; + } + /* parse material transparency */ + else if (!_pico_stricmp(p->token,"*material_transparency")) + { + /* get transparency value from ase */ + if (!_pico_parse_float( p,&transValue )) + _ase_error_return("Material transparency parse error"); + + /* skip rest and continue with next token */ + _pico_parse_skip_rest( p ); + continue; + } + /* parse material shininess */ + else if (!_pico_stricmp(p->token,"*material_shine")) + { + /* remark: + * - not sure but instead of '*material_shine' i might + * need to use '*material_shinestrength' */ + + /* get shine value from ase */ + if (!_pico_parse_float( p,&shineValue )) + _ase_error_return("Material shine parse error"); + + /* scale ase shine range 0..1 to pico range 0..127 */ + shineValue *= 128.0; + + /* skip rest and continue with next token */ + _pico_parse_skip_rest( p ); + continue; + } + /* parse ambient material color */ + else if (!_pico_stricmp(p->token,"*material_ambient")) + { + picoVec3_t vec; + /* get r,g,b float values from ase */ + if (!_pico_parse_vec( p,vec )) + _ase_error_return("Material color parse error"); + + /* setup 0..255 range color values */ + ambientColor[ 0 ] = (int)( vec[ 0 ] * 255.0 ); + ambientColor[ 1 ] = (int)( vec[ 1 ] * 255.0 ); + ambientColor[ 2 ] = (int)( vec[ 2 ] * 255.0 ); + ambientColor[ 3 ] = (int)( 255 ); + + /* skip rest and continue with next token */ + _pico_parse_skip_rest( p ); + continue; + } + /* parse diffuse material color */ + else if (!_pico_stricmp(p->token,"*material_diffuse")) + { + picoVec3_t vec; + + /* get r,g,b float values from ase */ + if (!_pico_parse_vec( p,vec )) + _ase_error_return("Material color parse error"); + + /* setup 0..255 range color */ + diffuseColor[ 0 ] = (int)( vec[ 0 ] * 255.0 ); + diffuseColor[ 1 ] = (int)( vec[ 1 ] * 255.0 ); + diffuseColor[ 2 ] = (int)( vec[ 2 ] * 255.0 ); + diffuseColor[ 3 ] = (int)( 255 ); + + /* skip rest and continue with next token */ + _pico_parse_skip_rest( p ); + continue; + } + /* parse specular material color */ + else if (!_pico_stricmp(p->token,"*material_specular")) + { + picoVec3_t vec; + + /* get r,g,b float values from ase */ + if (!_pico_parse_vec( p,vec )) + _ase_error_return("Material color parse error"); + + /* setup 0..255 range color */ + specularColor[ 0 ] = (int)( vec[ 0 ] * 255 ); + specularColor[ 1 ] = (int)( vec[ 1 ] * 255 ); + specularColor[ 2 ] = (int)( vec[ 2 ] * 255 ); + specularColor[ 3 ] = (int)( 255 ); + + /* skip rest and continue with next token */ + _pico_parse_skip_rest( p ); + continue; + } + /* material diffuse map */ + else if (!_pico_stricmp(p->token,"*map_diffuse") ) + { + int sublevel = 0; + + /* parse material block */ + while( 1 ) + { + /* get next token */ + if (_pico_parse(p,1) == NULL) break; + if (!strlen(p->token)) continue; + + /* handle levels */ + if (p->token[0] == '{') sublevel++; + if (p->token[0] == '}') sublevel--; + if (!sublevel) break; + + /* parse diffuse map bitmap */ + if (!_pico_stricmp(p->token,"*bitmap")) + { + char* name = _pico_parse(p,0); + if (name == NULL) + _ase_error_return("Missing material map bitmap name"); + mapname = _pico_alloc ( strlen ( name ) + 1 ); + strcpy ( mapname, name ); + /* skip rest and continue with next token */ + _pico_parse_skip_rest( p ); + continue; + } + } + } + /* end map_diffuse block */ + } + /* end material block */ + + if( subMaterial == NULL ) + { + /* allocate new pico shader */ + shader = PicoNewShader( model ); + if (shader == NULL) + { + PicoFreeModel( model ); + return NULL; + } + + /* set material name */ + PicoSetShaderName( shader,materialName ); + + /* set shader's transparency */ + PicoSetShaderTransparency( shader,transValue ); + + /* set shader's ambient color */ + PicoSetShaderAmbientColor( shader,ambientColor ); + + /* set diffuse alpha to transparency */ + diffuseColor[3] = (picoByte_t)( transValue * 255.0 ); + + /* set shader's diffuse color */ + PicoSetShaderDiffuseColor( shader,diffuseColor ); + + /* set shader's specular color */ + PicoSetShaderSpecularColor( shader,specularColor ); + + /* set shader's shininess */ + PicoSetShaderShininess( shader,shineValue ); + + /* set material map name */ + PicoSetShaderMapName( shader, mapname ); + + /* extract shadername from bitmap path */ + if(mapname != NULL) + { + char* p = mapname; + + /* convert to shader-name format */ + { + /* unix-style path separators */ + char* s = mapname; + for(; *s != '\0'; ++s) + { + if(*s == '\\') + { + *s = '/'; + } + } + } + { + /* remove extension */ + char* last_period = strrchr(p, '.'); + if(last_period != NULL) + { + *last_period = '\0'; + } + } + + /* find game root */ + for(; *p != '\0'; ++p) + { + if(_pico_strnicmp(p, "quake", 5) == 0 || _pico_strnicmp(p, "doom", 4) == 0) + { + break; + } + } + /* root-relative */ + for(; *p != '\0'; ++p) + { + if(*p == '/') + { + ++p; + break; + } + } + /* game-relative */ + for(; *p != '\0'; ++p) + { + if(*p == '/') + { + ++p; + break; + } + } + + if(*p != '\0') + { + /* set material name */ + PicoSetShaderName( shader,p ); + } + } + + /* this is just a material with 1 submaterial */ + subMaterial = _ase_add_submaterial( &materials, index, 0, shader ); + } + + /* ydnar: free mapname */ + if( mapname != NULL ) + _pico_free( mapname ); + } // !_pico_stricmp ( "*material" ) + + /* skip unparsed rest of line and continue */ + _pico_parse_skip_rest( p ); + } + + /* ydnar: finish existing surface */ + _ase_submit_triangles(model, materials, vertices, texcoords, colors, faces, numFaces,numVertices,submodel++); + _pico_free(faces); + _pico_free(vertices); + _pico_free(texcoords); + _pico_free(colors); + +#ifdef DEBUG_PM_ASE + _ase_print_materials(materials); + finish = clock(); + elapsed = (double)(finish - start) / CLOCKS_PER_SEC; + _pico_printf( PICO_NORMAL, "Loaded model in in %-.2f second(s)\n", elapsed ); +#endif //DEBUG_PM_ASE + + _ase_free_materials(&materials); + + _pico_free_parser( p ); + + /* return allocated pico model */ + return model; +} + +/* pico file format module definition */ +const picoModule_t picoModuleASE = +{ + "1.0", /* module version string */ + "Autodesk 3DSMAX ASCII", /* module display name */ + "Jared Hefty, seaw0lf", /* author's name */ + "2003 Jared Hefty, 2002 seaw0lf", /* module copyright */ + { + "ase",NULL,NULL,NULL /* default extensions to use */ + }, + _ase_canload, /* validation routine */ + _ase_load, /* load routine */ + NULL, /* save validation routine */ + NULL /* save routine */ +}; diff --git a/tools/urt/libs/picomodel/pm_fm.c b/tools/urt/libs/picomodel/pm_fm.c new file mode 100644 index 00000000..7f15e81f --- /dev/null +++ b/tools/urt/libs/picomodel/pm_fm.c @@ -0,0 +1,667 @@ +/* ----------------------------------------------------------------------------- + +PicoModel Library + +Copyright (c) 2002, Randy Reddig & seaw0lf +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list +of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +Neither the names of the copyright holders nor the names of its contributors may +be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +----------------------------------------------------------------------------- */ + +/* +Nurail: Used pm_md3.c (Randy Reddig) as a template. +*/ + +/* marker */ +#define PM_FM_C + +/* dependencies */ +#include "pm_fm.h" + +//#define FM_VERBOSE_DBG 0 +#undef FM_VERBOSE_DBG +#undef FM_DBG + +typedef struct index_LUT_s +{ + short Vert; + short ST; + struct index_LUT_s *next; + +} index_LUT_t; + +typedef struct index_DUP_LUT_s +{ + short ST; + short OldVert; + +} index_DUP_LUT_t; + + +// _fm_canload() +static int _fm_canload( PM_PARAMS_CANLOAD ) +{ + fm_t fm; + unsigned char *bb; + int fm_file_pos; + + bb = (unsigned char *) buffer; + + // Header + fm.fm_header_hdr = (fm_chunk_header_t *) bb; + fm_file_pos = sizeof(fm_chunk_header_t) + fm.fm_header_hdr->size; +#ifdef FM_VERBOSE_DBG + _pico_printf( PICO_VERBOSE, "IDENT: %s\n", (unsigned char *) fm.fm_header_hdr->ident ); +#endif + if( (strcmp(fm.fm_header_hdr->ident, FM_HEADERCHUNKNAME)) ) + { +#ifdef FM_DBG + _pico_printf( PICO_WARNING, "FM Header Ident incorrect\n"); +#endif + return PICO_PMV_ERROR_IDENT; + } + + // check fm + if( _pico_little_long( fm.fm_header_hdr->version ) != FM_HEADERCHUNKVER ) + { +#ifdef FM_DBG + _pico_printf( PICO_WARNING, "FM Header Version incorrect\n"); +#endif + return PICO_PMV_ERROR_VERSION; + } + + // Skin + fm.fm_skin_hdr = (fm_chunk_header_t *) (bb + fm_file_pos); + fm_file_pos += sizeof(fm_chunk_header_t) + fm.fm_skin_hdr->size; +#ifdef FM_VERBOSE_DBG + _pico_printf( PICO_VERBOSE, "SKIN: %s\n", (unsigned char *) fm.fm_skin_hdr->ident ); +#endif + if( (strcmp(fm.fm_skin_hdr->ident, FM_SKINCHUNKNAME)) ) + { +#ifdef FM_DBG + _pico_printf( PICO_WARNING, "FM Skin Ident incorrect\n"); +#endif + return PICO_PMV_ERROR_IDENT; + } + + // check fm + if( _pico_little_long( fm.fm_skin_hdr->version ) != FM_SKINCHUNKVER ) + { +#ifdef FM_DBG + _pico_printf( PICO_WARNING, "FM Skin Version incorrect\n"); +#endif + return PICO_PMV_ERROR_VERSION; + } + + // st + fm.fm_st_hdr = (fm_chunk_header_t *) (bb + fm_file_pos); + fm_file_pos += sizeof(fm_chunk_header_t) + fm.fm_st_hdr->size; +#ifdef FM_VERBOSE_DBG + _pico_printf( PICO_VERBOSE, "ST: %s\n", (unsigned char *) fm.fm_st_hdr->ident ); +#endif + if( (strcmp(fm.fm_st_hdr->ident, FM_STCOORDCHUNKNAME)) ) + { +#ifdef FM_DBG + _pico_printf( PICO_WARNING, "FM ST Ident incorrect\n"); +#endif + return PICO_PMV_ERROR_IDENT; + } + + // check fm + if( _pico_little_long( fm.fm_st_hdr->version ) != FM_STCOORDCHUNKVER ) + { +#ifdef FM_DBG + _pico_printf( PICO_WARNING, "FM ST Version incorrect\n"); +#endif + return PICO_PMV_ERROR_VERSION; + } + + // tri + fm.fm_tri_hdr = (fm_chunk_header_t *) (bb + fm_file_pos); + fm_file_pos += sizeof(fm_chunk_header_t) + fm.fm_tri_hdr->size; +#ifdef FM_VERBOSE_DBG + _pico_printf( PICO_VERBOSE, "TRI: %s\n", (unsigned char *) fm.fm_tri_hdr->ident ); +#endif + if( (strcmp(fm.fm_tri_hdr->ident, FM_TRISCHUNKNAME)) ) + { +#ifdef FM_DBG + _pico_printf( PICO_WARNING, "FM Tri Ident incorrect\n"); +#endif + return PICO_PMV_ERROR_IDENT; + } + + // check fm + if( _pico_little_long( fm.fm_tri_hdr->version ) != FM_TRISCHUNKVER ) + { +#ifdef FM_DBG + _pico_printf( PICO_WARNING, "FM Tri Version incorrect\n"); +#endif + return PICO_PMV_ERROR_VERSION; + } + + // frame + fm.fm_frame_hdr = (fm_chunk_header_t *) (bb + fm_file_pos); + fm_file_pos += sizeof(fm_chunk_header_t); +#ifdef FM_VERBOSE_DBG + _pico_printf( PICO_VERBOSE, "FRAME: %s\n", (unsigned char *) fm.fm_frame_hdr->ident ); +#endif + if( (strcmp(fm.fm_frame_hdr->ident, FM_FRAMESCHUNKNAME)) ) + { +#ifdef FM_DBG + _pico_printf( PICO_WARNING, "FM Frame Ident incorrect\n"); +#endif + return PICO_PMV_ERROR_IDENT; + } + + // check fm + if( _pico_little_long( fm.fm_frame_hdr->version ) != FM_FRAMESCHUNKVER ) + { +#ifdef FM_DBG + _pico_printf( PICO_WARNING, "FM Frame Version incorrect\n"); +#endif + return PICO_PMV_ERROR_VERSION; + } + + // file seems to be a valid fm + return PICO_PMV_OK; +} + + + +// _fm_load() loads a Heretic 2 model file. +static picoModel_t *_fm_load( PM_PARAMS_LOAD ) +{ + int i, j, dups, dup_index; + int fm_file_pos; + short tot_numVerts; + index_LUT_t *p_index_LUT, *p_index_LUT2, *p_index_LUT3; + index_DUP_LUT_t *p_index_LUT_DUPS; + + fm_vert_normal_t *vert; + + char skinname[FM_SKINPATHSIZE]; + fm_t fm; + fm_header_t *fm_head; + fm_st_t *texCoord; + fm_xyz_st_t *tri_verts; + fm_xyz_st_t *triangle; + fm_frame_t *frame; + + picoByte_t *bb; + picoModel_t *picoModel; + picoSurface_t *picoSurface; + picoShader_t *picoShader; + picoVec3_t xyz, normal; + picoVec2_t st; + picoColor_t color; + + + bb = (picoByte_t*) buffer; + + // Header Header + fm.fm_header_hdr = (fm_chunk_header_t *) bb; + fm_file_pos = sizeof(fm_chunk_header_t) + fm.fm_header_hdr->size; + if( (strcmp(fm.fm_header_hdr->ident, FM_HEADERCHUNKNAME)) ) + { + _pico_printf( PICO_WARNING, "FM Header Ident incorrect\n"); + return NULL; + } + + if( _pico_little_long( fm.fm_header_hdr->version ) != FM_HEADERCHUNKVER ) + { + _pico_printf( PICO_WARNING, "FM Header Version incorrect\n"); + return NULL; + } + + // Skin Header + fm.fm_skin_hdr = (fm_chunk_header_t *) (bb + fm_file_pos); + fm_file_pos += sizeof(fm_chunk_header_t) + fm.fm_skin_hdr->size; + if( (strcmp(fm.fm_skin_hdr->ident, FM_SKINCHUNKNAME)) ) + { + _pico_printf( PICO_WARNING, "FM Skin Ident incorrect\n"); + return NULL; + } + + if( _pico_little_long( fm.fm_skin_hdr->version ) != FM_SKINCHUNKVER ) + { + _pico_printf( PICO_WARNING, "FM Skin Version incorrect\n"); + return NULL; + } + + // ST Header + fm.fm_st_hdr = (fm_chunk_header_t *) (bb + fm_file_pos); + fm_file_pos += sizeof(fm_chunk_header_t) + fm.fm_st_hdr->size; + if( (strcmp(fm.fm_st_hdr->ident, FM_STCOORDCHUNKNAME)) ) + { + _pico_printf( PICO_WARNING, "FM ST Ident incorrect\n"); + return NULL; + } + + if( _pico_little_long( fm.fm_st_hdr->version ) != FM_STCOORDCHUNKVER ) + { + _pico_printf( PICO_WARNING, "FM ST Version incorrect\n"); + return NULL; + } + + // Tris Header + fm.fm_tri_hdr = (fm_chunk_header_t *) (bb + fm_file_pos); + fm_file_pos += sizeof(fm_chunk_header_t) + fm.fm_tri_hdr->size; + if( (strcmp(fm.fm_tri_hdr->ident, FM_TRISCHUNKNAME)) ) + { + _pico_printf( PICO_WARNING, "FM Tri Ident incorrect\n"); + return NULL; + } + + if( _pico_little_long( fm.fm_tri_hdr->version ) != FM_TRISCHUNKVER ) + { + _pico_printf( PICO_WARNING, "FM Tri Version incorrect\n"); + return NULL; + } + + // Frame Header + fm.fm_frame_hdr = (fm_chunk_header_t *) (bb + fm_file_pos); + fm_file_pos += sizeof(fm_chunk_header_t); + if( (strcmp(fm.fm_frame_hdr->ident, FM_FRAMESCHUNKNAME)) ) + { + _pico_printf( PICO_WARNING, "FM Frame Ident incorrect\n"); + return NULL; + } + + if( _pico_little_long( fm.fm_frame_hdr->version ) != FM_FRAMESCHUNKVER ) + { + _pico_printf( PICO_WARNING, "FM Frame Version incorrect\n"); + return NULL; + } + + // Header + fm_file_pos = sizeof(fm_chunk_header_t); + fm_head = fm.fm_header = (fm_header_t *) (bb + fm_file_pos); + fm_file_pos += fm.fm_header_hdr->size; + + // Skin + fm_file_pos += sizeof(fm_chunk_header_t); + fm.fm_skin = (fm_skinpath_t *) (bb + fm_file_pos); + fm_file_pos += fm.fm_skin_hdr->size; + + // ST + fm_file_pos += sizeof(fm_chunk_header_t); + texCoord = fm.fm_st = (fm_st_t *) (bb + fm_file_pos); + fm_file_pos += fm.fm_st_hdr->size; + + // Tri + fm_file_pos += sizeof(fm_chunk_header_t); + tri_verts = fm.fm_tri = (fm_xyz_st_t *) (bb + fm_file_pos); + fm_file_pos += fm.fm_tri_hdr->size; + + // Frame + fm_file_pos += sizeof(fm_chunk_header_t); + frame = fm.fm_frame = (fm_frame_t *) (bb + fm_file_pos); + + // do frame check + if( fm_head->numFrames < 1 ) + { + _pico_printf( PICO_ERROR, "%s has 0 frames!", fileName ); + return NULL; + } + + if( frameNum < 0 || frameNum >= fm_head->numFrames ) + { + _pico_printf( PICO_ERROR, "Invalid or out-of-range FM frame specified" ); + return NULL; + } + + // swap fm + fm_head->skinWidth = _pico_little_long( fm_head->skinWidth ); + fm_head->skinHeight = _pico_little_long( fm_head->skinHeight ); + fm_head->frameSize = _pico_little_long( fm_head->frameSize ); + + fm_head->numSkins = _pico_little_long( fm_head->numSkins ); + fm_head->numXYZ = _pico_little_long( fm_head->numXYZ ); + fm_head->numST = _pico_little_long( fm_head->numST ); + fm_head->numTris = _pico_little_long( fm_head->numTris ); + fm_head->numGLCmds = _pico_little_long( fm_head->numGLCmds ); + fm_head->numFrames = _pico_little_long( fm_head->numFrames ); + + // swap frame scale and translation + for( i = 0; i < 3; i++ ) + { + frame->header.scale[ i ] = _pico_little_float( frame->header.scale[ i ] ); + frame->header.translate[ i ] = _pico_little_float( frame->header.translate[ i ] ); + } + + // swap triangles + triangle = tri_verts; + for( i = 0; i < fm_head->numTris; i++, triangle++ ) + { + for( j = 0; j < 3; j++ ) + { + triangle->index_xyz[ j ] = _pico_little_short( triangle->index_xyz[ j ] ); + triangle->index_st[ j ] = _pico_little_short( triangle->index_st[ j ] ); + } + } + + // swap st coords + for( i = 0; i < fm_head->numST; i++ ) + { + texCoord->s = _pico_little_short( texCoord[i].s ); + texCoord->t = _pico_little_short( texCoord[i].t ); + } + // set Skin Name + strncpy(skinname, (unsigned char *) fm.fm_skin, FM_SKINPATHSIZE ); + +#ifdef FM_VERBOSE_DBG + // Print out md2 values + _pico_printf(PICO_VERBOSE,"numSkins->%d numXYZ->%d numST->%d numTris->%d numFrames->%d\nSkin Name \"%s\"\n", fm_head->numSkins, fm_head->numXYZ, fm_head->numST, fm_head->numTris, fm_head->numFrames, &skinname ); +#endif + + // detox Skin name + _pico_setfext( skinname, "" ); + _pico_unixify( skinname ); + + /* create new pico model */ + picoModel = PicoNewModel(); + if( picoModel == NULL ) + { + _pico_printf( PICO_ERROR, "Unable to allocate a new model" ); + return NULL; + } + + /* do model setup */ + PicoSetModelFrameNum( picoModel, frameNum ); + PicoSetModelNumFrames( picoModel, fm_head->numFrames ); /* sea */ + PicoSetModelName( picoModel, fileName ); + PicoSetModelFileName( picoModel, fileName ); + + // allocate new pico surface + picoSurface = PicoNewSurface( picoModel ); + if( picoSurface == NULL ) + { + _pico_printf( PICO_ERROR, "Unable to allocate a new model surface" ); + PicoFreeModel( picoModel ); + return NULL; + } + + + PicoSetSurfaceType( picoSurface, PICO_TRIANGLES ); + PicoSetSurfaceName( picoSurface, frame->header.name ); + picoShader = PicoNewShader( picoModel ); + if( picoShader == NULL ) + { + _pico_printf( PICO_ERROR, "Unable to allocate a new model shader" ); + PicoFreeModel( picoModel ); + return NULL; + } + + PicoSetShaderName( picoShader, skinname ); + + // associate current surface with newly created shader + PicoSetSurfaceShader( picoSurface, picoShader ); + + // Init LUT for Verts + p_index_LUT = (index_LUT_t *)_pico_alloc(sizeof(index_LUT_t) * fm_head->numXYZ); + for(i=0; inumXYZ; i++) + { + p_index_LUT[i].Vert = -1; + p_index_LUT[i].ST = -1; + p_index_LUT[i].next = NULL; + } + + // Fill in Look Up Table, and allocate/fill Linked List from vert array as needed for dup STs per Vert. + tot_numVerts = fm_head->numXYZ; + dups = 0; + triangle = tri_verts; + + for(i=0; inumTris; i++) + { + for(j=0; j<3; j++) + { + if (p_index_LUT[triangle->index_xyz[j]].ST == -1) // No Main Entry + p_index_LUT[triangle->index_xyz[j]].ST = triangle->index_st[j]; + + else if (triangle->index_st[j] == p_index_LUT[triangle->index_xyz[j]].ST ) // Equal to Main Entry + { +#ifdef FM_VERBOSE_DBG + _pico_printf( PICO_NORMAL, "-> Tri #%d, Vert %d:\t XYZ:%d ST:%d\n", i, j, triangle->index_xyz[j], triangle->index_st[j]); +#endif + continue; + } + else if ( (p_index_LUT[triangle->index_xyz[j]].next == NULL) ) // Not equal to Main entry, and no LL entry + { // Add first entry of LL from Main + p_index_LUT2 = (index_LUT_t *)_pico_alloc(sizeof(index_LUT_t)); + if (p_index_LUT2 == NULL) + _pico_printf( PICO_NORMAL, " Couldn't allocate memory!\n"); + p_index_LUT[triangle->index_xyz[j]].next = (index_LUT_t *)p_index_LUT2; + p_index_LUT2->Vert = dups; + p_index_LUT2->ST = triangle->index_st[j]; + p_index_LUT2->next = NULL; +#ifdef FM_VERBOSE_DBG + _pico_printf( PICO_NORMAL, " ADDING first LL XYZ:%d DUP:%d ST:%d\n", triangle->index_xyz[j], dups, triangle->index_st[j]); +#endif + triangle->index_xyz[j] = dups + fm_head->numXYZ; // Make change in Tri hunk + dups++; + } + else // Try to find in LL from Main Entry + { + p_index_LUT3 = p_index_LUT2 = p_index_LUT[triangle->index_xyz[j]].next; + while ( (p_index_LUT2 != NULL) && (triangle->index_xyz[j] != p_index_LUT2->Vert) ) // Walk down LL + { + p_index_LUT3 = p_index_LUT2; + p_index_LUT2 = p_index_LUT2->next; + } + p_index_LUT2 = p_index_LUT3; + + if ( triangle->index_st[j] == p_index_LUT2->ST ) // Found it + { + triangle->index_xyz[j] = p_index_LUT2->Vert + fm_head->numXYZ; // Make change in Tri hunk +#ifdef FM_VERBOSE_DBG + _pico_printf( PICO_NORMAL, "--> Tri #%d, Vert %d:\t XYZ:%d ST:%d\n", i, j, triangle->index_xyz[j], triangle->index_st[j]); +#endif + continue; + } + + if ( p_index_LUT2->next == NULL) // Didn't find it. Add entry to LL. + { + // Add the Entry + p_index_LUT3 = (index_LUT_t *)_pico_alloc(sizeof(index_LUT_t)); + if (p_index_LUT3 == NULL) + _pico_printf( PICO_NORMAL, " Couldn't allocate memory!\n"); + p_index_LUT2->next = (index_LUT_t *)p_index_LUT3; + p_index_LUT3->Vert = dups; + p_index_LUT3->ST = triangle->index_st[j]; + p_index_LUT3->next = NULL; +#ifdef FM_VERBOSE_DBG + _pico_printf( PICO_NORMAL, " ADDING additional LL XYZ:%d DUP:%d NewXYZ:%d ST:%d\n", triangle->index_xyz[j], dups, dups + (fm_head->numXYZ), triangle->index_st[j]); +#endif + triangle->index_xyz[j] = dups + fm_head->numXYZ; // Make change in Tri hunk + dups++; + } + } +#ifdef FM_VERBOSE_DBG + _pico_printf( PICO_NORMAL, "---> Tri #%d, Vert %d:\t XYZ:%d ST:%d\n", i, j, triangle->index_xyz[j], triangle->index_st[j]); +#endif + } + triangle++; + } + + // malloc and build array for Dup STs + p_index_LUT_DUPS = (index_DUP_LUT_t *)_pico_alloc(sizeof(index_DUP_LUT_t) * dups); + if (p_index_LUT_DUPS == NULL) + _pico_printf( PICO_NORMAL, " Couldn't allocate memory!\n"); + + dup_index = 0; + for(i=0; inumXYZ; i++) + { + p_index_LUT2 = p_index_LUT[i].next; + while (p_index_LUT2 != NULL) + { + p_index_LUT_DUPS[p_index_LUT2->Vert].OldVert = i; + p_index_LUT_DUPS[p_index_LUT2->Vert].ST = p_index_LUT2->ST; + dup_index++; + p_index_LUT2 = p_index_LUT2->next; + } + } +#ifdef FM_VERBOSE_DBG + _pico_printf( PICO_NORMAL, " Dups = %d\n", dups); + _pico_printf( PICO_NORMAL, " Dup Index = %d\n", dup_index); +#endif + for(i=0; inumXYZ; i++) + { +#ifdef FM_VERBOSE_DBG + _pico_printf( PICO_NORMAL, "Vert: %4d\t%4d",i, p_index_LUT[i].ST); +#endif + if (p_index_LUT[i].next != NULL) + { + + p_index_LUT2 = p_index_LUT[i].next; + do { +#ifdef FM_VERBOSE_DBG + _pico_printf( PICO_NORMAL, " %4d %4d", p_index_LUT2->Vert, p_index_LUT2->ST); +#endif + p_index_LUT2 = p_index_LUT2->next; + } while ( p_index_LUT2 != NULL); + + } +#ifdef FM_VERBOSE_DBG + _pico_printf( PICO_NORMAL, "\n"); +#endif + } + + +#ifdef FM_VERBOSE_DBG + for(i=0; inumTris; i++) + { + for(j=0; j<3; j++) + _pico_printf( PICO_NORMAL, "Tri #%d, Vert %d:\t XYZ:%d ST:%d\n", i, j, triangle->index_xyz[j], triangle->index_st[j]); + _pico_printf( PICO_NORMAL, "\n"); + triangle++; + } +#endif + // Build Picomodel + triangle = tri_verts; + for( j = 0; j < fm_head->numTris; j++, triangle++ ) + { + PicoSetSurfaceIndex( picoSurface, j*3 , triangle->index_xyz[0] ); + PicoSetSurfaceIndex( picoSurface, j*3+1 , triangle->index_xyz[1] ); + PicoSetSurfaceIndex( picoSurface, j*3+2 , triangle->index_xyz[2] ); + } + + vert = (fm_vert_normal_t*) ((picoByte_t*) (frame->verts) ); + for(i=0; i< fm_head->numXYZ; i++, vert++) + { + /* set vertex origin */ + xyz[ 0 ] = vert->v[0] * frame->header.scale[0] + frame->header.translate[0]; + xyz[ 1 ] = vert->v[1] * frame->header.scale[1] + frame->header.translate[1]; + xyz[ 2 ] = vert->v[2] * frame->header.scale[2] + frame->header.translate[2]; + PicoSetSurfaceXYZ( picoSurface, i , xyz ); + + /* set normal */ + normal[ 0 ] = fm_normals[vert->lightnormalindex][0]; + normal[ 1 ] = fm_normals[vert->lightnormalindex][1]; + normal[ 2 ] = fm_normals[vert->lightnormalindex][2]; + PicoSetSurfaceNormal( picoSurface, i , normal ); + + /* set st coords */ + st[ 0 ] = ((texCoord[p_index_LUT[i].ST].s) / ((float)fm_head->skinWidth)); + st[ 1 ] = (texCoord[p_index_LUT[i].ST].t / ((float)fm_head->skinHeight)); + PicoSetSurfaceST( picoSurface, 0, i , st ); + } + + if (dups) + { + for(i=0; iverts[j].v[0] * frame->header.scale[0] + frame->header.translate[0]; + xyz[ 1 ] = frame->verts[j].v[1] * frame->header.scale[1] + frame->header.translate[1]; + xyz[ 2 ] = frame->verts[j].v[2] * frame->header.scale[2] + frame->header.translate[2]; + PicoSetSurfaceXYZ( picoSurface, i + fm_head->numXYZ , xyz ); + + /* set normal */ + normal[ 0 ] = fm_normals[frame->verts[j].lightnormalindex][0]; + normal[ 1 ] = fm_normals[frame->verts[j].lightnormalindex][1]; + normal[ 2 ] = fm_normals[frame->verts[j].lightnormalindex][2]; + PicoSetSurfaceNormal( picoSurface, i + fm_head->numXYZ , normal ); + + /* set st coords */ + st[ 0 ] = ((texCoord[p_index_LUT_DUPS[i].ST].s) / ((float)fm_head->skinWidth)); + st[ 1 ] = (texCoord[p_index_LUT_DUPS[i].ST].t / ((float)fm_head->skinHeight)); + PicoSetSurfaceST( picoSurface, 0, i + fm_head->numXYZ , st ); + } + } + + /* set color */ + PicoSetSurfaceColor( picoSurface, 0, 0, color ); + + // Free up malloc'ed LL entries + for(i=0; inumXYZ; i++) + { + if(p_index_LUT[i].next != NULL) + { + p_index_LUT2 = p_index_LUT[i].next; + do { + p_index_LUT3 = p_index_LUT2->next; + _pico_free(p_index_LUT2); + p_index_LUT2 = p_index_LUT3; + dups--; + } while (p_index_LUT2 != NULL); + } + } + + if (dups) + _pico_printf(PICO_WARNING, " Not all LL mallocs freed\n"); + + // Free malloc'ed LUTs + _pico_free(p_index_LUT); + _pico_free(p_index_LUT_DUPS); + + /* return the new pico model */ + return picoModel; + +} + + + +/* pico file format module definition */ +const picoModule_t picoModuleFM = +{ + "0.85", /* module version string */ + "Heretic 2 FM", /* module display name */ + "Nurail", /* author's name */ + "2003 Nurail", /* module copyright */ + { + "fm", NULL, NULL, NULL /* default extensions to use */ + }, + _fm_canload, /* validation routine */ + _fm_load, /* load routine */ + NULL, /* save validation routine */ + NULL /* save routine */ +}; diff --git a/tools/urt/libs/picomodel/pm_fm.h b/tools/urt/libs/picomodel/pm_fm.h new file mode 100644 index 00000000..ce43d334 --- /dev/null +++ b/tools/urt/libs/picomodel/pm_fm.h @@ -0,0 +1,367 @@ +/* ----------------------------------------------------------------------------- + +PicoModel Library + +Copyright (c) 2002, Randy Reddig & seaw0lf +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list +of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +Neither the names of the copyright holders nor the names of its contributors may +be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +----------------------------------------------------------------------------- */ + +// This header file is based from the following: + +/* + FlexModel.H - Header file for FlexModel file structure + + By Chris Burke + serotonin@earthlink.net +*/ + +#ifndef __PM_FM_H__ +#define __PM_FM_H__ + +#include "picointernal.h" + + +// +// Absolute limits (from QData / QMView source) +// +#define MAX_FM_TRIANGLES 2048 +#define MAX_FM_VERTS 2048 +#define MAX_FM_FRAMES 2048 +#define MAX_FM_SKINS 64 +#define MAX_FM_SKINNAME 64 +#define MAX_FM_MESH_NODES 16 + +#define DTRIVERTX_V0 0 +#define DTRIVERTX_V1 1 +#define DTRIVERTX_V2 2 +#define DTRIVERTX_LNI 3 +#define DTRIVERTX_SIZE 4 + +#define SKINPAGE_WIDTH 640 +#define SKINPAGE_HEIGHT 480 + +#define ENCODED_WIDTH_X 92 +#define ENCODED_WIDTH_Y 475 +#define ENCODED_HEIGHT_X 128 +#define ENCODED_HEIGHT_Y 475 + +#define SCALE_ADJUST_FACTOR 0.96 + +#define INFO_HEIGHT 5 +#define INFO_Y (SKINPAGE_HEIGHT-INFO_HEIGHT) + +#ifndef byte + #define byte unsigned char +#endif + + +// +// Generic header on every chunk +// +#define FM_MAXCHUNKIDENT 32L +typedef struct +{ + char ident[FM_MAXCHUNKIDENT]; + unsigned int version; + unsigned int size; +} fm_chunk_header_t; + +// +// The format of the "header" chunk +// +#define FM_HEADERCHUNKNAME "header" +#define FM_HEADERCHUNKVER 2 +#define FM_HEADERCHUNKSIZE 40 +typedef struct +{ + int skinWidth; // in pixels + int skinHeight; // in pixels + int frameSize; // size of each frame (in bytes) + int numSkins; // number of skins + int numXYZ; // number of unique vertices in 3D space + int numST; // number of unique vertices in texture space + int numTris; // number of unique triangles + int numGLCmds; // # 32-bit elements in strip/fan command list + int numFrames; // number of animation frames + int numMeshNodes; // number of mesh nodes +} fm_header_t; + +// +// The format of an entry in the "skin" chunk. +// The number of entries is given in the fmheader chunk +// +#define FM_SKINCHUNKNAME "skin" +#define FM_SKINCHUNKVER 1 +#define FM_MAXPATHLENGTH 64L +#define FM_SKINPATHSIZE (FM_MAXPATHLENGTH) +typedef struct +{ + char path[FM_SKINPATHSIZE]; // path, relative to 'base' +} fm_skinpath_t; + +// +// The format of the "st coord" chunk. This is a list +// of unique skin texture (u, v) coordinates to be mapped +// to verteces of the model +// +#define FM_STCOORDCHUNKNAME "st coord" +#define FM_STCOORDCHUNKVER 1 +#define FM_STCOORDUVSIZE (2L + 2L) + +typedef struct +{ + short s; + short t; +} fm_st_t; + +// +// The format of the "tris" chunk. This is a list of vertex indeces +// in 3D space, and the corresponding vertex indeces in texture space. +// +#define FM_TRISCHUNKNAME "tris" +#define FM_TRISCHUNKVER 1 +#define FM_TRISINFOSIZE (2L*3 + 2L*3) + +typedef struct +{ + short index_xyz[3]; + short index_st[3]; +} fm_xyz_st_t; + + +// +// The format of the "frames" chunk. This is a list of animation +// frames, each specifying the coordinates and "light normal" index +// of every vertex of the model in 3D space. +// +#define FM_FRAMESCHUNKNAME "frames" +#define FM_FRAMESCHUNKVER 1 + +#define FM_NUMVERTEXNORMALS 162 + +// Frame info +typedef struct +{ + byte v[3]; // scaled by header info + byte lightnormalindex; // index in canned table of closest vertex normal +} fm_vert_normal_t; + +typedef struct +{ + float scale[3]; // multiply byte verts by this + float translate[3]; // then add this + char name[16]; // frame name +} fm_framehdr_t; + +typedef struct +{ + fm_framehdr_t header; // One header per frame + fm_vert_normal_t verts[1]; // variable number of these +} fm_frame_t; + +typedef struct +{ + fm_chunk_header_t *fm_header_hdr; + fm_header_t *fm_header; + fm_chunk_header_t *fm_skin_hdr; + fm_skinpath_t *fm_skin; + fm_chunk_header_t *fm_st_hdr; + fm_st_t *fm_st; + fm_chunk_header_t *fm_tri_hdr; + fm_xyz_st_t *fm_tri; + fm_chunk_header_t *fm_frame_hdr; + fm_frame_t *fm_frame; +} fm_t; + +float fm_normals[FM_NUMVERTEXNORMALS][3] = { + {-0.525731f, 0.000000f, 0.850651f}, + {-0.442863f, 0.238856f, 0.864188f}, + {-0.295242f, 0.000000f, 0.955423f}, + {-0.309017f, 0.500000f, 0.809017f}, + {-0.162460f, 0.262866f, 0.951056f}, + {0.000000f, 0.000000f, 1.000000f}, + {0.000000f, 0.850651f, 0.525731f}, + {-0.147621f, 0.716567f, 0.681718f}, + {0.147621f, 0.716567f, 0.681718f}, + {0.000000f, 0.525731f, 0.850651f}, + {0.309017f, 0.500000f, 0.809017f}, + {0.525731f, 0.000000f, 0.850651f}, + {0.295242f, 0.000000f, 0.955423f}, + {0.442863f, 0.238856f, 0.864188f}, + {0.162460f, 0.262866f, 0.951056f}, + {-0.681718f, 0.147621f, 0.716567f}, + {-0.809017f, 0.309017f, 0.500000f}, + {-0.587785f, 0.425325f, 0.688191f}, + {-0.850651f, 0.525731f, 0.000000f}, + {-0.864188f, 0.442863f, 0.238856f}, + {-0.716567f, 0.681718f, 0.147621f}, + {-0.688191f, 0.587785f, 0.425325f}, + {-0.500000f, 0.809017f, 0.309017f}, + {-0.238856f, 0.864188f, 0.442863f}, + {-0.425325f, 0.688191f, 0.587785f}, + {-0.716567f, 0.681718f, -0.147621f}, + {-0.500000f, 0.809017f, -0.309017f}, + {-0.525731f, 0.850651f, 0.000000f}, + {0.000000f, 0.850651f, -0.525731f}, + {-0.238856f, 0.864188f, -0.442863f}, + {0.000000f, 0.955423f, -0.295242f}, + {-0.262866f, 0.951056f, -0.162460f}, + {0.000000f, 1.000000f, 0.000000f}, + {0.000000f, 0.955423f, 0.295242f}, + {-0.262866f, 0.951056f, 0.162460f}, + {0.238856f, 0.864188f, 0.442863f}, + {0.262866f, 0.951056f, 0.162460f}, + {0.500000f, 0.809017f, 0.309017f}, + {0.238856f, 0.864188f, -0.442863f}, + {0.262866f, 0.951056f, -0.162460f}, + {0.500000f, 0.809017f, -0.309017f}, + {0.850651f, 0.525731f, 0.000000f}, + {0.716567f, 0.681718f, 0.147621f}, + {0.716567f, 0.681718f, -0.147621f}, + {0.525731f, 0.850651f, 0.000000f}, + {0.425325f, 0.688191f, 0.587785f}, + {0.864188f, 0.442863f, 0.238856f}, + {0.688191f, 0.587785f, 0.425325f}, + {0.809017f, 0.309017f, 0.500000f}, + {0.681718f, 0.147621f, 0.716567f}, + {0.587785f, 0.425325f, 0.688191f}, + {0.955423f, 0.295242f, 0.000000f}, + {1.000000f, 0.000000f, 0.000000f}, + {0.951056f, 0.162460f, 0.262866f}, + {0.850651f, -0.525731f, 0.000000f}, + {0.955423f, -0.295242f, 0.000000f}, + {0.864188f, -0.442863f, 0.238856f}, + {0.951056f, -0.162460f, 0.262866f}, + {0.809017f, -0.309017f, 0.500000f}, + {0.681718f, -0.147621f, 0.716567f}, + {0.850651f, 0.000000f, 0.525731f}, + {0.864188f, 0.442863f, -0.238856f}, + {0.809017f, 0.309017f, -0.500000f}, + {0.951056f, 0.162460f, -0.262866f}, + {0.525731f, 0.000000f, -0.850651f}, + {0.681718f, 0.147621f, -0.716567f}, + {0.681718f, -0.147621f, -0.716567f}, + {0.850651f, 0.000000f, -0.525731f}, + {0.809017f, -0.309017f, -0.500000f}, + {0.864188f, -0.442863f, -0.238856f}, + {0.951056f, -0.162460f, -0.262866f}, + {0.147621f, 0.716567f, -0.681718f}, + {0.309017f, 0.500000f, -0.809017f}, + {0.425325f, 0.688191f, -0.587785f}, + {0.442863f, 0.238856f, -0.864188f}, + {0.587785f, 0.425325f, -0.688191f}, + {0.688191f, 0.587785f, -0.425325f}, + {-0.147621f, 0.716567f, -0.681718f}, + {-0.309017f, 0.500000f, -0.809017f}, + {0.000000f, 0.525731f, -0.850651f}, + {-0.525731f, 0.000000f, -0.850651f}, + {-0.442863f, 0.238856f, -0.864188f}, + {-0.295242f, 0.000000f, -0.955423f}, + {-0.162460f, 0.262866f, -0.951056f}, + {0.000000f, 0.000000f, -1.000000f}, + {0.295242f, 0.000000f, -0.955423f}, + {0.162460f, 0.262866f, -0.951056f}, + {-0.442863f, -0.238856f, -0.864188f}, + {-0.309017f, -0.500000f, -0.809017f}, + {-0.162460f, -0.262866f, -0.951056f}, + {0.000000f, -0.850651f, -0.525731f}, + {-0.147621f, -0.716567f, -0.681718f}, + {0.147621f, -0.716567f, -0.681718f}, + {0.000000f, -0.525731f, -0.850651f}, + {0.309017f, -0.500000f, -0.809017f}, + {0.442863f, -0.238856f, -0.864188f}, + {0.162460f, -0.262866f, -0.951056f}, + {0.238856f, -0.864188f, -0.442863f}, + {0.500000f, -0.809017f, -0.309017f}, + {0.425325f, -0.688191f, -0.587785f}, + {0.716567f, -0.681718f, -0.147621f}, + {0.688191f, -0.587785f, -0.425325f}, + {0.587785f, -0.425325f, -0.688191f}, + {0.000000f, -0.955423f, -0.295242f}, + {0.000000f, -1.000000f, 0.000000f}, + {0.262866f, -0.951056f, -0.162460f}, + {0.000000f, -0.850651f, 0.525731f}, + {0.000000f, -0.955423f, 0.295242f}, + {0.238856f, -0.864188f, 0.442863f}, + {0.262866f, -0.951056f, 0.162460f}, + {0.500000f, -0.809017f, 0.309017f}, + {0.716567f, -0.681718f, 0.147621f}, + {0.525731f, -0.850651f, 0.000000f}, + {-0.238856f, -0.864188f, -0.442863f}, + {-0.500000f, -0.809017f, -0.309017f}, + {-0.262866f, -0.951056f, -0.162460f}, + {-0.850651f, -0.525731f, 0.000000f}, + {-0.716567f, -0.681718f, -0.147621f}, + {-0.716567f, -0.681718f, 0.147621f}, + {-0.525731f, -0.850651f, 0.000000f}, + {-0.500000f, -0.809017f, 0.309017f}, + {-0.238856f, -0.864188f, 0.442863f}, + {-0.262866f, -0.951056f, 0.162460f}, + {-0.864188f, -0.442863f, 0.238856f}, + {-0.809017f, -0.309017f, 0.500000f}, + {-0.688191f, -0.587785f, 0.425325f}, + {-0.681718f, -0.147621f, 0.716567f}, + {-0.442863f, -0.238856f, 0.864188f}, + {-0.587785f, -0.425325f, 0.688191f}, + {-0.309017f, -0.500000f, 0.809017f}, + {-0.147621f, -0.716567f, 0.681718f}, + {-0.425325f, -0.688191f, 0.587785f}, + {-0.162460f, -0.262866f, 0.951056f}, + {0.442863f, -0.238856f, 0.864188f}, + {0.162460f, -0.262866f, 0.951056f}, + {0.309017f, -0.500000f, 0.809017f}, + {0.147621f, -0.716567f, 0.681718f}, + {0.000000f, -0.525731f, 0.850651f}, + {0.425325f, -0.688191f, 0.587785f}, + {0.587785f, -0.425325f, 0.688191f}, + {0.688191f, -0.587785f, 0.425325f}, + {-0.955423f, 0.295242f, 0.000000f}, + {-0.951056f, 0.162460f, 0.262866f}, + {-1.000000f, 0.000000f, 0.000000f}, + {-0.850651f, 0.000000f, 0.525731f}, + {-0.955423f, -0.295242f, 0.000000f}, + {-0.951056f, -0.162460f, 0.262866f}, + {-0.864188f, 0.442863f, -0.238856f}, + {-0.951056f, 0.162460f, -0.262866f}, + {-0.809017f, 0.309017f, -0.500000f}, + {-0.864188f, -0.442863f, -0.238856f}, + {-0.951056f, -0.162460f, -0.262866f}, + {-0.809017f, -0.309017f, -0.500000f}, + {-0.681718f, 0.147621f, -0.716567f}, + {-0.681718f, -0.147621f, -0.716567f}, + {-0.850651f, 0.000000f, -0.525731f}, + {-0.688191f, 0.587785f, -0.425325f}, + {-0.587785f, 0.425325f, -0.688191f}, + {-0.425325f, 0.688191f, -0.587785f}, + {-0.425325f, -0.688191f, -0.587785f}, + {-0.587785f, -0.425325f, -0.688191f}, + {-0.688191f, -0.587785f, -0.425325f}, +}; + +#endif diff --git a/tools/urt/libs/picomodel/pm_lwo.c b/tools/urt/libs/picomodel/pm_lwo.c new file mode 100644 index 00000000..c5a6a7c6 --- /dev/null +++ b/tools/urt/libs/picomodel/pm_lwo.c @@ -0,0 +1,445 @@ +/* ----------------------------------------------------------------------------- + +PicoModel Library + +Copyright (c) 2002, Randy Reddig & seaw0lf +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list +of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +Neither the names of the copyright holders nor the names of its contributors may +be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +----------------------------------------------------------------------------- */ + +/* marker */ +#define PM_LWO_C + +/* dependencies */ +#include "picointernal.h" +#include "lwo/lwo2.h" + +/* uncomment when debugging this module */ +/*#define DEBUG_PM_LWO*/ + +#ifdef DEBUG_PM_LWO +#include "time.h" +#endif + +/* helper functions */ +static const char *lwo_lwIDToStr( unsigned int lwID ) +{ + static char lwIDStr[5]; + + if (!lwID) + { + return "n/a"; + } + + lwIDStr[ 0 ] = (char)((lwID) >> 24); + lwIDStr[ 1 ] = (char)((lwID) >> 16); + lwIDStr[ 2 ] = (char)((lwID) >> 8); + lwIDStr[ 3 ] = (char)((lwID)); + lwIDStr[ 4 ] = '\0'; + + return lwIDStr; +} + +/* +_lwo_canload() +validates a LightWave Object model file. btw, i use the +preceding underscore cause it's a static func referenced +by one structure only. +*/ +static int _lwo_canload( PM_PARAMS_CANLOAD ) +{ + picoMemStream_t *s; + unsigned int failID = 0; + int failpos = -1; + int ret; + + /* create a new pico memorystream */ + s = _pico_new_memstream( (picoByte_t *)buffer, bufSize ); + if (s == NULL) + { + return PICO_PMV_ERROR_MEMORY; + } + + ret = lwValidateObject( fileName, s, &failID, &failpos ); + + _pico_free_memstream( s ); + + return ret; +} + +/* +_lwo_load() +loads a LightWave Object model file. +*/ +static picoModel_t *_lwo_load( PM_PARAMS_LOAD ) +{ + picoMemStream_t *s; + unsigned int failID = 0; + int failpos = -1; + lwObject *obj; + lwSurface *surface; + lwLayer *layer; + lwPoint *pt; + lwPolygon *pol; + lwPolVert *v; + lwVMapPt *vm; + char name[ 64 ]; + int i, j, k, numverts; + + picoModel_t *picoModel; + picoSurface_t *picoSurface; + picoShader_t *picoShader; + picoVec3_t xyz, normal; + picoVec2_t st; + picoColor_t color; + + int defaultSTAxis[ 2 ]; + picoVec2_t defaultXYZtoSTScale; + + picoVertexCombinationHash_t **hashTable; + picoVertexCombinationHash_t *vertexCombinationHash; + +#ifdef DEBUG_PM_LWO + clock_t load_start, load_finish, convert_start, convert_finish; + double load_elapsed, convert_elapsed; + + load_start = clock(); +#endif + + /* do frame check */ + if( frameNum < 0 || frameNum >= 1 ) + { + _pico_printf( PICO_ERROR, "Invalid or out-of-range LWO frame specified" ); + return NULL; + } + + /* create a new pico memorystream */ + s = _pico_new_memstream( (picoByte_t *)buffer, bufSize ); + if (s == NULL) + { + return NULL; + } + + obj = lwGetObject( fileName, s, &failID, &failpos ); + + _pico_free_memstream( s ); + + if( !obj ) { + _pico_printf( PICO_ERROR, "Couldn't load LWO file, failed on ID '%s', position %d", lwo_lwIDToStr( failID ), failpos ); + return NULL; + } + +#ifdef DEBUG_PM_LWO + convert_start = load_finish = clock(); + load_elapsed = (double)(load_finish - load_start) / CLOCKS_PER_SEC; +#endif + + /* ------------------------------------------------- + pico model creation + ------------------------------------------------- */ + + /* create a new pico model */ + picoModel = PicoNewModel(); + if (picoModel == NULL) + { + _pico_printf( PICO_ERROR, "Unable to allocate a new model" ); + return NULL; + } + + /* do model setup */ + PicoSetModelFrameNum( picoModel, frameNum ); + PicoSetModelNumFrames( picoModel, 1 ); + PicoSetModelName( picoModel, fileName ); + PicoSetModelFileName( picoModel, fileName ); + + /* create all polygons from layer[ 0 ] that belong to this surface */ + layer = &obj->layer[0]; + + /* warn the user that other layers are discarded */ + if (obj->nlayers > 1) + { + _pico_printf( PICO_WARNING, "LWO loader discards any geometry data not in Layer 1 (%d layers found)", obj->nlayers ); + } + + /* initialize dummy normal */ + normal[ 0 ] = normal[ 1 ] = normal[ 2 ] = 0.f; + + /* setup default st map */ + st[ 0 ] = st[ 1 ] = 0.f; /* st[0] holds max, st[1] holds max par one */ + defaultSTAxis[ 0 ] = 0; + defaultSTAxis[ 1 ] = 1; + for( i = 0; i < 3; i++ ) + { + float min = layer->bbox[ i ]; + float max = layer->bbox[ i + 3 ]; + float size = max - min; + + if (size > st[ 0 ]) + { + defaultSTAxis[ 1 ] = defaultSTAxis[ 0 ]; + defaultSTAxis[ 0 ] = i; + + st[ 1 ] = st[ 0 ]; + st[ 0 ] = size; + } + else if (size > st[ 1 ]) + { + defaultSTAxis[ 1 ] = i; + st[ 1 ] = size; + } + } + defaultXYZtoSTScale[ 0 ] = 4.f / st[ 0 ]; + defaultXYZtoSTScale[ 1 ] = 4.f / st[ 1 ]; + + /* LWO surfaces become pico surfaces */ + surface = obj->surf; + while (surface) + { + /* allocate new pico surface */ + picoSurface = PicoNewSurface( picoModel ); + if (picoSurface == NULL) + { + _pico_printf( PICO_ERROR, "Unable to allocate a new model surface" ); + PicoFreeModel( picoModel ); + lwFreeObject( obj ); + return NULL; + } + + /* LWO model surfaces are all triangle meshes */ + PicoSetSurfaceType( picoSurface, PICO_TRIANGLES ); + + /* set surface name */ + PicoSetSurfaceName( picoSurface, surface->name ); + + /* create new pico shader */ + picoShader = PicoNewShader( picoModel ); + if (picoShader == NULL) + { + _pico_printf( PICO_ERROR, "Unable to allocate a new model shader" ); + PicoFreeModel( picoModel ); + lwFreeObject( obj ); + return NULL; + } + + /* detox and set shader name */ + strncpy( name, surface->name, sizeof(name) ); + _pico_first_token( name ); + _pico_setfext( name, "" ); + _pico_unixify( name ); + PicoSetShaderName( picoShader, name ); + + /* associate current surface with newly created shader */ + PicoSetSurfaceShader( picoSurface, picoShader ); + + /* copy indices and vertex data */ + numverts = 0; + + hashTable = PicoNewVertexCombinationHashTable(); + + if (hashTable == NULL) + { + _pico_printf( PICO_ERROR, "Unable to allocate hash table" ); + PicoFreeModel( picoModel ); + lwFreeObject( obj ); + return NULL; + } + + for( i = 0, pol = layer->polygon.pol; i < layer->polygon.count; i++, pol++ ) + { + /* does this polygon belong to this surface? */ + if (pol->surf != surface) + continue; + + /* we only support polygons of the FACE type */ + if (pol->type != ID_FACE) + { + _pico_printf( PICO_WARNING, "LWO loader discarded a polygon because it's type != FACE (%s)", lwo_lwIDToStr( pol->type ) ); + continue; + } + + /* NOTE: LWO has support for non-convex polygons, do we want to store them as well? */ + if (pol->nverts != 3) + { + _pico_printf( PICO_WARNING, "LWO loader discarded a polygon because it has != 3 verts (%d)", pol->nverts ); + continue; + } + + for( j = 0, v = pol->v; j < 3; j++, v++ ) + { + pt = &layer->point.pt[ v->index ]; + + /* setup data */ + xyz[ 0 ] = pt->pos[ 0 ]; + xyz[ 1 ] = pt->pos[ 2 ]; + xyz[ 2 ] = pt->pos[ 1 ]; + +/* doom3 lwo data doesn't seem to have smoothing-angle information */ +#if 0 + if(surface->smooth <= 0) + { + /* use face normals */ + normal[ 0 ] = v->norm[ 0 ]; + normal[ 1 ] = v->norm[ 2 ]; + normal[ 2 ] = v->norm[ 1 ]; + } + else +#endif + { + /* smooth normals later */ + normal[ 0 ] = 0; + normal[ 1 ] = 0; + normal[ 2 ] = 0; + } + + st[ 0 ] = xyz[ defaultSTAxis[ 0 ] ] * defaultXYZtoSTScale[ 0 ]; + st[ 1 ] = xyz[ defaultSTAxis[ 1 ] ] * defaultXYZtoSTScale[ 1 ]; + + color[ 0 ] = (picoByte_t)(surface->color.rgb[ 0 ] * surface->diffuse.val * 0xFF); + color[ 1 ] = (picoByte_t)(surface->color.rgb[ 1 ] * surface->diffuse.val * 0xFF); + color[ 2 ] = (picoByte_t)(surface->color.rgb[ 2 ] * surface->diffuse.val * 0xFF); + color[ 3 ] = 0xFF; + + /* set from points */ + for( k = 0, vm = pt->vm; k < pt->nvmaps; k++, vm++ ) + { + if (vm->vmap->type == LWID_('T','X','U','V')) + { + /* set st coords */ + st[ 0 ] = vm->vmap->val[ vm->index ][ 0 ]; + st[ 1 ] = 1.f - vm->vmap->val[ vm->index ][ 1 ]; + } + else if (vm->vmap->type == LWID_('R','G','B','A')) + { + /* set rgba */ + color[ 0 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 0 ] * surface->color.rgb[ 0 ] * surface->diffuse.val * 0xFF); + color[ 1 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 1 ] * surface->color.rgb[ 1 ] * surface->diffuse.val * 0xFF); + color[ 2 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 2 ] * surface->color.rgb[ 2 ] * surface->diffuse.val * 0xFF); + color[ 3 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 3 ] * 0xFF); + } + } + + /* override with polygon data */ + for( k = 0, vm = v->vm; k < v->nvmaps; k++, vm++ ) + { + if (vm->vmap->type == LWID_('T','X','U','V')) + { + /* set st coords */ + st[ 0 ] = vm->vmap->val[ vm->index ][ 0 ]; + st[ 1 ] = 1.f - vm->vmap->val[ vm->index ][ 1 ]; + } + else if (vm->vmap->type == LWID_('R','G','B','A')) + { + /* set rgba */ + color[ 0 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 0 ] * surface->color.rgb[ 0 ] * surface->diffuse.val * 0xFF); + color[ 1 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 1 ] * surface->color.rgb[ 1 ] * surface->diffuse.val * 0xFF); + color[ 2 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 2 ] * surface->color.rgb[ 2 ] * surface->diffuse.val * 0xFF); + color[ 3 ] = (picoByte_t)(vm->vmap->val[ vm->index ][ 3 ] * 0xFF); + } + } + + /* find vertex in this surface and if we can't find it there create it */ + vertexCombinationHash = PicoFindVertexCombinationInHashTable( hashTable, xyz, normal, st, color ); + + if (vertexCombinationHash) + { + /* found an existing one */ + PicoSetSurfaceIndex( picoSurface, (i * 3 + j ), vertexCombinationHash->index ); + } + else + { + /* it is a new one */ + vertexCombinationHash = PicoAddVertexCombinationToHashTable( hashTable, xyz, normal, st, color, (picoIndex_t) numverts ); + + if (vertexCombinationHash == NULL) + { + _pico_printf( PICO_ERROR, "Unable to allocate hash bucket entry table" ); + PicoFreeVertexCombinationHashTable( hashTable ); + PicoFreeModel( picoModel ); + lwFreeObject( obj ); + return NULL; + } + + /* add the vertex to this surface */ + PicoSetSurfaceXYZ( picoSurface, numverts, xyz ); + + /* set dummy normal */ + PicoSetSurfaceNormal( picoSurface, numverts, normal ); + + /* set color */ + PicoSetSurfaceColor( picoSurface, 0, numverts, color ); + + /* set st coords */ + PicoSetSurfaceST( picoSurface, 0, numverts, st ); + + /* set index */ + PicoSetSurfaceIndex( picoSurface, (i * 3 + j ), (picoIndex_t) numverts ); + + numverts++; + } + } + } + + /* free the hashtable */ + PicoFreeVertexCombinationHashTable( hashTable ); + + /* get next surface */ + surface = surface->next; + } + +#ifdef DEBUG_PM_LWO + load_start = convert_finish = clock(); +#endif + + lwFreeObject( obj ); + +#ifdef DEBUG_PM_LWO + load_finish = clock(); + load_elapsed += (double)(load_finish - load_start) / CLOCKS_PER_SEC; + convert_elapsed = (double)(convert_finish - convert_start) / CLOCKS_PER_SEC; + _pico_printf( PICO_NORMAL, "Loaded model in in %-.2f second(s) (loading: %-.2fs converting: %-.2fs)\n", load_elapsed + convert_elapsed, load_elapsed, convert_elapsed ); +#endif + + /* return the new pico model */ + return picoModel; +} + +/* pico file format module definition */ +const picoModule_t picoModuleLWO = +{ + "1.0", /* module version string */ + "LightWave Object", /* module display name */ + "Arnout van Meer", /* author's name */ + "2003 Arnout van Meer, 2000 Ernie Wright", /* module copyright */ + { + "lwo", NULL, NULL, NULL /* default extensions to use */ + }, + _lwo_canload, /* validation routine */ + _lwo_load, /* load routine */ + NULL, /* save validation routine */ + NULL /* save routine */ +}; diff --git a/tools/urt/libs/picomodel/pm_md2.c b/tools/urt/libs/picomodel/pm_md2.c new file mode 100644 index 00000000..c91a873b --- /dev/null +++ b/tools/urt/libs/picomodel/pm_md2.c @@ -0,0 +1,667 @@ +/* ----------------------------------------------------------------------------- + +PicoModel Library + +Copyright (c) 2002, Randy Reddig & seaw0lf +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list +of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +Neither the names of the copyright holders nor the names of its contributors may +be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +----------------------------------------------------------------------------- */ + +/* +Nurail: Used pm_md3.c (Randy Reddig) as a template. +*/ + + +/* marker */ +#define PM_MD2_C + +/* dependencies */ +#include "picointernal.h" + + +/* md2 model format */ +#define MD2_MAGIC "IDP2" +#define MD2_VERSION 8 + +#define MD2_NUMVERTEXNORMALS 162 +#define MD2_MAX_SKINNAME 64 +#define MD2_MAX_TRIANGLES 4096 +#define MD2_MAX_VERTS 2048 +#define MD2_MAX_FRAMES 512 +#define MD2_MAX_MD2SKINS 32 +#define MD2_MAX_SKINNAME 64 + +#ifndef byte + #define byte unsigned char +#endif + +typedef struct index_LUT_s +{ + short Vert; + short ST; + struct index_LUT_s *next; + +} index_LUT_t; + +typedef struct index_DUP_LUT_s +{ + short ST; + short OldVert; + +} index_DUP_LUT_t; + +typedef struct +{ + short s; + short t; +} md2St_t; + +typedef struct +{ + short index_xyz[3]; + short index_st[3]; +} md2Triangle_t; + +typedef struct +{ + byte v[3]; // scaled byte to fit in frame mins/maxs + byte lightnormalindex; +} md2XyzNormal_t; + +typedef struct md2Frame_s +{ + float scale[3]; // multiply byte verts by this + float translate[3]; // then add this + char name[16]; // frame name from grabbing + md2XyzNormal_t verts[1]; // variable sized +} +md2Frame_t; + + +/* md2 model file md2 structure */ +typedef struct md2_s +{ + char magic[ 4 ]; + int version; + + int skinWidth; + int skinHeight; + int frameSize; + + int numSkins; + int numXYZ; + int numST; + int numTris; + int numGLCmds; + int numFrames; + + int ofsSkins; + int ofsST; + int ofsTris; + int ofsFrames; + int ofsGLCmds; + int ofsEnd; +} +md2_t; + +float md2_normals[ MD2_NUMVERTEXNORMALS ][ 3 ] = +{ + { -0.525731f, 0.000000f, 0.850651f }, + { -0.442863f, 0.238856f, 0.864188f }, + { -0.295242f, 0.000000f, 0.955423f }, + { -0.309017f, 0.500000f, 0.809017f }, + { -0.162460f, 0.262866f, 0.951056f }, + { 0.000000f, 0.000000f, 1.000000f }, + { 0.000000f, 0.850651f, 0.525731f }, + { -0.147621f, 0.716567f, 0.681718f }, + { 0.147621f, 0.716567f, 0.681718f }, + { 0.000000f, 0.525731f, 0.850651f }, + { 0.309017f, 0.500000f, 0.809017f }, + { 0.525731f, 0.000000f, 0.850651f }, + { 0.295242f, 0.000000f, 0.955423f }, + { 0.442863f, 0.238856f, 0.864188f }, + { 0.162460f, 0.262866f, 0.951056f }, + { -0.681718f, 0.147621f, 0.716567f }, + { -0.809017f, 0.309017f, 0.500000f }, + { -0.587785f, 0.425325f, 0.688191f }, + { -0.850651f, 0.525731f, 0.000000f }, + { -0.864188f, 0.442863f, 0.238856f }, + { -0.716567f, 0.681718f, 0.147621f }, + { -0.688191f, 0.587785f, 0.425325f }, + { -0.500000f, 0.809017f, 0.309017f }, + { -0.238856f, 0.864188f, 0.442863f }, + { -0.425325f, 0.688191f, 0.587785f }, + { -0.716567f, 0.681718f, -0.147621f }, + { -0.500000f, 0.809017f, -0.309017f }, + { -0.525731f, 0.850651f, 0.000000f }, + { 0.000000f, 0.850651f, -0.525731f }, + { -0.238856f, 0.864188f, -0.442863f }, + { 0.000000f, 0.955423f, -0.295242f }, + { -0.262866f, 0.951056f, -0.162460f }, + { 0.000000f, 1.000000f, 0.000000f }, + { 0.000000f, 0.955423f, 0.295242f }, + { -0.262866f, 0.951056f, 0.162460f }, + { 0.238856f, 0.864188f, 0.442863f }, + { 0.262866f, 0.951056f, 0.162460f }, + { 0.500000f, 0.809017f, 0.309017f }, + { 0.238856f, 0.864188f, -0.442863f }, + { 0.262866f, 0.951056f, -0.162460f }, + { 0.500000f, 0.809017f, -0.309017f }, + { 0.850651f, 0.525731f, 0.000000f }, + { 0.716567f, 0.681718f, 0.147621f }, + { 0.716567f, 0.681718f, -0.147621f }, + { 0.525731f, 0.850651f, 0.000000f }, + { 0.425325f, 0.688191f, 0.587785f }, + { 0.864188f, 0.442863f, 0.238856f }, + { 0.688191f, 0.587785f, 0.425325f }, + { 0.809017f, 0.309017f, 0.500000f }, + { 0.681718f, 0.147621f, 0.716567f }, + { 0.587785f, 0.425325f, 0.688191f }, + { 0.955423f, 0.295242f, 0.000000f }, + { 1.000000f, 0.000000f, 0.000000f }, + { 0.951056f, 0.162460f, 0.262866f }, + { 0.850651f, -0.525731f, 0.000000f }, + { 0.955423f, -0.295242f, 0.000000f }, + { 0.864188f, -0.442863f, 0.238856f }, + { 0.951056f, -0.162460f, 0.262866f }, + { 0.809017f, -0.309017f, 0.500000f }, + { 0.681718f, -0.147621f, 0.716567f }, + { 0.850651f, 0.000000f, 0.525731f }, + { 0.864188f, 0.442863f, -0.238856f }, + { 0.809017f, 0.309017f, -0.500000f }, + { 0.951056f, 0.162460f, -0.262866f }, + { 0.525731f, 0.000000f, -0.850651f }, + { 0.681718f, 0.147621f, -0.716567f }, + { 0.681718f, -0.147621f, -0.716567f }, + { 0.850651f, 0.000000f, -0.525731f }, + { 0.809017f, -0.309017f, -0.500000f }, + { 0.864188f, -0.442863f, -0.238856f }, + { 0.951056f, -0.162460f, -0.262866f }, + { 0.147621f, 0.716567f, -0.681718f }, + { 0.309017f, 0.500000f, -0.809017f }, + { 0.425325f, 0.688191f, -0.587785f }, + { 0.442863f, 0.238856f, -0.864188f }, + { 0.587785f, 0.425325f, -0.688191f }, + { 0.688191f, 0.587785f, -0.425325f }, + { -0.147621f, 0.716567f, -0.681718f }, + { -0.309017f, 0.500000f, -0.809017f }, + { 0.000000f, 0.525731f, -0.850651f }, + { -0.525731f, 0.000000f, -0.850651f }, + { -0.442863f, 0.238856f, -0.864188f }, + { -0.295242f, 0.000000f, -0.955423f }, + { -0.162460f, 0.262866f, -0.951056f }, + { 0.000000f, 0.000000f, -1.000000f }, + { 0.295242f, 0.000000f, -0.955423f }, + { 0.162460f, 0.262866f, -0.951056f }, + { -0.442863f, -0.238856f, -0.864188f }, + { -0.309017f, -0.500000f, -0.809017f }, + { -0.162460f, -0.262866f, -0.951056f }, + { 0.000000f, -0.850651f, -0.525731f }, + { -0.147621f, -0.716567f, -0.681718f }, + { 0.147621f, -0.716567f, -0.681718f }, + { 0.000000f, -0.525731f, -0.850651f }, + { 0.309017f, -0.500000f, -0.809017f }, + { 0.442863f, -0.238856f, -0.864188f }, + { 0.162460f, -0.262866f, -0.951056f }, + { 0.238856f, -0.864188f, -0.442863f }, + { 0.500000f, -0.809017f, -0.309017f }, + { 0.425325f, -0.688191f, -0.587785f }, + { 0.716567f, -0.681718f, -0.147621f }, + { 0.688191f, -0.587785f, -0.425325f }, + { 0.587785f, -0.425325f, -0.688191f }, + { 0.000000f, -0.955423f, -0.295242f }, + { 0.000000f, -1.000000f, 0.000000f }, + { 0.262866f, -0.951056f, -0.162460f }, + { 0.000000f, -0.850651f, 0.525731f }, + { 0.000000f, -0.955423f, 0.295242f }, + { 0.238856f, -0.864188f, 0.442863f }, + { 0.262866f, -0.951056f, 0.162460f }, + { 0.500000f, -0.809017f, 0.309017f }, + { 0.716567f, -0.681718f, 0.147621f }, + { 0.525731f, -0.850651f, 0.000000f }, + { -0.238856f, -0.864188f, -0.442863f }, + { -0.500000f, -0.809017f, -0.309017f }, + { -0.262866f, -0.951056f, -0.162460f }, + { -0.850651f, -0.525731f, 0.000000f }, + { -0.716567f, -0.681718f, -0.147621f }, + { -0.716567f, -0.681718f, 0.147621f }, + { -0.525731f, -0.850651f, 0.000000f }, + { -0.500000f, -0.809017f, 0.309017f }, + { -0.238856f, -0.864188f, 0.442863f }, + { -0.262866f, -0.951056f, 0.162460f }, + { -0.864188f, -0.442863f, 0.238856f }, + { -0.809017f, -0.309017f, 0.500000f }, + { -0.688191f, -0.587785f, 0.425325f }, + { -0.681718f, -0.147621f, 0.716567f }, + { -0.442863f, -0.238856f, 0.864188f }, + { -0.587785f, -0.425325f, 0.688191f }, + { -0.309017f, -0.500000f, 0.809017f }, + { -0.147621f, -0.716567f, 0.681718f }, + { -0.425325f, -0.688191f, 0.587785f }, + { -0.162460f, -0.262866f, 0.951056f }, + { 0.442863f, -0.238856f, 0.864188f }, + { 0.162460f, -0.262866f, 0.951056f }, + { 0.309017f, -0.500000f, 0.809017f }, + { 0.147621f, -0.716567f, 0.681718f }, + { 0.000000f, -0.525731f, 0.850651f }, + { 0.425325f, -0.688191f, 0.587785f }, + { 0.587785f, -0.425325f, 0.688191f }, + { 0.688191f, -0.587785f, 0.425325f }, + { -0.955423f, 0.295242f, 0.000000f }, + { -0.951056f, 0.162460f, 0.262866f }, + { -1.000000f, 0.000000f, 0.000000f }, + { -0.850651f, 0.000000f, 0.525731f }, + { -0.955423f, -0.295242f, 0.000000f }, + { -0.951056f, -0.162460f, 0.262866f }, + { -0.864188f, 0.442863f, -0.238856f }, + { -0.951056f, 0.162460f, -0.262866f }, + { -0.809017f, 0.309017f, -0.500000f }, + { -0.864188f, -0.442863f, -0.238856f }, + { -0.951056f, -0.162460f, -0.262866f }, + { -0.809017f, -0.309017f, -0.500000f }, + { -0.681718f, 0.147621f, -0.716567f }, + { -0.681718f, -0.147621f, -0.716567f }, + { -0.850651f, 0.000000f, -0.525731f }, + { -0.688191f, 0.587785f, -0.425325f }, + { -0.587785f, 0.425325f, -0.688191f }, + { -0.425325f, 0.688191f, -0.587785f }, + { -0.425325f, -0.688191f, -0.587785f }, + { -0.587785f, -0.425325f, -0.688191f }, + { -0.688191f, -0.587785f, -0.425325f }, +}; + + +// _md2_canload() + +static int _md2_canload( PM_PARAMS_CANLOAD ) +{ + md2_t *md2; + + /* to keep the compiler happy */ + *fileName = *fileName; + + /* sanity check */ + if( bufSize < ( sizeof( *md2 ) * 2) ) + return PICO_PMV_ERROR_SIZE; + + /* set as md2 */ + md2 = (md2_t*) buffer; + + /* check md2 magic */ + if( *((int*) md2->magic) != *((int*) MD2_MAGIC) ) + return PICO_PMV_ERROR_IDENT; + + /* check md2 version */ + if( _pico_little_long( md2->version ) != MD2_VERSION ) + return PICO_PMV_ERROR_VERSION; + + /* file seems to be a valid md2 */ + return PICO_PMV_OK; +} + + + +// _md2_load() loads a quake2 md2 model file. + + +static picoModel_t *_md2_load( PM_PARAMS_LOAD ) +{ + int i, j, dups, dup_index; + short tot_numVerts; + index_LUT_t *p_index_LUT, *p_index_LUT2, *p_index_LUT3; + index_DUP_LUT_t *p_index_LUT_DUPS; + md2Triangle_t *p_md2Triangle; + + char skinname[ MD2_MAX_SKINNAME ]; + md2_t *md2; + md2St_t *texCoord; + md2Frame_t *frame; + md2Triangle_t *triangle; + md2XyzNormal_t *vertex; + + picoByte_t *bb; + picoModel_t *picoModel; + picoSurface_t *picoSurface; + picoShader_t *picoShader; + picoVec3_t xyz, normal; + picoVec2_t st; + picoColor_t color; + + + /* set as md2 */ + bb = (picoByte_t*) buffer; + md2 = (md2_t*) buffer; + + /* check ident and version */ + if( *((int*) md2->magic) != *((int*) MD2_MAGIC) || _pico_little_long( md2->version ) != MD2_VERSION ) + { + /* not an md2 file (todo: set error) */ + _pico_printf( PICO_ERROR, "%s is not an MD2 File!", fileName ); + return NULL; + } + + // swap md2 + md2->version = _pico_little_long( md2->version ); + + md2->skinWidth = _pico_little_long( md2->skinWidth ); + md2->skinHeight = _pico_little_long( md2->skinHeight ); + md2->frameSize = _pico_little_long( md2->frameSize ); + + md2->numSkins = _pico_little_long( md2->numSkins ); + md2->numXYZ = _pico_little_long( md2->numXYZ ); + md2->numST = _pico_little_long( md2->numST ); + md2->numTris = _pico_little_long( md2->numTris ); + md2->numGLCmds = _pico_little_long( md2->numGLCmds ); + md2->numFrames = _pico_little_long( md2->numFrames ); + + md2->ofsSkins = _pico_little_long( md2->ofsSkins ); + md2->ofsST = _pico_little_long( md2->ofsST ); + md2->ofsTris = _pico_little_long( md2->ofsTris ); + md2->ofsFrames = _pico_little_long( md2->ofsFrames ); + md2->ofsGLCmds = _pico_little_long( md2->ofsGLCmds ); + md2->ofsEnd = _pico_little_long( md2->ofsEnd ); + + // do frame check + if( md2->numFrames < 1 ) + { + _pico_printf( PICO_ERROR, "%s has 0 frames!", fileName ); + return NULL; + } + + if( frameNum < 0 || frameNum >= md2->numFrames ) + { + _pico_printf( PICO_ERROR, "Invalid or out-of-range MD2 frame specified" ); + return NULL; + } + + // Setup Frame + frame = (md2Frame_t *) (bb + md2->ofsFrames + (sizeof(md2Frame_t) * frameNum)); + + // swap frame scale and translation + for( i = 0; i < 3; i++ ) + { + frame->scale[ i ] = _pico_little_float( frame->scale[ i ] ); + frame->translate[ i ] = _pico_little_float( frame->translate[ i ] ); + } + + // swap triangles + triangle = (md2Triangle_t *) ((picoByte_t *) (bb + md2->ofsTris) ); + for( i = 0; i < md2->numTris; i++, triangle++ ) + { + for( j = 0; j < 3; j++ ) + { + triangle->index_xyz[ j ] = _pico_little_short( triangle->index_xyz[ j ] ); + triangle->index_st[ j ] = _pico_little_short( triangle->index_st[ j ] ); + } + } + + // swap st coords + texCoord = (md2St_t*) ((picoByte_t *) (bb + md2->ofsST) ); + for( i = 0; i < md2->numST; i++, texCoord++ ) + { + texCoord->s = _pico_little_short( texCoord->s ); + texCoord->t = _pico_little_short( texCoord->t ); + } + + // set Skin Name + strncpy(skinname, (bb + md2->ofsSkins), MD2_MAX_SKINNAME ); + + // Print out md2 values + _pico_printf(PICO_VERBOSE,"Skins: %d Verts: %d STs: %d Triangles: %d Frames: %d\nSkin Name \"%s\"\n", md2->numSkins, md2->numXYZ, md2->numST, md2->numTris, md2->numFrames, &skinname ); + + // detox Skin name + _pico_setfext( skinname, "" ); + _pico_unixify( skinname ); + + /* create new pico model */ + picoModel = PicoNewModel(); + if( picoModel == NULL ) + { + _pico_printf( PICO_ERROR, "Unable to allocate a new model" ); + return NULL; + } + + /* do model setup */ + PicoSetModelFrameNum( picoModel, frameNum ); + PicoSetModelNumFrames( picoModel, md2->numFrames ); /* sea */ + PicoSetModelName( picoModel, fileName ); + PicoSetModelFileName( picoModel, fileName ); + + // allocate new pico surface + picoSurface = PicoNewSurface( picoModel ); + if( picoSurface == NULL ) + { + _pico_printf( PICO_ERROR, "Unable to allocate a new model surface" ); + PicoFreeModel( picoModel ); + return NULL; + } + + + PicoSetSurfaceType( picoSurface, PICO_TRIANGLES ); + PicoSetSurfaceName( picoSurface, frame->name ); + picoShader = PicoNewShader( picoModel ); + if( picoShader == NULL ) + { + _pico_printf( PICO_ERROR, "Unable to allocate a new model shader" ); + PicoFreeModel( picoModel ); + return NULL; + } + + PicoSetShaderName( picoShader, skinname ); + + // associate current surface with newly created shader + PicoSetSurfaceShader( picoSurface, picoShader ); + + // Init LUT for Verts + p_index_LUT = (index_LUT_t *)_pico_alloc(sizeof(index_LUT_t) * md2->numXYZ); + for(i=0; inumXYZ; i++) + { + p_index_LUT[i].Vert = -1; + p_index_LUT[i].ST = -1; + p_index_LUT[i].next = NULL; + } + + // Fill in Look Up Table, and allocate/fill Linked List from vert array as needed for dup STs per Vert. + tot_numVerts = md2->numXYZ; + dups = 0; + for(i=0; inumTris; i++) + { + p_md2Triangle = (md2Triangle_t *) ( bb + md2->ofsTris + (sizeof(md2Triangle_t)*i)); + for(j=0; j<3; j++) + { + if (p_index_LUT[p_md2Triangle->index_xyz[j]].ST == -1) // No Main Entry + p_index_LUT[p_md2Triangle->index_xyz[j]].ST = p_md2Triangle->index_st[j]; + + else if (p_md2Triangle->index_st[j] == p_index_LUT[p_md2Triangle->index_xyz[j]].ST ) // Equal to Main Entry + continue; + + else if ( (p_index_LUT[p_md2Triangle->index_xyz[j]].next == NULL) ) // Not equal to Main entry, and no LL entry + { // Add first entry of LL from Main + p_index_LUT2 = (index_LUT_t *)_pico_alloc(sizeof(index_LUT_t)); + if (p_index_LUT2 == NULL) + _pico_printf( PICO_ERROR," Couldn't allocate memory!\n"); + p_index_LUT[p_md2Triangle->index_xyz[j]].next = (index_LUT_t *)p_index_LUT2; + p_index_LUT2->Vert = dups; + p_index_LUT2->ST = p_md2Triangle->index_st[j]; + p_index_LUT2->next = NULL; + p_md2Triangle->index_xyz[j] = dups + md2->numXYZ; // Make change in Tri hunk + dups++; + } + else // Try to find in LL from Main Entry + { + p_index_LUT3 = p_index_LUT2 = p_index_LUT[p_md2Triangle->index_xyz[j]].next; + while ( (p_index_LUT2 != NULL) && (p_md2Triangle->index_xyz[j] != p_index_LUT2->Vert) ) // Walk down LL + { + p_index_LUT3 = p_index_LUT2; + p_index_LUT2 = p_index_LUT2->next; + } + p_index_LUT2 = p_index_LUT3; + + if ( p_md2Triangle->index_st[j] == p_index_LUT2->ST ) // Found it + { + p_md2Triangle->index_xyz[j] = p_index_LUT2->Vert + md2->numXYZ; // Make change in Tri hunk + continue; + } + + if ( p_index_LUT2->next == NULL) // Didn't find it. Add entry to LL. + { + // Add the Entry + p_index_LUT3 = (index_LUT_t *)_pico_alloc(sizeof(index_LUT_t)); + if (p_index_LUT3 == NULL) + _pico_printf( PICO_ERROR," Couldn't allocate memory!\n"); + p_index_LUT2->next = (index_LUT_t *)p_index_LUT3; + p_index_LUT3->Vert = p_md2Triangle->index_xyz[j]; + p_index_LUT3->ST = p_md2Triangle->index_st[j]; + p_index_LUT3->next = NULL; + p_md2Triangle->index_xyz[j] = dups + md2->numXYZ; // Make change in Tri hunk + dups++; + } + } + } + } + + // malloc and build array for Dup STs + p_index_LUT_DUPS = (index_DUP_LUT_t *)_pico_alloc(sizeof(index_DUP_LUT_t) * dups); + if (p_index_LUT_DUPS == NULL) + _pico_printf( PICO_ERROR," Couldn't allocate memory!\n"); + + dup_index = 0; + for(i=0; inumXYZ; i++) + { + p_index_LUT2 = p_index_LUT[i].next; + while (p_index_LUT2 != NULL) + { + p_index_LUT_DUPS[p_index_LUT2->Vert].OldVert = i; + p_index_LUT_DUPS[p_index_LUT2->Vert].ST = p_index_LUT2->ST; + dup_index++; + p_index_LUT2 = p_index_LUT2->next; + } + } + + // Build Picomodel + triangle = (md2Triangle_t *) ((picoByte_t *) (bb + md2->ofsTris) ); + texCoord = (md2St_t*) ((picoByte_t *) (bb + md2->ofsST) ); + vertex = (md2XyzNormal_t*) ((picoByte_t*) (frame->verts) ); + for( j = 0; j < md2->numTris; j++, triangle++ ) + { + PicoSetSurfaceIndex( picoSurface, j*3 , triangle->index_xyz[0] ); + PicoSetSurfaceIndex( picoSurface, j*3+1 , triangle->index_xyz[1] ); + PicoSetSurfaceIndex( picoSurface, j*3+2 , triangle->index_xyz[2] ); + } + + for(i=0; i< md2->numXYZ; i++, vertex++) + { + /* set vertex origin */ + xyz[ 0 ] = vertex->v[0] * frame->scale[0] + frame->translate[0]; + xyz[ 1 ] = vertex->v[1] * frame->scale[1] + frame->translate[1]; + xyz[ 2 ] = vertex->v[2] * frame->scale[2] + frame->translate[2]; + PicoSetSurfaceXYZ( picoSurface, i , xyz ); + + /* set normal */ + normal[ 0 ] = md2_normals[vertex->lightnormalindex][0]; + normal[ 1 ] = md2_normals[vertex->lightnormalindex][1]; + normal[ 2 ] = md2_normals[vertex->lightnormalindex][2]; + PicoSetSurfaceNormal( picoSurface, i , normal ); + + /* set st coords */ + st[ 0 ] = ((texCoord[p_index_LUT[i].ST].s) / ((float)md2->skinWidth)); + st[ 1 ] = (texCoord[p_index_LUT[i].ST].t / ((float)md2->skinHeight)); + PicoSetSurfaceST( picoSurface, 0, i , st ); + } + + if (dups) + { + for(i=0; iverts[j].v[0] * frame->scale[0] + frame->translate[0]; + xyz[ 1 ] = frame->verts[j].v[1] * frame->scale[1] + frame->translate[1]; + xyz[ 2 ] = frame->verts[j].v[2] * frame->scale[2] + frame->translate[2]; + PicoSetSurfaceXYZ( picoSurface, i + md2->numXYZ , xyz ); + + /* set normal */ + normal[ 0 ] = md2_normals[frame->verts[j].lightnormalindex][0]; + normal[ 1 ] = md2_normals[frame->verts[j].lightnormalindex][1]; + normal[ 2 ] = md2_normals[frame->verts[j].lightnormalindex][2]; + PicoSetSurfaceNormal( picoSurface, i + md2->numXYZ , normal ); + + /* set st coords */ + st[ 0 ] = ((texCoord[p_index_LUT_DUPS[i].ST].s) / ((float)md2->skinWidth)); + st[ 1 ] = (texCoord[p_index_LUT_DUPS[i].ST].t / ((float)md2->skinHeight)); + PicoSetSurfaceST( picoSurface, 0, i + md2->numXYZ , st ); + } + } + + /* set color */ + PicoSetSurfaceColor( picoSurface, 0, 0, color ); + + // Free up malloc'ed LL entries + for(i=0; inumXYZ; i++) + { + if(p_index_LUT[i].next != NULL) + { + p_index_LUT2 = p_index_LUT[i].next; + do { + p_index_LUT3 = p_index_LUT2->next; + _pico_free(p_index_LUT2); + p_index_LUT2 = p_index_LUT3; + dups--; + } while (p_index_LUT2 != NULL); + } + } + + if (dups) + _pico_printf(PICO_WARNING, " Not all LL mallocs freed\n"); + + // Free malloc'ed LUTs + _pico_free(p_index_LUT); + _pico_free(p_index_LUT_DUPS); + + /* return the new pico model */ + return picoModel; + +} + + + +/* pico file format module definition */ +const picoModule_t picoModuleMD2 = +{ + "0.875", /* module version string */ + "Quake 2 MD2", /* module display name */ + "Nurail", /* author's name */ + "2003 Nurail", /* module copyright */ + { + "md2", NULL, NULL, NULL /* default extensions to use */ + }, + _md2_canload, /* validation routine */ + _md2_load, /* load routine */ + NULL, /* save validation routine */ + NULL /* save routine */ +}; diff --git a/tools/urt/libs/picomodel/pm_md3.c b/tools/urt/libs/picomodel/pm_md3.c new file mode 100644 index 00000000..6d87469d --- /dev/null +++ b/tools/urt/libs/picomodel/pm_md3.c @@ -0,0 +1,425 @@ +/* ----------------------------------------------------------------------------- + +PicoModel Library + +Copyright (c) 2002, Randy Reddig & seaw0lf +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list +of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +Neither the names of the copyright holders nor the names of its contributors may +be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +----------------------------------------------------------------------------- */ + + + +/* marker */ +#define PM_MD3_C + + + +/* dependencies */ +#include "picointernal.h" + + + +/* md3 model format */ +#define MD3_MAGIC "IDP3" +#define MD3_VERSION 15 + +/* md3 vertex scale */ +#define MD3_SCALE (1.0f / 64.0f) + +/* md3 model frame information */ +typedef struct md3Frame_s +{ + float bounds[ 2 ][ 3 ]; + float localOrigin[ 3 ]; + float radius; + char creator[ 16 ]; +} +md3Frame_t; + +/* md3 model tag information */ +typedef struct md3Tag_s +{ + char name[ 64 ]; + float origin[ 3 ]; + float axis[ 3 ][ 3 ]; +} +md3Tag_t; + +/* md3 surface md3 (one object mesh) */ +typedef struct md3Surface_s +{ + char magic[ 4 ]; + char name[ 64 ]; /* polyset name */ + int flags; + int numFrames; /* all model surfaces should have the same */ + int numShaders; /* all model surfaces should have the same */ + int numVerts; + int numTriangles; + int ofsTriangles; + int ofsShaders; /* offset from start of md3Surface_t */ + int ofsSt; /* texture coords are common for all frames */ + int ofsVertexes; /* numVerts * numFrames */ + int ofsEnd; /* next surface follows */ +} +md3Surface_t; + +typedef struct md3Shader_s +{ + char name[ 64 ]; + int shaderIndex; /* for ingame use */ +} +md3Shader_t; + +typedef struct md3Triangle_s +{ + int indexes[ 3 ]; +} +md3Triangle_t; + +typedef struct md3TexCoord_s +{ + float st[ 2 ]; +} +md3TexCoord_t; + +typedef struct md3Vertex_s +{ + short xyz[ 3 ]; + short normal; +} +md3Vertex_t; + + +/* md3 model file md3 structure */ +typedef struct md3_s +{ + char magic[ 4 ]; /* MD3_MAGIC */ + int version; + char name[ 64 ]; /* model name */ + int flags; + int numFrames; + int numTags; + int numSurfaces; + int numSkins; /* number of skins for the mesh */ + int ofsFrames; /* offset for first frame */ + int ofsTags; /* numFrames * numTags */ + int ofsSurfaces; /* first surface, others follow */ + int ofsEnd; /* end of file */ +} +md3_t; + + + + +/* +_md3_canload() +validates a quake3 arena md3 model file. btw, i use the +preceding underscore cause it's a static func referenced +by one structure only. +*/ + +static int _md3_canload( PM_PARAMS_CANLOAD ) +{ + md3_t *md3; + + + /* to keep the compiler happy */ + *fileName = *fileName; + + /* sanity check */ + if( bufSize < ( sizeof( *md3 ) * 2) ) + return PICO_PMV_ERROR_SIZE; + + /* set as md3 */ + md3 = (md3_t*) buffer; + + /* check md3 magic */ + if( *((int*) md3->magic) != *((int*) MD3_MAGIC) ) + return PICO_PMV_ERROR_IDENT; + + /* check md3 version */ + if( _pico_little_long( md3->version ) != MD3_VERSION ) + return PICO_PMV_ERROR_VERSION; + + /* file seems to be a valid md3 */ + return PICO_PMV_OK; +} + + + +/* +_md3_load() +loads a quake3 arena md3 model file. +*/ + +static picoModel_t *_md3_load( PM_PARAMS_LOAD ) +{ + int i, j; + picoByte_t *bb; + md3_t *md3; + md3Surface_t *surface; + md3Shader_t *shader; + md3TexCoord_t *texCoord; + md3Frame_t *frame; + md3Triangle_t *triangle; + md3Vertex_t *vertex; + double lat, lng; + + picoModel_t *picoModel; + picoSurface_t *picoSurface; + picoShader_t *picoShader; + picoVec3_t xyz, normal; + picoVec2_t st; + picoColor_t color; + + + /* ------------------------------------------------- + md3 loading + ------------------------------------------------- */ + + + /* set as md3 */ + bb = (picoByte_t*) buffer; + md3 = (md3_t*) buffer; + + /* check ident and version */ + if( *((int*) md3->magic) != *((int*) MD3_MAGIC) || _pico_little_long( md3->version ) != MD3_VERSION ) + { + /* not an md3 file (todo: set error) */ + return NULL; + } + + /* swap md3; sea: swaps fixed */ + md3->version = _pico_little_long( md3->version ); + md3->numFrames = _pico_little_long( md3->numFrames ); + md3->numTags = _pico_little_long( md3->numTags ); + md3->numSurfaces = _pico_little_long( md3->numSurfaces ); + md3->numSkins = _pico_little_long( md3->numSkins ); + md3->ofsFrames = _pico_little_long( md3->ofsFrames ); + md3->ofsTags = _pico_little_long( md3->ofsTags ); + md3->ofsSurfaces = _pico_little_long( md3->ofsSurfaces ); + md3->ofsEnd = _pico_little_long( md3->ofsEnd ); + + /* do frame check */ + if( md3->numFrames < 1 ) + { + _pico_printf( PICO_ERROR, "MD3 with 0 frames" ); + return NULL; + } + + if( frameNum < 0 || frameNum >= md3->numFrames ) + { + _pico_printf( PICO_ERROR, "Invalid or out-of-range MD3 frame specified" ); + return NULL; + } + + /* swap frames */ + frame = (md3Frame_t*) (bb + md3->ofsFrames ); + for( i = 0; i < md3->numFrames; i++, frame++ ) + { + frame->radius = _pico_little_float( frame->radius ); + for( j = 0; j < 3; j++ ) + { + frame->bounds[ 0 ][ j ] = _pico_little_float( frame->bounds[ 0 ][ j ] ); + frame->bounds[ 1 ][ j ] = _pico_little_float( frame->bounds[ 1 ][ j ] ); + frame->localOrigin[ j ] = _pico_little_float( frame->localOrigin[ j ] ); + } + } + + /* swap surfaces */ + surface = (md3Surface_t*) (bb + md3->ofsSurfaces); + for( i = 0; i < md3->numSurfaces; i++ ) + { + /* swap surface md3; sea: swaps fixed */ + surface->flags = _pico_little_long( surface->flags ); + surface->numFrames = _pico_little_long( surface->numFrames ); + surface->numShaders = _pico_little_long( surface->numShaders ); + surface->numTriangles = _pico_little_long( surface->numTriangles ); + surface->ofsTriangles = _pico_little_long( surface->ofsTriangles ); + surface->numVerts = _pico_little_long( surface->numVerts ); + surface->ofsShaders = _pico_little_long( surface->ofsShaders ); + surface->ofsSt = _pico_little_long( surface->ofsSt ); + surface->ofsVertexes = _pico_little_long( surface->ofsVertexes ); + surface->ofsEnd = _pico_little_long( surface->ofsEnd ); + + /* swap triangles */ + triangle = (md3Triangle_t*) ((picoByte_t*) surface + surface->ofsTriangles); + for( j = 0; j < surface->numTriangles; j++, triangle++ ) + { + /* sea: swaps fixed */ + triangle->indexes[ 0 ] = _pico_little_long( triangle->indexes[ 0 ] ); + triangle->indexes[ 1 ] = _pico_little_long( triangle->indexes[ 1 ] ); + triangle->indexes[ 2 ] = _pico_little_long( triangle->indexes[ 2 ] ); + } + + /* swap st coords */ + texCoord = (md3TexCoord_t*) ((picoByte_t*) surface + surface->ofsSt); + for( j = 0; j < surface->numVerts; j++, texCoord++ ) + { + texCoord->st[ 0 ] = _pico_little_float( texCoord->st[ 0 ] ); + texCoord->st[ 1 ] = _pico_little_float( texCoord->st[ 1 ] ); + } + + /* swap xyz/normals */ + vertex = (md3Vertex_t*) ((picoByte_t*) surface + surface->ofsVertexes); + for( j = 0; j < (surface->numVerts * surface->numFrames); j++, vertex++) + { + vertex->xyz[ 0 ] = _pico_little_short( vertex->xyz[ 0 ] ); + vertex->xyz[ 1 ] = _pico_little_short( vertex->xyz[ 1 ] ); + vertex->xyz[ 2 ] = _pico_little_short( vertex->xyz[ 2 ] ); + vertex->normal = _pico_little_short( vertex->normal ); + } + + /* get next surface */ + surface = (md3Surface_t*) ((picoByte_t*) surface + surface->ofsEnd); + } + + /* ------------------------------------------------- + pico model creation + ------------------------------------------------- */ + + /* create new pico model */ + picoModel = PicoNewModel(); + if( picoModel == NULL ) + { + _pico_printf( PICO_ERROR, "Unable to allocate a new model" ); + return NULL; + } + + /* do model setup */ + PicoSetModelFrameNum( picoModel, frameNum ); + PicoSetModelNumFrames( picoModel, md3->numFrames ); /* sea */ + PicoSetModelName( picoModel, fileName ); + PicoSetModelFileName( picoModel, fileName ); + + /* md3 surfaces become picomodel surfaces */ + surface = (md3Surface_t*) (bb + md3->ofsSurfaces); + + /* run through md3 surfaces */ + for( i = 0; i < md3->numSurfaces; i++ ) + { + /* allocate new pico surface */ + picoSurface = PicoNewSurface( picoModel ); + if( picoSurface == NULL ) + { + _pico_printf( PICO_ERROR, "Unable to allocate a new model surface" ); + PicoFreeModel( picoModel ); /* sea */ + return NULL; + } + + /* md3 model surfaces are all triangle meshes */ + PicoSetSurfaceType( picoSurface, PICO_TRIANGLES ); + + /* set surface name */ + PicoSetSurfaceName( picoSurface, surface->name ); + + /* create new pico shader -sea */ + picoShader = PicoNewShader( picoModel ); + if( picoShader == NULL ) + { + _pico_printf( PICO_ERROR, "Unable to allocate a new model shader" ); + PicoFreeModel( picoModel ); + return NULL; + } + + /* detox and set shader name */ + shader = (md3Shader_t*) ((picoByte_t*) surface + surface->ofsShaders); + _pico_setfext( shader->name, "" ); + _pico_unixify( shader->name ); + PicoSetShaderName( picoShader, shader->name ); + + /* associate current surface with newly created shader */ + PicoSetSurfaceShader( picoSurface, picoShader ); + + /* copy indexes */ + triangle = (md3Triangle_t *) ((picoByte_t*) surface + surface->ofsTriangles); + + for( j = 0; j < surface->numTriangles; j++, triangle++ ) + { + PicoSetSurfaceIndex( picoSurface, (j * 3 + 0), (picoIndex_t) triangle->indexes[ 0 ] ); + PicoSetSurfaceIndex( picoSurface, (j * 3 + 1), (picoIndex_t) triangle->indexes[ 1 ] ); + PicoSetSurfaceIndex( picoSurface, (j * 3 + 2), (picoIndex_t) triangle->indexes[ 2 ] ); + } + + /* copy vertexes */ + texCoord = (md3TexCoord_t*) ((picoByte_t *) surface + surface->ofsSt); + vertex = (md3Vertex_t*) ((picoByte_t*) surface + surface->ofsVertexes + surface->numVerts * frameNum * sizeof( md3Vertex_t ) ); + _pico_set_color( color, 255, 255, 255, 255 ); + + for( j = 0; j < surface->numVerts; j++, texCoord++, vertex++ ) + { + /* set vertex origin */ + xyz[ 0 ] = MD3_SCALE * vertex->xyz[ 0 ]; + xyz[ 1 ] = MD3_SCALE * vertex->xyz[ 1 ]; + xyz[ 2 ] = MD3_SCALE * vertex->xyz[ 2 ]; + PicoSetSurfaceXYZ( picoSurface, j, xyz ); + + /* decode lat/lng normal to 3 float normal */ + lat = (float) ((vertex->normal >> 8) & 0xff); + lng = (float) (vertex->normal & 0xff); + lat *= PICO_PI / 128; + lng *= PICO_PI / 128; + normal[ 0 ] = (picoVec_t) cos( lat ) * (picoVec_t) sin( lng ); + normal[ 1 ] = (picoVec_t) sin( lat ) * (picoVec_t) sin( lng ); + normal[ 2 ] = (picoVec_t) cos( lng ); + PicoSetSurfaceNormal( picoSurface, j, normal ); + + /* set st coords */ + st[ 0 ] = texCoord->st[ 0 ]; + st[ 1 ] = texCoord->st[ 1 ]; + PicoSetSurfaceST( picoSurface, 0, j, st ); + + /* set color */ + PicoSetSurfaceColor( picoSurface, 0, j, color ); + } + + /* get next surface */ + surface = (md3Surface_t*) ((picoByte_t*) surface + surface->ofsEnd); + } + + /* return the new pico model */ + return picoModel; +} + + + +/* pico file format module definition */ +const picoModule_t picoModuleMD3 = +{ + "1.3", /* module version string */ + "Quake 3 Arena", /* module display name */ + "Randy Reddig", /* author's name */ + "2002 Randy Reddig", /* module copyright */ + { + "md3", NULL, NULL, NULL /* default extensions to use */ + }, + _md3_canload, /* validation routine */ + _md3_load, /* load routine */ + NULL, /* save validation routine */ + NULL /* save routine */ +}; diff --git a/tools/urt/libs/picomodel/pm_mdc.c b/tools/urt/libs/picomodel/pm_mdc.c new file mode 100644 index 00000000..3036d112 --- /dev/null +++ b/tools/urt/libs/picomodel/pm_mdc.c @@ -0,0 +1,750 @@ +/* ----------------------------------------------------------------------------- + +PicoModel Library + +Copyright (c) 2002, Randy Reddig & seaw0lf +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list +of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +Neither the names of the copyright holders nor the names of its contributors may +be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +----------------------------------------------------------------------------- */ + + + +/* marker */ +#define PM_MDC_C + + + +/* dependencies */ +#include "picointernal.h" + +/* mdc model format */ +#define MDC_MAGIC "IDPC" +#define MDC_VERSION 2 + +/* mdc vertex scale */ +#define MDC_SCALE (1.0f / 64.0f) +#define MDC_MAX_OFS 127.0f +#define MDC_DIST_SCALE 0.05f + +/* mdc decoding normal table */ +double mdcNormals[ 256 ][ 3 ] = +{ + { 1.000000, 0.000000, 0.000000 }, + { 0.980785, 0.195090, 0.000000 }, + { 0.923880, 0.382683, 0.000000 }, + { 0.831470, 0.555570, 0.000000 }, + { 0.707107, 0.707107, 0.000000 }, + { 0.555570, 0.831470, 0.000000 }, + { 0.382683, 0.923880, 0.000000 }, + { 0.195090, 0.980785, 0.000000 }, + { -0.000000, 1.000000, 0.000000 }, + { -0.195090, 0.980785, 0.000000 }, + { -0.382683, 0.923880, 0.000000 }, + { -0.555570, 0.831470, 0.000000 }, + { -0.707107, 0.707107, 0.000000 }, + { -0.831470, 0.555570, 0.000000 }, + { -0.923880, 0.382683, 0.000000 }, + { -0.980785, 0.195090, 0.000000 }, + { -1.000000, -0.000000, 0.000000 }, + { -0.980785, -0.195090, 0.000000 }, + { -0.923880, -0.382683, 0.000000 }, + { -0.831470, -0.555570, 0.000000 }, + { -0.707107, -0.707107, 0.000000 }, + { -0.555570, -0.831469, 0.000000 }, + { -0.382684, -0.923880, 0.000000 }, + { -0.195090, -0.980785, 0.000000 }, + { 0.000000, -1.000000, 0.000000 }, + { 0.195090, -0.980785, 0.000000 }, + { 0.382684, -0.923879, 0.000000 }, + { 0.555570, -0.831470, 0.000000 }, + { 0.707107, -0.707107, 0.000000 }, + { 0.831470, -0.555570, 0.000000 }, + { 0.923880, -0.382683, 0.000000 }, + { 0.980785, -0.195090, 0.000000 }, + { 0.980785, 0.000000, -0.195090 }, + { 0.956195, 0.218245, -0.195090 }, + { 0.883657, 0.425547, -0.195090 }, + { 0.766809, 0.611510, -0.195090 }, + { 0.611510, 0.766809, -0.195090 }, + { 0.425547, 0.883657, -0.195090 }, + { 0.218245, 0.956195, -0.195090 }, + { -0.000000, 0.980785, -0.195090 }, + { -0.218245, 0.956195, -0.195090 }, + { -0.425547, 0.883657, -0.195090 }, + { -0.611510, 0.766809, -0.195090 }, + { -0.766809, 0.611510, -0.195090 }, + { -0.883657, 0.425547, -0.195090 }, + { -0.956195, 0.218245, -0.195090 }, + { -0.980785, -0.000000, -0.195090 }, + { -0.956195, -0.218245, -0.195090 }, + { -0.883657, -0.425547, -0.195090 }, + { -0.766809, -0.611510, -0.195090 }, + { -0.611510, -0.766809, -0.195090 }, + { -0.425547, -0.883657, -0.195090 }, + { -0.218245, -0.956195, -0.195090 }, + { 0.000000, -0.980785, -0.195090 }, + { 0.218245, -0.956195, -0.195090 }, + { 0.425547, -0.883657, -0.195090 }, + { 0.611510, -0.766809, -0.195090 }, + { 0.766809, -0.611510, -0.195090 }, + { 0.883657, -0.425547, -0.195090 }, + { 0.956195, -0.218245, -0.195090 }, + { 0.923880, 0.000000, -0.382683 }, + { 0.892399, 0.239118, -0.382683 }, + { 0.800103, 0.461940, -0.382683 }, + { 0.653281, 0.653281, -0.382683 }, + { 0.461940, 0.800103, -0.382683 }, + { 0.239118, 0.892399, -0.382683 }, + { -0.000000, 0.923880, -0.382683 }, + { -0.239118, 0.892399, -0.382683 }, + { -0.461940, 0.800103, -0.382683 }, + { -0.653281, 0.653281, -0.382683 }, + { -0.800103, 0.461940, -0.382683 }, + { -0.892399, 0.239118, -0.382683 }, + { -0.923880, -0.000000, -0.382683 }, + { -0.892399, -0.239118, -0.382683 }, + { -0.800103, -0.461940, -0.382683 }, + { -0.653282, -0.653281, -0.382683 }, + { -0.461940, -0.800103, -0.382683 }, + { -0.239118, -0.892399, -0.382683 }, + { 0.000000, -0.923880, -0.382683 }, + { 0.239118, -0.892399, -0.382683 }, + { 0.461940, -0.800103, -0.382683 }, + { 0.653281, -0.653282, -0.382683 }, + { 0.800103, -0.461940, -0.382683 }, + { 0.892399, -0.239117, -0.382683 }, + { 0.831470, 0.000000, -0.555570 }, + { 0.790775, 0.256938, -0.555570 }, + { 0.672673, 0.488726, -0.555570 }, + { 0.488726, 0.672673, -0.555570 }, + { 0.256938, 0.790775, -0.555570 }, + { -0.000000, 0.831470, -0.555570 }, + { -0.256938, 0.790775, -0.555570 }, + { -0.488726, 0.672673, -0.555570 }, + { -0.672673, 0.488726, -0.555570 }, + { -0.790775, 0.256938, -0.555570 }, + { -0.831470, -0.000000, -0.555570 }, + { -0.790775, -0.256938, -0.555570 }, + { -0.672673, -0.488726, -0.555570 }, + { -0.488725, -0.672673, -0.555570 }, + { -0.256938, -0.790775, -0.555570 }, + { 0.000000, -0.831470, -0.555570 }, + { 0.256938, -0.790775, -0.555570 }, + { 0.488725, -0.672673, -0.555570 }, + { 0.672673, -0.488726, -0.555570 }, + { 0.790775, -0.256938, -0.555570 }, + { 0.707107, 0.000000, -0.707107 }, + { 0.653281, 0.270598, -0.707107 }, + { 0.500000, 0.500000, -0.707107 }, + { 0.270598, 0.653281, -0.707107 }, + { -0.000000, 0.707107, -0.707107 }, + { -0.270598, 0.653282, -0.707107 }, + { -0.500000, 0.500000, -0.707107 }, + { -0.653281, 0.270598, -0.707107 }, + { -0.707107, -0.000000, -0.707107 }, + { -0.653281, -0.270598, -0.707107 }, + { -0.500000, -0.500000, -0.707107 }, + { -0.270598, -0.653281, -0.707107 }, + { 0.000000, -0.707107, -0.707107 }, + { 0.270598, -0.653281, -0.707107 }, + { 0.500000, -0.500000, -0.707107 }, + { 0.653282, -0.270598, -0.707107 }, + { 0.555570, 0.000000, -0.831470 }, + { 0.481138, 0.277785, -0.831470 }, + { 0.277785, 0.481138, -0.831470 }, + { -0.000000, 0.555570, -0.831470 }, + { -0.277785, 0.481138, -0.831470 }, + { -0.481138, 0.277785, -0.831470 }, + { -0.555570, -0.000000, -0.831470 }, + { -0.481138, -0.277785, -0.831470 }, + { -0.277785, -0.481138, -0.831470 }, + { 0.000000, -0.555570, -0.831470 }, + { 0.277785, -0.481138, -0.831470 }, + { 0.481138, -0.277785, -0.831470 }, + { 0.382683, 0.000000, -0.923880 }, + { 0.270598, 0.270598, -0.923880 }, + { -0.000000, 0.382683, -0.923880 }, + { -0.270598, 0.270598, -0.923880 }, + { -0.382683, -0.000000, -0.923880 }, + { -0.270598, -0.270598, -0.923880 }, + { 0.000000, -0.382683, -0.923880 }, + { 0.270598, -0.270598, -0.923880 }, + { 0.195090, 0.000000, -0.980785 }, + { -0.000000, 0.195090, -0.980785 }, + { -0.195090, -0.000000, -0.980785 }, + { 0.000000, -0.195090, -0.980785 }, + { 0.980785, 0.000000, 0.195090 }, + { 0.956195, 0.218245, 0.195090 }, + { 0.883657, 0.425547, 0.195090 }, + { 0.766809, 0.611510, 0.195090 }, + { 0.611510, 0.766809, 0.195090 }, + { 0.425547, 0.883657, 0.195090 }, + { 0.218245, 0.956195, 0.195090 }, + { -0.000000, 0.980785, 0.195090 }, + { -0.218245, 0.956195, 0.195090 }, + { -0.425547, 0.883657, 0.195090 }, + { -0.611510, 0.766809, 0.195090 }, + { -0.766809, 0.611510, 0.195090 }, + { -0.883657, 0.425547, 0.195090 }, + { -0.956195, 0.218245, 0.195090 }, + { -0.980785, -0.000000, 0.195090 }, + { -0.956195, -0.218245, 0.195090 }, + { -0.883657, -0.425547, 0.195090 }, + { -0.766809, -0.611510, 0.195090 }, + { -0.611510, -0.766809, 0.195090 }, + { -0.425547, -0.883657, 0.195090 }, + { -0.218245, -0.956195, 0.195090 }, + { 0.000000, -0.980785, 0.195090 }, + { 0.218245, -0.956195, 0.195090 }, + { 0.425547, -0.883657, 0.195090 }, + { 0.611510, -0.766809, 0.195090 }, + { 0.766809, -0.611510, 0.195090 }, + { 0.883657, -0.425547, 0.195090 }, + { 0.956195, -0.218245, 0.195090 }, + { 0.923880, 0.000000, 0.382683 }, + { 0.892399, 0.239118, 0.382683 }, + { 0.800103, 0.461940, 0.382683 }, + { 0.653281, 0.653281, 0.382683 }, + { 0.461940, 0.800103, 0.382683 }, + { 0.239118, 0.892399, 0.382683 }, + { -0.000000, 0.923880, 0.382683 }, + { -0.239118, 0.892399, 0.382683 }, + { -0.461940, 0.800103, 0.382683 }, + { -0.653281, 0.653281, 0.382683 }, + { -0.800103, 0.461940, 0.382683 }, + { -0.892399, 0.239118, 0.382683 }, + { -0.923880, -0.000000, 0.382683 }, + { -0.892399, -0.239118, 0.382683 }, + { -0.800103, -0.461940, 0.382683 }, + { -0.653282, -0.653281, 0.382683 }, + { -0.461940, -0.800103, 0.382683 }, + { -0.239118, -0.892399, 0.382683 }, + { 0.000000, -0.923880, 0.382683 }, + { 0.239118, -0.892399, 0.382683 }, + { 0.461940, -0.800103, 0.382683 }, + { 0.653281, -0.653282, 0.382683 }, + { 0.800103, -0.461940, 0.382683 }, + { 0.892399, -0.239117, 0.382683 }, + { 0.831470, 0.000000, 0.555570 }, + { 0.790775, 0.256938, 0.555570 }, + { 0.672673, 0.488726, 0.555570 }, + { 0.488726, 0.672673, 0.555570 }, + { 0.256938, 0.790775, 0.555570 }, + { -0.000000, 0.831470, 0.555570 }, + { -0.256938, 0.790775, 0.555570 }, + { -0.488726, 0.672673, 0.555570 }, + { -0.672673, 0.488726, 0.555570 }, + { -0.790775, 0.256938, 0.555570 }, + { -0.831470, -0.000000, 0.555570 }, + { -0.790775, -0.256938, 0.555570 }, + { -0.672673, -0.488726, 0.555570 }, + { -0.488725, -0.672673, 0.555570 }, + { -0.256938, -0.790775, 0.555570 }, + { 0.000000, -0.831470, 0.555570 }, + { 0.256938, -0.790775, 0.555570 }, + { 0.488725, -0.672673, 0.555570 }, + { 0.672673, -0.488726, 0.555570 }, + { 0.790775, -0.256938, 0.555570 }, + { 0.707107, 0.000000, 0.707107 }, + { 0.653281, 0.270598, 0.707107 }, + { 0.500000, 0.500000, 0.707107 }, + { 0.270598, 0.653281, 0.707107 }, + { -0.000000, 0.707107, 0.707107 }, + { -0.270598, 0.653282, 0.707107 }, + { -0.500000, 0.500000, 0.707107 }, + { -0.653281, 0.270598, 0.707107 }, + { -0.707107, -0.000000, 0.707107 }, + { -0.653281, -0.270598, 0.707107 }, + { -0.500000, -0.500000, 0.707107 }, + { -0.270598, -0.653281, 0.707107 }, + { 0.000000, -0.707107, 0.707107 }, + { 0.270598, -0.653281, 0.707107 }, + { 0.500000, -0.500000, 0.707107 }, + { 0.653282, -0.270598, 0.707107 }, + { 0.555570, 0.000000, 0.831470 }, + { 0.481138, 0.277785, 0.831470 }, + { 0.277785, 0.481138, 0.831470 }, + { -0.000000, 0.555570, 0.831470 }, + { -0.277785, 0.481138, 0.831470 }, + { -0.481138, 0.277785, 0.831470 }, + { -0.555570, -0.000000, 0.831470 }, + { -0.481138, -0.277785, 0.831470 }, + { -0.277785, -0.481138, 0.831470 }, + { 0.000000, -0.555570, 0.831470 }, + { 0.277785, -0.481138, 0.831470 }, + { 0.481138, -0.277785, 0.831470 }, + { 0.382683, 0.000000, 0.923880 }, + { 0.270598, 0.270598, 0.923880 }, + { -0.000000, 0.382683, 0.923880 }, + { -0.270598, 0.270598, 0.923880 }, + { -0.382683, -0.000000, 0.923880 }, + { -0.270598, -0.270598, 0.923880 }, + { 0.000000, -0.382683, 0.923880 }, + { 0.270598, -0.270598, 0.923880 }, + { 0.195090, 0.000000, 0.980785 }, + { -0.000000, 0.195090, 0.980785 }, + { -0.195090, -0.000000, 0.980785 }, + { 0.000000, -0.195090, 0.980785 } +}; + +/* mdc model frame information */ +typedef struct mdcFrame_s +{ + float bounds[ 2 ][ 3 ]; + float localOrigin[ 3 ]; + float radius; + char creator[ 16 ]; +} +mdcFrame_t; + +/* mdc model tag information */ +typedef struct mdcTag_s +{ + short xyz[3]; + short angles[3]; +} +mdcTag_t; + +/* mdc surface mdc (one object mesh) */ +typedef struct mdcSurface_s +{ + char magic[ 4 ]; + char name[ 64 ]; /* polyset name */ + int flags; + int numCompFrames; /* all surfaces in a model should have the same */ + int numBaseFrames; /* ditto */ + int numShaders; /* all model surfaces should have the same */ + int numVerts; + int numTriangles; + int ofsTriangles; + int ofsShaders; /* offset from start of mdcSurface_t */ + int ofsSt; /* texture coords are common for all frames */ + int ofsXyzNormals; /* numVerts * numBaseFrames */ + int ofsXyzCompressed; /* numVerts * numCompFrames */ + + int ofsFrameBaseFrames; /* numFrames */ + int ofsFrameCompFrames; /* numFrames */ + int ofsEnd; /* next surface follows */ +} +mdcSurface_t; + +typedef struct mdcShader_s +{ + char name[ 64 ]; + int shaderIndex; /* for ingame use */ +} +mdcShader_t; + +typedef struct mdcTriangle_s +{ + int indexes[ 3 ]; +} +mdcTriangle_t; + +typedef struct mdcTexCoord_s +{ + float st[ 2 ]; +} +mdcTexCoord_t; + +typedef struct mdcVertex_s +{ + short xyz[ 3 ]; + short normal; +} +mdcVertex_t; + +typedef struct mdcXyzCompressed_s +{ + unsigned int ofsVec; /* offset direction from the last base frame */ +} +mdcXyzCompressed_t; + + +/* mdc model file mdc structure */ +typedef struct mdc_s +{ + char magic[ 4 ]; /* MDC_MAGIC */ + int version; + char name[ 64 ]; /* model name */ + int flags; + int numFrames; + int numTags; + int numSurfaces; + int numSkins; /* number of skins for the mesh */ + int ofsFrames; /* offset for first frame */ + int ofsTagNames; /* numTags */ + int ofsTags; /* numFrames * numTags */ + int ofsSurfaces; /* first surface, others follow */ + int ofsEnd; /* end of file */ +} +mdc_t; + + + + +/* +_mdc_canload() +validates a Return to Castle Wolfenstein model file. btw, i use the +preceding underscore cause it's a static func referenced +by one structure only. +*/ + +static int _mdc_canload( PM_PARAMS_CANLOAD ) +{ + mdc_t *mdc; + + + /* to keep the compiler happy */ + *fileName = *fileName; + + /* sanity check */ + if( bufSize < ( sizeof( *mdc ) * 2) ) + return PICO_PMV_ERROR_SIZE; + + /* set as mdc */ + mdc = (mdc_t*) buffer; + + /* check mdc magic */ + if( *((int*) mdc->magic) != *((int*) MDC_MAGIC) ) + return PICO_PMV_ERROR_IDENT; + + /* check mdc version */ + if( _pico_little_long( mdc->version ) != MDC_VERSION ) + return PICO_PMV_ERROR_VERSION; + + /* file seems to be a valid mdc */ + return PICO_PMV_OK; +} + + + +/* +_mdc_load() +loads a Return to Castle Wolfenstein mdc model file. +*/ + +static picoModel_t *_mdc_load( PM_PARAMS_LOAD ) +{ + int i, j; + picoByte_t *bb; + mdc_t *mdc; + mdcSurface_t *surface; + mdcShader_t *shader; + mdcTexCoord_t *texCoord; + mdcFrame_t *frame; + mdcTriangle_t *triangle; + mdcVertex_t *vertex; + mdcXyzCompressed_t *vertexComp; + short *mdcShort, *mdcCompVert; + double lat, lng; + + picoModel_t *picoModel; + picoSurface_t *picoSurface; + picoShader_t *picoShader; + picoVec3_t xyz, normal; + picoVec2_t st; + picoColor_t color; + + + /* ------------------------------------------------- + mdc loading + ------------------------------------------------- */ + + + /* set as mdc */ + bb = (picoByte_t*) buffer; + mdc = (mdc_t*) buffer; + + /* check ident and version */ + if( *((int*) mdc->magic) != *((int*) MDC_MAGIC) || _pico_little_long( mdc->version ) != MDC_VERSION ) + { + /* not an mdc file (todo: set error) */ + return NULL; + } + + /* swap mdc */ + mdc->version = _pico_little_long( mdc->version ); + mdc->numFrames = _pico_little_long( mdc->numFrames ); + mdc->numTags = _pico_little_long( mdc->numTags ); + mdc->numSurfaces = _pico_little_long( mdc->numSurfaces ); + mdc->numSkins = _pico_little_long( mdc->numSkins ); + mdc->ofsFrames = _pico_little_long( mdc->ofsFrames ); + mdc->ofsTags = _pico_little_long( mdc->ofsTags ); + mdc->ofsTagNames = _pico_little_long( mdc->ofsTagNames ); + mdc->ofsSurfaces = _pico_little_long( mdc->ofsSurfaces ); + mdc->ofsEnd = _pico_little_long( mdc->ofsEnd ); + + /* do frame check */ + if( mdc->numFrames < 1 ) + { + _pico_printf( PICO_ERROR, "MDC with 0 frames" ); + return NULL; + } + + if( frameNum < 0 || frameNum >= mdc->numFrames ) + { + _pico_printf( PICO_ERROR, "Invalid or out-of-range MDC frame specified" ); + return NULL; + } + + /* swap frames */ + frame = (mdcFrame_t*) (bb + mdc->ofsFrames ); + for( i = 0; i < mdc->numFrames; i++, frame++ ) + { + frame->radius = _pico_little_float( frame->radius ); + for( j = 0; j < 3; j++ ) + { + frame->bounds[ 0 ][ j ] = _pico_little_float( frame->bounds[ 0 ][ j ] ); + frame->bounds[ 1 ][ j ] = _pico_little_float( frame->bounds[ 1 ][ j ] ); + frame->localOrigin[ j ] = _pico_little_float( frame->localOrigin[ j ] ); + } + } + + /* swap surfaces */ + surface = (mdcSurface_t*) (bb + mdc->ofsSurfaces); + for( i = 0; i < mdc->numSurfaces; i++ ) + { + /* swap surface mdc */ + surface->flags = _pico_little_long( surface->flags ); + surface->numBaseFrames = _pico_little_long( surface->numBaseFrames ); + surface->numCompFrames = _pico_little_long( surface->numCompFrames ); + surface->numShaders = _pico_little_long( surface->numShaders ); + surface->numTriangles = _pico_little_long( surface->numTriangles ); + surface->ofsTriangles = _pico_little_long( surface->ofsTriangles ); + surface->numVerts = _pico_little_long( surface->numVerts ); + surface->ofsShaders = _pico_little_long( surface->ofsShaders ); + surface->ofsSt = _pico_little_long( surface->ofsSt ); + surface->ofsXyzNormals = _pico_little_long( surface->ofsXyzNormals ); + surface->ofsXyzCompressed = _pico_little_long( surface->ofsXyzCompressed ); + surface->ofsFrameBaseFrames = _pico_little_long( surface->ofsFrameBaseFrames ); + surface->ofsFrameCompFrames = _pico_little_long( surface->ofsFrameCompFrames ); + surface->ofsEnd = _pico_little_long( surface->ofsEnd ); + + /* swap triangles */ + triangle = (mdcTriangle_t*) ((picoByte_t*) surface + surface->ofsTriangles); + for( j = 0; j < surface->numTriangles; j++, triangle++ ) + { + /* sea: swaps fixed */ + triangle->indexes[ 0 ] = _pico_little_long( triangle->indexes[ 0 ] ); + triangle->indexes[ 1 ] = _pico_little_long( triangle->indexes[ 1 ] ); + triangle->indexes[ 2 ] = _pico_little_long( triangle->indexes[ 2 ] ); + } + + /* swap st coords */ + texCoord = (mdcTexCoord_t*) ((picoByte_t*) surface + surface->ofsSt); + for( j = 0; j < surface->numVerts; j++, texCoord++ ) + { + texCoord->st[ 0 ] = _pico_little_float( texCoord->st[ 0 ] ); + texCoord->st[ 1 ] = _pico_little_float( texCoord->st[ 1 ] ); + } + + /* swap xyz/normals */ + vertex = (mdcVertex_t*) ((picoByte_t*) surface + surface->ofsXyzNormals); + for( j = 0; j < (surface->numVerts * surface->numBaseFrames); j++, vertex++) + { + vertex->xyz[ 0 ] = _pico_little_short( vertex->xyz[ 0 ] ); + vertex->xyz[ 1 ] = _pico_little_short( vertex->xyz[ 1 ] ); + vertex->xyz[ 2 ] = _pico_little_short( vertex->xyz[ 2 ] ); + vertex->normal = _pico_little_short( vertex->normal ); + } + + /* swap xyz/compressed */ + vertexComp = (mdcXyzCompressed_t*) ((picoByte_t*) surface + surface->ofsXyzCompressed); + for( j = 0; j < (surface->numVerts * surface->numCompFrames); j++, vertexComp++) + { + vertexComp->ofsVec = _pico_little_long( vertexComp->ofsVec ); + } + + /* swap base frames */ + mdcShort = (short *) ((picoByte_t*) surface + surface->ofsFrameBaseFrames); + for( j = 0; j < mdc->numFrames; j++, mdcShort++) + { + *mdcShort = _pico_little_short( *mdcShort ); + } + + /* swap compressed frames */ + mdcShort = (short *) ((picoByte_t*) surface + surface->ofsFrameCompFrames); + for( j = 0; j < mdc->numFrames; j++, mdcShort++) + { + *mdcShort = _pico_little_short( *mdcShort ); + } + + /* get next surface */ + surface = (mdcSurface_t*) ((picoByte_t*) surface + surface->ofsEnd); + } + + /* ------------------------------------------------- + pico model creation + ------------------------------------------------- */ + + /* create new pico model */ + picoModel = PicoNewModel(); + if( picoModel == NULL ) + { + _pico_printf( PICO_ERROR, "Unable to allocate a new model" ); + return NULL; + } + + /* do model setup */ + PicoSetModelFrameNum( picoModel, frameNum ); + PicoSetModelNumFrames( picoModel, mdc->numFrames ); /* sea */ + PicoSetModelName( picoModel, fileName ); + PicoSetModelFileName( picoModel, fileName ); + + /* mdc surfaces become picomodel surfaces */ + surface = (mdcSurface_t*) (bb + mdc->ofsSurfaces); + + /* run through mdc surfaces */ + for( i = 0; i < mdc->numSurfaces; i++ ) + { + /* allocate new pico surface */ + picoSurface = PicoNewSurface( picoModel ); + if( picoSurface == NULL ) + { + _pico_printf( PICO_ERROR, "Unable to allocate a new model surface" ); + PicoFreeModel( picoModel ); /* sea */ + return NULL; + } + + /* mdc model surfaces are all triangle meshes */ + PicoSetSurfaceType( picoSurface, PICO_TRIANGLES ); + + /* set surface name */ + PicoSetSurfaceName( picoSurface, surface->name ); + + /* create new pico shader -sea */ + picoShader = PicoNewShader( picoModel ); + if( picoShader == NULL ) + { + _pico_printf( PICO_ERROR, "Unable to allocate a new model shader" ); + PicoFreeModel( picoModel ); + return NULL; + } + + /* detox and set shader name */ + shader = (mdcShader_t*) ((picoByte_t*) surface + surface->ofsShaders); + _pico_setfext( shader->name, "" ); + _pico_unixify( shader->name ); + PicoSetShaderName( picoShader, shader->name ); + + /* associate current surface with newly created shader */ + PicoSetSurfaceShader( picoSurface, picoShader ); + + /* copy indexes */ + triangle = (mdcTriangle_t *) ((picoByte_t*) surface + surface->ofsTriangles); + + for( j = 0; j < surface->numTriangles; j++, triangle++ ) + { + PicoSetSurfaceIndex( picoSurface, (j * 3 + 0), (picoIndex_t) triangle->indexes[ 0 ] ); + PicoSetSurfaceIndex( picoSurface, (j * 3 + 1), (picoIndex_t) triangle->indexes[ 1 ] ); + PicoSetSurfaceIndex( picoSurface, (j * 3 + 2), (picoIndex_t) triangle->indexes[ 2 ] ); + } + + /* copy vertexes */ + texCoord = (mdcTexCoord_t*) ((picoByte_t *) surface + surface->ofsSt); + mdcShort = (short *) ((picoByte_t *) surface + surface->ofsXyzNormals) + ((int)*((short *) ((picoByte_t *) surface + surface->ofsFrameBaseFrames) + frameNum) * surface->numVerts * 4); + if( surface->numCompFrames > 0 ) + { + mdcCompVert = (short *) ((picoByte_t *) surface + surface->ofsFrameCompFrames) + frameNum; + if( *mdcCompVert >= 0 ) + vertexComp = (mdcXyzCompressed_t *) ((picoByte_t *) surface + surface->ofsXyzCompressed) + (*mdcCompVert * surface->numVerts); + } + _pico_set_color( color, 255, 255, 255, 255 ); + + for( j = 0; j < surface->numVerts; j++, texCoord++, mdcShort+=4 ) + { + /* set vertex origin */ + xyz[ 0 ] = MDC_SCALE * mdcShort[ 0 ]; + xyz[ 1 ] = MDC_SCALE * mdcShort[ 1 ]; + xyz[ 2 ] = MDC_SCALE * mdcShort[ 2 ]; + + /* add compressed ofsVec */ + if( surface->numCompFrames > 0 && *mdcCompVert >= 0 ) + { + xyz[ 0 ] += ((float) ((vertexComp->ofsVec) & 255) - MDC_MAX_OFS) * MDC_DIST_SCALE; + xyz[ 1 ] += ((float) ((vertexComp->ofsVec >> 8) & 255) - MDC_MAX_OFS) * MDC_DIST_SCALE; + xyz[ 2 ] += ((float) ((vertexComp->ofsVec >> 16) & 255) - MDC_MAX_OFS) * MDC_DIST_SCALE; + PicoSetSurfaceXYZ( picoSurface, j, xyz ); + + normal[ 0 ] = (float) mdcNormals[ (vertexComp->ofsVec >> 24) ][ 0 ]; + normal[ 1 ] = (float) mdcNormals[ (vertexComp->ofsVec >> 24) ][ 1 ]; + normal[ 2 ] = (float) mdcNormals[ (vertexComp->ofsVec >> 24) ][ 2 ]; + PicoSetSurfaceNormal( picoSurface, j, normal ); + + vertexComp++; + } + else + { + PicoSetSurfaceXYZ( picoSurface, j, xyz ); + + /* decode lat/lng normal to 3 float normal */ + lat = (float) ((*(mdcShort + 3) >> 8) & 0xff); + lng = (float) (*(mdcShort + 3) & 0xff); + lat *= PICO_PI / 128; + lng *= PICO_PI / 128; + normal[ 0 ] = (picoVec_t) cos( lat ) * (picoVec_t) sin( lng ); + normal[ 1 ] = (picoVec_t) sin( lat ) * (picoVec_t) sin( lng ); + normal[ 2 ] = (picoVec_t) cos( lng ); + PicoSetSurfaceNormal( picoSurface, j, normal ); + } + + /* set st coords */ + st[ 0 ] = texCoord->st[ 0 ]; + st[ 1 ] = texCoord->st[ 1 ]; + PicoSetSurfaceST( picoSurface, 0, j, st ); + + /* set color */ + PicoSetSurfaceColor( picoSurface, 0, j, color ); + } + + /* get next surface */ + surface = (mdcSurface_t*) ((picoByte_t*) surface + surface->ofsEnd); + } + + /* return the new pico model */ + return picoModel; +} + + + +/* pico file format module definition */ +const picoModule_t picoModuleMDC = +{ + "1.3", /* module version string */ + "RtCW MDC", /* module display name */ + "Arnout van Meer", /* author's name */ + "2002 Arnout van Meer", /* module copyright */ + { + "mdc", NULL, NULL, NULL /* default extensions to use */ + }, + _mdc_canload, /* validation routine */ + _mdc_load, /* load routine */ + NULL, /* save validation routine */ + NULL /* save routine */ +}; diff --git a/tools/urt/libs/picomodel/pm_ms3d.c b/tools/urt/libs/picomodel/pm_ms3d.c new file mode 100644 index 00000000..4924fd9a --- /dev/null +++ b/tools/urt/libs/picomodel/pm_ms3d.c @@ -0,0 +1,494 @@ +/* ----------------------------------------------------------------------------- + +PicoModel Library + +Copyright (c) 2002, Randy Reddig & seaw0lf +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list +of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +Neither the names of the copyright holders nor the names of its contributors may +be used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +----------------------------------------------------------------------------- */ + + + +/* marker */ +#define PM_MS3D_C + +/* dependencies */ +#include "picointernal.h" + +/* disable warnings */ +#ifdef WIN32 +#pragma warning( disable:4100 ) /* unref param */ +#endif + +/* remarks: + * - loader seems stable + * todo: + * - fix uv coordinate problem + * - check for buffer overflows ('bufptr' accesses) + */ +/* uncomment when debugging this module */ + #define DEBUG_PM_MS3D + #define DEBUG_PM_MS3D_EX + +/* plain white */ +static picoColor_t white = { 255,255,255,255 }; + +/* ms3d limits */ +#define MS3D_MAX_VERTS 8192 +#define MS3D_MAX_TRIS 16384 +#define MS3D_MAX_GROUPS 128 +#define MS3D_MAX_MATERIALS 128 +#define MS3D_MAX_JOINTS 128 +#define MS3D_MAX_KEYFRAMES 216 + +/* ms3d flags */ +#define MS3D_SELECTED 1 +#define MS3D_HIDDEN 2 +#define MS3D_SELECTED2 4 +#define MS3D_DIRTY 8 + +/* this freaky loader needs byte alignment */ +#pragma pack(push, 1) + +/* ms3d header */ +typedef struct SMsHeader +{ + char magic[10]; + int version; +} +TMsHeader; + +/* ms3d vertex */ +typedef struct SMsVertex +{ + unsigned char flags; /* sel, sel2, or hidden */ + float xyz[3]; + char boneID; /* -1 means 'no bone' */ + unsigned char refCount; +} +TMsVertex; + +/* ms3d triangle */ +typedef struct SMsTriangle +{ + unsigned short flags; /* sel, sel2, or hidden */ + unsigned short vertexIndices[3]; + float vertexNormals[3][3]; + float s[3]; + float t[3]; + unsigned char smoothingGroup; /* 1 - 32 */ + unsigned char groupIndex; +} +TMsTriangle; + +/* ms3d material */ +typedef struct SMsMaterial +{ + char name[32]; + float ambient[4]; + float diffuse[4]; + float specular[4]; + float emissive[4]; + float shininess; /* range 0..128 */ + float transparency; /* range 0..1 */ + unsigned char mode; + char texture [128]; /* texture.bmp */ + char alphamap[128]; /* alpha.bmp */ +} +TMsMaterial; + +// ms3d group (static part) +// followed by a variable size block (see below) +typedef struct SMsGroup +{ + unsigned char flags; // sel, hidden + char name[32]; + unsigned short numTriangles; +/* + unsigned short triangleIndices[ numTriangles ]; + char materialIndex; // -1 means 'no material' +*/ +} +TMsGroup; + +// ms3d joint +typedef struct SMsJoint +{ + unsigned char flags; + char name[32]; + char parentName[32]; + float rotation[3]; + float translation[3]; + unsigned short numRotationKeyframes; + unsigned short numTranslationKeyframes; +} +TMsJoint; + +// ms3d keyframe +typedef struct SMsKeyframe +{ + float time; + float parameter[3]; +} +TMsKeyframe; + +/* restore previous data alignment */ +#pragma pack(pop) + +/* _ms3d_canload: + * validates a milkshape3d model file. + */ +static int _ms3d_canload( PM_PARAMS_CANLOAD ) +{ + TMsHeader *hdr; + + + /* to keep the compiler happy */ + *fileName = *fileName; + + /* sanity check */ + if (bufSize < sizeof(TMsHeader)) + return PICO_PMV_ERROR_SIZE; + + /* get ms3d header */ + hdr = (TMsHeader *)buffer; + + /* check ms3d magic */ + if (strncmp(hdr->magic,"MS3D000000",10) != 0) + return PICO_PMV_ERROR_IDENT; + + /* check ms3d version */ + if (_pico_little_long(hdr->version) < 3 || + _pico_little_long(hdr->version) > 4) + { + _pico_printf( PICO_ERROR,"MS3D file ignored. Only MS3D 1.3 and 1.4 is supported." ); + return PICO_PMV_ERROR_VERSION; + } + /* file seems to be a valid ms3d */ + return PICO_PMV_OK; +} + +static unsigned char *GetWord( unsigned char *bufptr, int *out ) +{ + if (bufptr == NULL) return NULL; + *out = _pico_little_short( *(unsigned short *)bufptr ); + return( bufptr + 2 ); +} + +/* _ms3d_load: + * loads a milkshape3d model file. +*/ +static picoModel_t *_ms3d_load( PM_PARAMS_LOAD ) +{ + picoModel_t *model; + unsigned char *bufptr; + int shaderRefs[ MS3D_MAX_GROUPS ]; + int numGroups; + int numMaterials; +// unsigned char *ptrToGroups; + int numVerts; + unsigned char *ptrToVerts; + int numTris; + unsigned char *ptrToTris; + int i,k,m; + + /* create new pico model */ + model = PicoNewModel(); + if (model == NULL) return NULL; + + /* do model setup */ + PicoSetModelFrameNum( model, frameNum ); + PicoSetModelName( model, fileName ); + PicoSetModelFileName( model, fileName ); + + /* skip header */ + bufptr = (unsigned char *)buffer + sizeof(TMsHeader); + + /* get number of vertices */ + bufptr = GetWord( bufptr,&numVerts ); + ptrToVerts = bufptr; + +#ifdef DEBUG_PM_MS3D + printf("NumVertices: %d\n",numVerts); +#endif + /* swap verts */ + for (i=0; ixyz[ 0 ] = _pico_little_float( vertex->xyz[ 0 ] ); + vertex->xyz[ 1 ] = _pico_little_float( vertex->xyz[ 1 ] ); + vertex->xyz[ 2 ] = _pico_little_float( vertex->xyz[ 2 ] ); + +#ifdef DEBUG_PM_MS3D_EX_ + printf("Vertex: x: %f y: %f z: %f\n", + msvd[i]->vertex[0], + msvd[i]->vertex[1], + msvd[i]->vertex[2]); +#endif + } + /* get number of triangles */ + bufptr = GetWord( bufptr,&numTris ); + ptrToTris = bufptr; + +#ifdef DEBUG_PM_MS3D + printf("NumTriangles: %d\n",numTris); +#endif + /* swap tris */ + for (i=0; iflags = _pico_little_short( triangle->flags ); + + /* run through all tri verts */ + for (k=0; k<3; k++) + { + /* swap tex coords */ + triangle->s[ k ] = _pico_little_float( triangle->s[ k ] ); + triangle->t[ k ] = _pico_little_float( triangle->t[ k ] ); + + /* swap fields */ + triangle->vertexIndices[ k ] = _pico_little_short( triangle->vertexIndices[ k ] ); + triangle->vertexNormals[ 0 ][ k ] = _pico_little_float( triangle->vertexNormals[ 0 ][ k ] ); + triangle->vertexNormals[ 1 ][ k ] = _pico_little_float( triangle->vertexNormals[ 1 ][ k ] ); + triangle->vertexNormals[ 2 ][ k ] = _pico_little_float( triangle->vertexNormals[ 2 ][ k ] ); + + /* check for out of range indices */ + if (triangle->vertexIndices[ k ] >= numVerts) + { + _pico_printf( PICO_ERROR,"Vertex %d index %d out of range (%d, max %d)",i,k,triangle->vertexIndices[k],numVerts-1); + PicoFreeModel( model ); + return NULL; /* yuck */ + } + } + } + /* get number of groups */ + bufptr = GetWord( bufptr,&numGroups ); +// ptrToGroups = bufptr; + +#ifdef DEBUG_PM_MS3D + printf("NumGroups: %d\n",numGroups); +#endif + /* run through all groups in model */ + for (i=0; i