commit e7e0b39e827c5342032e4160762495c2d1472fbc Author: Martin Felis (berta) Date: Mon Apr 5 23:38:59 2010 +0200 initial commit diff --git a/.hgignore b/.hgignore new file mode 100644 index 0000000..37e9599 --- /dev/null +++ b/.hgignore @@ -0,0 +1,20 @@ +syntax:glob +CMakeCache.txt +CMakeFiles +cmake_install.cmake +install_manifest.txt +debug +tags +Makefile + +start +runtests +hasteroids + +./doc/html/* + +*.log +*.a +*.o +*.so +*.swp diff --git a/CMake/FindCEGUI.cmake b/CMake/FindCEGUI.cmake new file mode 100644 index 0000000..1c8a0e1 --- /dev/null +++ b/CMake/FindCEGUI.cmake @@ -0,0 +1,45 @@ +# Tries to find CEGUI (http://CEGUI.bespin.org) a simple and fast +# network library by Lee Salzman +# + +SET (CEGUI_FOUND FALSE) + +FIND_PATH (CEGUI_INCLUDE_DIR CEGUI.h /usr/include/CEGUI + /usr/local/include/CEGUI $ENV{CEGUI_PATH}/include/CEGUI + $ENV{CEGUI_INCLUDE_PATH}/CEGUI) + +FIND_LIBRARY (CEGUI_BASE_LIBRARIES NAMES CEGUIBase PATHS /usr/lib /usr/local/lib $ENV{CEGUI_PATH} $ENV{CEGUI_PATH}/lib ENV{CEGUI_LIBRARY_PATH}) + +FIND_LIBRARY (CEGUI_RENDERER_LIBRARIES NAMES CEGUIOpenGLRenderer PATHS /usr/lib /usr/local/lib $ENV{CEGUI_PATH} $ENV{CEGUI_PATH}/lib ENV{CEGUI_LIBRARY_PATH}) + +SET ( CEGUI_LIBRARIES FALSE ) + +IF ( CEGUI_BASE_LIBRARIES AND CEGUI_RENDERER_LIBRARIES ) + SET ( CEGUI_LIBRARIES + ${CEGUI_BASE_LIBRARIES} + ${CEGUI_RENDERER_LIBRARIES} + ) +ENDIF ( CEGUI_BASE_LIBRARIES AND CEGUI_RENDERER_LIBRARIES ) + +MESSAGE (STATUS "CEGUI Libraries: ${CEGUI_LIBRARIES}") + +IF (CEGUI_INCLUDE_DIR AND CEGUI_LIBRARIES) + SET (CEGUI_FOUND TRUE) +ENDIF (CEGUI_INCLUDE_DIR AND CEGUI_LIBRARIES) + +IF (CEGUI_FOUND) + IF (NOT CEGUI_FIND_QUIETLY) + MESSAGE(STATUS "Found CEGUI: ${CEGUI_LIBRARIES}") + ENDIF (NOT CEGUI_FIND_QUIETLY) +ELSE (CEGUI_FOUND) + IF (CEGUI_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find CEGUI") + ENDIF (CEGUI_FIND_REQUIRED) +ENDIF (CEGUI_FOUND) + +MARK_AS_ADVANCED ( + CEGUI_INCLUDE_DIR + CEGUI_BASE_LIBRARIES + CEGUI_RENDERER_LIBRARIES + CEGUI_LIBRARIES + ) diff --git a/CMake/FindENET.cmake b/CMake/FindENET.cmake new file mode 100644 index 0000000..19a46b4 --- /dev/null +++ b/CMake/FindENET.cmake @@ -0,0 +1,28 @@ +# Tries to find ENET (http://enet.bespin.org) a simple and fast +# network library by Lee Salzman +# + +SET (ENET_FOUND FALSE) + +FIND_PATH (ENET_INCLUDE_DIR enet/enet.h /usr/include/ /usr/local/include/ $ENV{ENET_PATH}/include $ENV{ENET_INCLUDE_PATH}) + +FIND_LIBRARY (ENET_LIBRARIES NAMES enet PATHS /usr/lib /usr/local/lib $ENV{ENET_PATH} $ENV{ENET_PATH}/lib ENV{ENET_LIBRARY_PATH}) + +IF (ENET_INCLUDE_DIR AND ENET_LIBRARIES) + SET (ENET_FOUND TRUE) +ENDIF (ENET_INCLUDE_DIR AND ENET_LIBRARIES) + +IF (ENET_FOUND) + IF (NOT ENET_FIND_QUIETLY) + MESSAGE(STATUS "Found ENET: ${ENET_LIBRARIES}") + ENDIF (NOT ENET_FIND_QUIETLY) +ELSE (ENET_FOUND) + IF (ENET_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find ENET") + ENDIF (ENET_FIND_REQUIRED) +ENDIF (ENET_FOUND) + +MARK_AS_ADVANCED ( + ENET_INCLUDE_DIR + ENET_LIBRARIES + ) diff --git a/CMake/FindFreeType2.cmake b/CMake/FindFreeType2.cmake new file mode 100644 index 0000000..3abf606 --- /dev/null +++ b/CMake/FindFreeType2.cmake @@ -0,0 +1,28 @@ +# Tries to find FREETYPE2 (http://freetype2.bespin.org) a simple and fast +# network library by Lee Salzman +# + +SET (FREETYPE2_FOUND FALSE) + +FIND_PATH (FREETYPE2_INCLUDE_DIR freetype/freetype.h /usr/include/ /usr/local/include/ /usr/include/freetype2 /usr/local/include/freetype2 $ENV{FREETYPE2_PATH}/include $ENV{FREETYPE2_INCLUDE_PATH}) + +FIND_LIBRARY (FREETYPE2_LIBRARIES NAMES freetype PATHS /usr/lib /usr/local/lib $ENV{FREETYPE2_PATH} $ENV{FREETYPE2_PATH}/lib ENV{FREETYPE2_LIBRARY_PATH}) + +IF (FREETYPE2_INCLUDE_DIR AND FREETYPE2_LIBRARIES) + SET (FREETYPE2_FOUND TRUE) +ENDIF (FREETYPE2_INCLUDE_DIR AND FREETYPE2_LIBRARIES) + +IF (FREETYPE2_FOUND) + IF (NOT FREETYPE2_FIND_QUIETLY) + MESSAGE(STATUS "Found FREETYPE2: ${FREETYPE2_LIBRARIES}") + ENDIF (NOT FREETYPE2_FIND_QUIETLY) +ELSE (FREETYPE2_FOUND) + IF (FREETYPE2_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find FREETYPE2") + ENDIF (FREETYPE2_FIND_REQUIRED) +ENDIF (FREETYPE2_FOUND) + +MARK_AS_ADVANCED ( + FREETYPE2_INCLUDE_DIR + FREETYPE2_LIBRARIES + ) diff --git a/CMake/FindUnitTest++.cmake b/CMake/FindUnitTest++.cmake new file mode 100644 index 0000000..261e7a8 --- /dev/null +++ b/CMake/FindUnitTest++.cmake @@ -0,0 +1,28 @@ +# - Try to find UnitTest++ +# +# + +SET (UNITTEST++_FOUND FALSE) + +FIND_PATH (UNITTEST++_INCLUDE_DIR UnitTest++.h /usr/include/unittest++ /usr/local/include/unittest++ $ENV{UNITTESTXX_PATH}/src $ENV{UNITTESTXX_INCLUDE_PATH}) + +FIND_LIBRARY (UNITTEST++_LIBRARY NAMES UnitTest++ PATHS /usr/lib /usr/local/lib $ENV{UNITTESTXX_PATH} ENV{UNITTESTXX_LIBRARY_PATH}) + +IF (UNITTEST++_INCLUDE_DIR AND UNITTEST++_LIBRARY) + SET (UNITTEST++_FOUND TRUE) +ENDIF (UNITTEST++_INCLUDE_DIR AND UNITTEST++_LIBRARY) + +IF (UNITTEST++_FOUND) + IF (NOT UnitTest++_FIND_QUIETLY) + MESSAGE(STATUS "Found UnitTest++: ${UNITTEST++_LIBRARY}") + ENDIF (NOT UnitTest++_FIND_QUIETLY) +ELSE (UNITTEST++_FOUND) + IF (UnitTest++_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find UnitTest++") + ENDIF (UnitTest++_FIND_REQUIRED) +ENDIF (UNITTEST++_FOUND) + +MARK_AS_ADVANCED ( + UNITTEST++_INCLUDE_DIR + UNITTEST++_LIBRARY + ) diff --git a/CMake/Findfysx.cmake b/CMake/Findfysx.cmake new file mode 100644 index 0000000..5270fc6 --- /dev/null +++ b/CMake/Findfysx.cmake @@ -0,0 +1,28 @@ +# - Try to find FYSX +# +# + +SET (FYSX_FOUND FALSE) + +FIND_PATH (FYSX_INCLUDE_DIR fysx.h /usr/include/ /usr/local/include/ $ENV{FYSX_PATH}/include $ENV{FYSX_INCLUDE_PATH}) + +FIND_LIBRARY (FYSX_LIBRARIES NAMES fysx PATHS /usr/lib /usr/local/lib $ENV{FYSX_PATH} $ENV{FYSX_PATH}/lib ENV{FYSX_LIBRARY_PATH}) + +IF (FYSX_INCLUDE_DIR AND FYSX_LIBRARIES) + SET (FYSX_FOUND TRUE) +ENDIF (FYSX_INCLUDE_DIR AND FYSX_LIBRARIES) + +IF (FYSX_FOUND) + IF (NOT FYSX_FIND_QUIETLY) + MESSAGE(STATUS "Found FYSX: ${FYSX_LIBRARIES}") + ENDIF (NOT FYSX_FIND_QUIETLY) +ELSE (FYSX_FOUND) + IF (FYSX_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find FYSX") + ENDIF (FYSX_FIND_REQUIRED) +ENDIF (FYSX_FOUND) + +MARK_AS_ADVANCED ( + FYSX_INCLUDE_DIR + FYSX_LIBRARIES + ) diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..62b6b65 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,43 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) + +LIST( APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMake ) + +# FIND_PACKAGE (Cal3D REQUIRED) + +INCLUDE_DIRECTORIES ( + src/ + engine/ + include/ + engine/libraries/mathlib/ + engine/libraries/coll2d/include + engine/libraries/oglft/ + ${FREETYPE2_INCLUDE_DIR} + ) + +SET_TARGET_PROPERTIES ( ${PROJECT_EXECUTABLES} PROPERTIES + LINKER_LANGUAGE CXX + ) + +ADD_SUBDIRECTORY ( engine ) + +SET ( ASTEROIDS_SOURCES + asteroids/AsteroidEntity.cc + asteroids/Controller.cc + asteroids/ControllerCommands.cc + asteroids/EntityFactory.cc + asteroids/EnumToString.cc + asteroids/main.cc + asteroids/Model.cc + asteroids/ModelCommands.cc + asteroids/Physics.cc + asteroids/RocketEntity.cc + asteroids/ShipEntity.cc + asteroids/View.cc + asteroids/MenuOverlay.cc + ) + +ADD_EXECUTABLE ( hasteroids ${ASTEROIDS_SOURCES} ) + +TARGET_LINK_LIBRARIES ( hasteroids + Engine + ) diff --git a/Doxyfile b/Doxyfile new file mode 100644 index 0000000..ff51aa3 --- /dev/null +++ b/Doxyfile @@ -0,0 +1,1563 @@ +# Doxyfile 1.5.8 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = Engine + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = ./doc/ + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek, +# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish, +# Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, Slovene, +# Spanish, Swedish, and Ukrainian. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it parses. +# With this tag you can assign which parser to use for a given extension. +# Doxygen has a built-in mapping, but you can override or extend it using this tag. +# The format is ext=language, where ext is a file extension, and language is one of +# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP, +# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat +# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C + +EXTENSION_MAPPING = + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate getter +# and setter methods for a property. Setting this option to YES (the default) +# will make doxygen to replace the get and set methods by a property in the +# documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penality. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will rougly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols + +SYMBOL_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = YES + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespace are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 29 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by +# doxygen. The layout file controls the global structure of the generated output files +# in an output format independent way. The create the layout file that represents +# doxygen's defaults, run doxygen with the -l option. You can optionally specify a +# file name after the option, if omitted DoxygenLayout.xml will be used as the name +# of the layout file. + +LAYOUT_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = ./game/ \ + ./engine/ \ + ./engine/doc/ \ + ./libraries/coll2d/include \ + ./libraries/coll2d/src + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.d \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.idl \ + *.odl \ + *.cs \ + *.php \ + *.php3 \ + *.inc \ + *.m \ + *.mm \ + *.dox \ + *.py \ + *.f90 \ + *.f \ + *.vhd \ + *.vhdl \ + *.C \ + *.CC \ + *.C++ \ + *.II \ + *.I++ \ + *.H \ + *.HH \ + *.H++ \ + *.CS \ + *.PHP \ + *.PHP3 \ + *.M \ + *.MM \ + *.PY \ + *.F90 \ + *.F \ + *.VHD \ + *.VHDL + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = NO + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER +# are set, an additional index file will be generated that can be used as input for +# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated +# HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add. +# For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see +# Qt Help Project / Custom Filters. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's +# filter section matches. +# Qt Help Project / Filter Attributes. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to FRAME, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. Other possible values +# for this tag are: HIERARCHIES, which will generate the Groups, Directories, +# and Class Hierarchy pages using a tree view instead of an ordered list; +# ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which +# disables this behavior completely. For backwards compatibility with previous +# releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE +# respectively. + +GENERATE_TREEVIEW = NONE + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = NO + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = YES + +# By default doxygen will write a font called FreeSans.ttf to the output +# directory and reference it in all dot files that doxygen generates. This +# font does not include all possible unicode characters however, so when you need +# these (or just want a differently looking font) you can specify the font name +# using DOT_FONTNAME. You need need to make sure dot is able to find the font, +# which can be done by putting it in a standard location or by setting the +# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory +# containing the font. + +DOT_FONTNAME = FreeSans + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the output directory to look for the +# FreeSans.ttf font (which doxygen will put there itself). If you specify a +# different font using DOT_FONTNAME you can set the path where dot +# can find it using this tag. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = YES + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = YES + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 1000 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = YES + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Options related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO diff --git a/asteroids/AsteroidEntity.cc b/asteroids/AsteroidEntity.cc new file mode 100644 index 0000000..d620bf5 --- /dev/null +++ b/asteroids/AsteroidEntity.cc @@ -0,0 +1,47 @@ +#include "Engine.h" + +#include "ModelBase.h" + +#include "AsteroidEntity.h" +#include "Controller.h" +#include "Model.h" +#include "EntityBase.h" + +#include "coll2d.h" + +namespace asteroids { + +bool AsteroidEntity::CollisionEvent (Engine::EntityBase* entity_state) { + GameEntityType other_type = (GameEntityType) entity_state->mType; + + Engine::LogMessage ("CONTACT OF AN ASTEROID"); + + if (other_type == GameEntityTypeRocket) { + // First remove the rocket + Engine::KillEntity (entity_state->mId); + + Engine::LogMessage ("You killed an ASTEROID!"); + + int i; + for (i = 0; i < mSubAsteroidsCount; i++) { + AsteroidEntity *asteroid = (AsteroidEntity*) Engine::CreateEntity (GameEntityTypeAsteroid); + vector3d position (rand()/float(RAND_MAX) * 1. - 0.5, 0., rand()/float(RAND_MAX) * 1. - 0.5); + asteroid->mSubAsteroidsCount = 0; + asteroid->mPhysicState->mRadius = 2. * mPhysicState->mRadius / mSubAsteroidsCount; + static_cast(asteroid->mPhysicState->mShape)->setRadius (asteroid->mPhysicState->mRadius); + asteroid->mPhysicState->mPosition = mPhysicState->mPosition + position; + asteroid->mPhysicState->mVelocity = position; + asteroid->mPhysicState->mAngleVelocity = mPhysicState->mAngleVelocity + (rand()/float(RAND_MAX) * 2. - 1) * mPhysicState->mAngleVelocity; + } + + Engine::KillEntity (mId); + + return true; + } else if (other_type == GameEntityTypeRocket) { + return false; + } + + return false; +} + +} diff --git a/asteroids/AsteroidEntity.h b/asteroids/AsteroidEntity.h new file mode 100644 index 0000000..126e6f9 --- /dev/null +++ b/asteroids/AsteroidEntity.h @@ -0,0 +1,33 @@ +#ifndef _ASTEROIDENTITY_H +#define _ASTEROIDENTITY_H + +#include "EntityBase.h" +#include "AsteroidsEnums.h" +#include "Sprite.h" + +namespace asteroids { + +struct AsteroidEntityPhysicState : public Engine::EntityPhysicState { + AsteroidEntityPhysicState () { + mBaseType = Engine::EntityBaseTypeBlock; + mType = GameEntityTypeAsteroid; + } + virtual ~AsteroidEntityPhysicState() {}; +}; + +struct AsteroidEntity: public Engine::EntityBase { + AsteroidEntity () { + mBaseType = Engine::EntityBaseTypeBlock; + mType = GameEntityTypeAsteroid; + + mSubAsteroidsCount = 4; + } + + virtual bool CollisionEvent (Engine::EntityBase *entity); + + int mSubAsteroidsCount; +}; + +} + +#endif // _ASTEROIDENTITY_H diff --git a/asteroids/AsteroidsEnums.h b/asteroids/AsteroidsEnums.h new file mode 100644 index 0000000..d9d032b --- /dev/null +++ b/asteroids/AsteroidsEnums.h @@ -0,0 +1,37 @@ +#if ( !defined(_ASTEROIDSENUMS_H) || defined(GENERATE_ENUM_STRINGS) ) + +#if ( !defined(GENERATE_ENUM_STRINGS)) + #define _ASTEROIDSENUMS_H +#endif + +#include "EnumToString.h" + +namespace asteroids { + +BEGIN_ENUM(GameEntityType) +{ + DECL_ENUM_ELEMENT(GameEntityTypeUnknown), + DECL_ENUM_ELEMENT(GameEntityTypeShip), + DECL_ENUM_ELEMENT(GameEntityTypeRocket), + DECL_ENUM_ELEMENT(GameEntityTypeAsteroid), + DECL_ENUM_ELEMENT(GameEntityTypeShipPart), + DECL_ENUM_ELEMENT(GameEntityTypeLast) +} +END_ENUM(GameEntityType) + +BEGIN_ENUM(GameState) +{ + DECL_ENUM_ELEMENT(GameStateMainMenu), + DECL_ENUM_ELEMENT(GameStateRunning), + DECL_ENUM_ELEMENT(GameStatePaused), + DECL_ENUM_ELEMENT(GameStatePlayerDied), + DECL_ENUM_ELEMENT(GameStateLevelComplete), + DECL_ENUM_ELEMENT(GameStateGameOver) +} +END_ENUM(GameState) + +#include "AsteroidsEvents.h" + +} +#endif /* _ASTEROIDSENUMS_H */ + diff --git a/asteroids/AsteroidsEvents.h b/asteroids/AsteroidsEvents.h new file mode 100644 index 0000000..e416ebe --- /dev/null +++ b/asteroids/AsteroidsEvents.h @@ -0,0 +1,14 @@ +#include "EnumToString.h" + +namespace asteroids { + +BEGIN_ENUM(Event) +{ + DECL_ENUM_ELEMENT(EventAccelerateStart), + DECL_ENUM_ELEMENT(EventAccelerateStop), + DECL_ENUM_ELEMENT(EventShipExplode) +} +END_ENUM(Event) + +} + diff --git a/asteroids/Controller.cc b/asteroids/Controller.cc new file mode 100644 index 0000000..b0ffeee --- /dev/null +++ b/asteroids/Controller.cc @@ -0,0 +1,26 @@ +#include "Controller.h" + +namespace asteroids { + +int Controller::OnInit (int argc, char *argv[]) { + Engine::ControllerBase::OnInit (argc, argv); + + mBindings[SDLK_q] = "quit"; + + mBindings[SDLK_v] = "+forward"; + mBindings[SDLK_h] = "+turnleft"; + mBindings[SDLK_g] = "+turnright"; + + mBindings[SDLK_UP] = "+forward"; + mBindings[SDLK_LEFT] = "+turnleft"; + mBindings[SDLK_RIGHT] = "+turnright"; + + mBindings[SDLK_SPACE] = "attack"; + + mBindings[SDLK_F8] = "toggleconsole"; + mBindings[SDLK_F9] = "set playerspeed 5.0"; + + return 0; +} + +} diff --git a/asteroids/Controller.h b/asteroids/Controller.h new file mode 100644 index 0000000..f81881b --- /dev/null +++ b/asteroids/Controller.h @@ -0,0 +1,35 @@ +#ifndef _CONTROLLER_H +#define _CONTROLLER_H + +#include "Engine.h" +#include "ControllerBase.h" + +namespace asteroids { + +/** \brief All possible controller states for an Entity + * + * These are used by the controller to update the current state of an Entity + * (e.g. EntityPhysicState). + */ +enum EntityControllerKeyState { + EntityKeyStateForward = 0, + EntityKeyStateBack, + EntityKeyStateLeft, + EntityKeyStateRight, + EntityKeyStateTurnLeft, + EntityKeyStateTurnRight, + EntityKeyStateLast +}; + +class Controller : public Engine::ControllerBase { + public: + Controller () {}; + protected: + /** \brief Set up basic keybindings */ + virtual int OnInit (int argc, char* argv[]); + /** \brief Registers the commands of the cnotroller */ + virtual void OnRegisterCommands (); +}; + +} +#endif // _CONTROLLER_H diff --git a/asteroids/ControllerCommands.cc b/asteroids/ControllerCommands.cc new file mode 100644 index 0000000..01464bd --- /dev/null +++ b/asteroids/ControllerCommands.cc @@ -0,0 +1,217 @@ +// #include "Engine.h" + +#include "Controller.h" +#include "Model.h" +#include "EntityBase.h" +#include "EventsBase.h" + +#include "Controller.h" +#include "ShipEntity.h" + +#include "AsteroidsEvents.h" + +namespace asteroids { + +static Controller *ControllerInstance = NULL; + +/* +forward */ +bool Cmd_ControllerForwardDown (std::vector args) { + assert (ControllerInstance); + + Engine::EntityBase* player_entity = Engine::GetEntity (Engine::GetPlayerEntityId()); + if (player_entity) { + player_entity->SetControllerKeyState (EntityKeyStateForward); + + Engine::EventBasePtr forward_event (new Engine::EventBase()); + forward_event->mEventType = EventAccelerateStart; + QueueEvent (forward_event); + + return true; + } + + return false; +} + +/* -forward */ +bool Cmd_ControllerForwardUp (std::vector args) { + assert (ControllerInstance); + + Engine::EntityBase* player_entity = Engine::GetEntity (Engine::GetPlayerEntityId()); + if (player_entity) { + player_entity->UnsetControllerKeyState (EntityKeyStateForward); + + Engine::EventBasePtr forward_event (new Engine::EventBase()); + forward_event->mEventType = EventAccelerateStop; + QueueEvent (forward_event); + + return true; + } + + return false; +} + +/* +back */ +bool Cmd_ControllerBackDown (std::vector args) { + assert (ControllerInstance); + + Engine::EntityBase* player_entity = Engine::GetEntity (Engine::GetPlayerEntityId()); + if (player_entity) { + player_entity->SetControllerKeyState (EntityKeyStateBack); + return true; + } + + return false; +} + +/* -back */ +bool Cmd_ControllerBackUp (std::vector args) { + assert (ControllerInstance); + + Engine::EntityBase* player_entity = Engine::GetEntity (Engine::GetPlayerEntityId()); + if (player_entity) { + player_entity->UnsetControllerKeyState (EntityKeyStateBack); + return true; + } + + return false; +} + +/* +left */ +bool Cmd_ControllerLeftDown (std::vector args) { + assert (ControllerInstance); + + Engine::EntityBase* player_entity = Engine::GetEntity (Engine::GetPlayerEntityId()); + if (player_entity) { + player_entity->SetControllerKeyState (EntityKeyStateLeft); + return true; + } + + return false; +} + +/* -left */ +bool Cmd_ControllerLeftUp (std::vector args) { + assert (ControllerInstance); + + Engine::EntityBase* player_entity = Engine::GetEntity (Engine::GetPlayerEntityId()); + if (player_entity) { + player_entity->UnsetControllerKeyState (EntityKeyStateLeft); + return true; + } + + return false; +} + +/* +right */ +bool Cmd_ControllerRightDown (std::vector args) { + assert (ControllerInstance); + + Engine::EntityBase* player_entity = Engine::GetEntity (Engine::GetPlayerEntityId()); + if (player_entity) { + player_entity->SetControllerKeyState (EntityKeyStateRight); + return true; + } + + return false; +} + +/* -right */ +bool Cmd_ControllerRightUp (std::vector args) { + assert (ControllerInstance); + + Engine::EntityBase* player_entity = Engine::GetEntity (Engine::GetPlayerEntityId()); + if (player_entity) { + player_entity->UnsetControllerKeyState (EntityKeyStateRight); + return true; + } + + return false; +} + +/* +turnleft */ +bool Cmd_ControllerTurnLeftDown (std::vector args) { + assert (ControllerInstance); + + Engine::EntityBase* player_entity = Engine::GetEntity (Engine::GetPlayerEntityId()); + if (player_entity) { + player_entity->SetControllerKeyState (EntityKeyStateTurnLeft); + return true; + } + + return false; +} + +/* -turnleft */ +bool Cmd_ControllerTurnLeftUp (std::vector args) { + assert (ControllerInstance); + + Engine::EntityBase* player_entity = Engine::GetEntity (Engine::GetPlayerEntityId()); + if (player_entity) { + player_entity->UnsetControllerKeyState (EntityKeyStateTurnLeft); + return true; + } + + return false; +} + +/* +turnright */ +bool Cmd_ControllerTurnRightDown (std::vector args) { + assert (ControllerInstance); + + Engine::EntityBase* player_entity = Engine::GetEntity (Engine::GetPlayerEntityId()); + if (player_entity) { + player_entity->SetControllerKeyState (EntityKeyStateTurnRight); + return true; + } + + return false; +} + +/* -turnright */ +bool Cmd_ControllerTurnRightUp (std::vector args) { + assert (ControllerInstance); + + Engine::EntityBase* player_entity = Engine::GetEntity (Engine::GetPlayerEntityId()); + if (player_entity) { + player_entity->UnsetControllerKeyState (EntityKeyStateTurnRight); + return true; + } + + return false; +} + +/* attack */ +bool Cmd_ControllerAttack (std::vector args) { + assert (ControllerInstance); + + ShipEntity* player_entity = (ShipEntity*) Engine::GetEntity (Engine::GetPlayerEntityId ()); + assert (player_entity); + player_entity->Attack (); + + return true; +} + +void Controller::OnRegisterCommands () { + ControllerBase::OnRegisterCommands (); + + ControllerInstance = this; + + Engine::AddCommand ("+forward", Cmd_ControllerForwardDown); + Engine::AddCommand ("-forward", Cmd_ControllerForwardUp); + Engine::AddCommand ("+back", Cmd_ControllerBackDown); + Engine::AddCommand ("-back", Cmd_ControllerBackUp); + + Engine::AddCommand ("+left", Cmd_ControllerLeftDown); + Engine::AddCommand ("-left", Cmd_ControllerLeftUp); + Engine::AddCommand ("+right", Cmd_ControllerRightDown); + Engine::AddCommand ("-right", Cmd_ControllerRightUp); + + Engine::AddCommand ("+turnleft", Cmd_ControllerTurnLeftDown); + Engine::AddCommand ("-turnleft", Cmd_ControllerTurnLeftUp); + Engine::AddCommand ("+turnright", Cmd_ControllerTurnRightDown); + Engine::AddCommand ("-turnright", Cmd_ControllerTurnRightUp); + + Engine::AddCommand ("attack", Cmd_ControllerAttack); +} + +} diff --git a/asteroids/EntityFactory.cc b/asteroids/EntityFactory.cc new file mode 100644 index 0000000..3b5e165 --- /dev/null +++ b/asteroids/EntityFactory.cc @@ -0,0 +1,139 @@ +#include + +#include "EntityBase.h" +#include "coll2d.h" + +#include "EntityFactory.h" +#include "ShipEntity.h" +#include "RocketEntity.h" +#include "AsteroidEntity.h" + +namespace asteroids { + +int EntityFactory::OnInit (int argc, char* argv[]) { + Engine::LogMessage ("Initializing EntityFactory"); + + return 0; +} + +Engine::EntityPhysicState* EntityFactory::CreateEntityPhysicState (int type) { + // In this simple factory the type is simply seen as the EntityBaseType. + // However to prevent errors we do a simple check vor validity. + if (type < 0 || type > GameEntityTypeLast ) { + Engine::LogError ("Cannot create Entity with type %d: invalid type!", type); + assert (0); + } + + // Create the Entity + Engine::EntityPhysicState* entity_physics = NULL; + + // type specific initialization + if (type == GameEntityTypeShip) { + entity_physics = new ShipEntityPhysicState (); + if (!entity_physics) { + Engine::LogError ("Could not allocate enough memory for EntityPhysicState of type '%d'", type); + assert (0); + } + entity_physics->mRadius = 0.5; + entity_physics->mShape = new coll2d::Sphere (entity_physics->mRadius); + + assert (entity_physics->mShape); + } else if (type == GameEntityTypeAsteroid) { + entity_physics = new AsteroidEntityPhysicState (); + if (!entity_physics) { + Engine::LogError ("Could not allocate enough memory for EntityPhysicState of type '%d'", type); + assert (0); + } + + entity_physics->mRadius = 1.; + entity_physics->mShape = new coll2d::Sphere (entity_physics->mRadius); + + assert (entity_physics->mShape); + } else if (type == GameEntityTypeRocket) { + entity_physics = new RocketEntityPhysicState(); + if (!entity_physics) { + Engine::LogError ("Could not allocate enough memory for EntityPhysicState of type '%d'", type); + assert (0); + } + entity_physics->mRadius = 0.1; + entity_physics->mShape = new coll2d::Sphere (entity_physics->mRadius); + } else if (type == GameEntityTypeShipPart) { + entity_physics = new RocketEntityPhysicState(); + entity_physics->mBaseType = Engine::EntityBaseTypeBlock; + if (!entity_physics) { + Engine::LogError ("Could not allocate enough memory for EntityPhysicState of type '%d'", type); + assert (0); + } + entity_physics->mRadius = 0.1; + entity_physics->mShape = new coll2d::Sphere (entity_physics->mRadius); + } else { + Engine::LogError ("No EntityPhysicState defined for GameEntity type '%d'", type); + assert (0); + } + + entity_physics->mType = type; + + return entity_physics; +} + +Engine::EntityControllerState* EntityFactory::CreateEntityControllerState (int type) { + // In this simple factory the type is simply seen as the EntityBaseType. + // However to prevent errors we do a simple check vor validity. + if (type < 0 || type >> Engine::EntityBaseTypeLast ) { + Engine::LogError ("Cannot create Entity with type %d: invalid type!", type); + assert (0); + } + + // Create the Entity + Engine::EntityControllerState* entity_controller = NULL; + + // specific values for each type + if (type == GameEntityTypeShip) { + entity_controller = new Engine::EntityControllerState (); + if (!entity_controller) { + Engine::LogError ("Could not allocate enough memory for EntityControllerState of type '%d'", type); + assert (0); + } + } + + return entity_controller; +} + +Engine::EntityBase* EntityFactory::CreateEntity (int type) { + // In this simple factory the type is simply seen as the EntityBaseType. + // However to prevent errors we do a simple check vor validity. + if (type < 0 || type > GameEntityTypeLast ) { + Engine::LogError ("Cannot create Entity with type %d: invalid type!", type); + assert (0); + } + + // Create the Entity + Engine::EntityBase *entity; + + if (type == GameEntityTypeShip) { + entity = new ShipEntity; + } else if (type == GameEntityTypeAsteroid) { + entity = new AsteroidEntity; + } else if (type == GameEntityTypeRocket) { + entity = new RocketEntity; + } else if (type == GameEntityTypeShipPart) { + entity = new Engine::EntityBase; + entity->mBaseType = Engine::EntityBaseTypeBlock; + } + + entity->mType = type; + + if (!entity) { + Engine::LogError ("Could not allocate enough memory for EntityVisualState of type '%d'", type); + assert (0); + } + + entity->mPhysicState = CreateEntityPhysicState (type); + entity->mControllerState = CreateEntityControllerState (type); + + return entity; +} + +} + + diff --git a/asteroids/EntityFactory.h b/asteroids/EntityFactory.h new file mode 100644 index 0000000..daa5a9b --- /dev/null +++ b/asteroids/EntityFactory.h @@ -0,0 +1,29 @@ +#ifndef _ENTITYFACTORY_H +#define _ENTITYFACTORY_H + +#include "EntityFactoryBase.h" +#include "AsteroidsEnums.h" + +namespace Engine { + struct EntityBase; + struct EntityPhysicState; + struct EntityControllerState; +} + +namespace asteroids { + + class EntityFactory: public Engine::EntityFactoryBase { + public: + EntityFactory () {}; + + virtual Engine::EntityPhysicState* CreateEntityPhysicState (int type); + virtual Engine::EntityControllerState* CreateEntityControllerState (int type); + + virtual Engine::EntityBase* CreateEntity (int type); + + protected: + virtual int OnInit (int argc, char *argv[]); + }; +} + +#endif // _ENTITYFACTORY_H diff --git a/asteroids/EnumToString.cc b/asteroids/EnumToString.cc new file mode 100644 index 0000000..36d2663 --- /dev/null +++ b/asteroids/EnumToString.cc @@ -0,0 +1,7 @@ +#include "AsteroidsEnums.h" + +#define GENERATE_ENUM_STRINGS + +#include "AsteroidsEnums.h" + +#undef GENERATE_ENUM_STRINGS diff --git a/asteroids/MenuOverlay.cc b/asteroids/MenuOverlay.cc new file mode 100644 index 0000000..95bb046 --- /dev/null +++ b/asteroids/MenuOverlay.cc @@ -0,0 +1,176 @@ +#include "OGLFT.h" + +#include +#include + +#include "DrawingsGL.h" + +#include "OverlayBase.h" +#include "MenuOverlay.h" +#include "Model.h" +#include "Sprite.h" +#include "ShipEntity.h" + +#include "Engine.h" + +namespace asteroids { + +// static float left = 0; +static float right = 0; +// static float top = 0; +static float bottom = 0; + +void MenuOverlay::Init () { + if (!mShipSprite.LoadFromPNG("./data/textures/ship.png")) + Engine::LogError ("Could not load ship sprite!"); + + assert (mShipSprite.GetWidth() > 1); + mShipSprite.SetScale (0.1); +} + +bool MenuOverlay::OnKeyDown (const SDL_keysym &keysym) { + if (mModel->GetGameState() == GameStateLevelComplete) { + switch (keysym.sym) { + case SDLK_RETURN: + mModel->SetGameState(GameStateRunning); + break; + default: + break; + } + return true; + } else if (mModel->GetGameState() == GameStateRunning) { + switch (keysym.sym) { + case SDLK_ESCAPE: + mModel->SetGameState(GameStatePaused); + return true; + default: + break; + } + + return false; + } else if (mModel->GetGameState() == GameStateGameOver) { + switch (keysym.sym) { + case SDLK_ESCAPE: + mModel->SetGameState(GameStateMainMenu); + break; + case SDLK_RETURN: + mModel->SetGameState(GameStateMainMenu); + break; + default: + break; + } + + return true; + } + else if (mModel->GetGameState() == GameStateMainMenu + || mModel->GetGameState() == GameStatePaused) { + switch (keysym.sym) { + case SDLK_ESCAPE: + Engine::RunCommand ("quit"); + return true; + case SDLK_RETURN: + mModel->SetGameState(GameStateRunning); + return true; + default: + return true; + } + } else if (mModel->GetGameState() == GameStatePlayerDied) { + switch (keysym.sym) { + case SDLK_RETURN: + mModel->SetGameState(GameStateRunning); + break; + default: + break; + } + return true; + } + + return false; +} + +void MenuOverlay::Draw () { + glClearColor (0.1, 0.1, 0.1, 1.); + + right = static_cast (Engine::GetWindowWidth()); + bottom = static_cast (Engine::GetWindowHeight()); + + // we switch to orthographic projection and draw the contents of the 2d + // overlay on top of the previous drawings + glMatrixMode (GL_PROJECTION); + glPushMatrix (); + glLoadIdentity (); + + // first we have to get the size of the current viewport to set up the + // orthographic projection correctly + GLint viewport[4]; + glGetIntegerv(GL_VIEWPORT, viewport); + gluOrtho2D (viewport[0], viewport[2], viewport[3], viewport[1]); + + glMatrixMode (GL_MODELVIEW); + glPushMatrix (); + glLoadIdentity (); + + // then we do the drawings + if (mModel->GetGameState() == GameStateRunning) { + glClearColor (0., 0., 0., 1.); + DrawGameRunning(); + } else if (mModel->GetGameState() == GameStateGameOver) + DrawGameOverScreen (); + else if (mModel->GetGameState() == GameStateMainMenu + || mModel->GetGameState() == GameStatePaused) + DrawGameMenu (); + else if (mModel->GetGameState() == GameStateLevelComplete) + DrawGameLevelComplete (); + else if (mModel->GetGameState() == GameStatePlayerDied) + DrawPlayerDied(); + + glPopMatrix (); + + glMatrixMode (GL_PROJECTION); + glPopMatrix (); + + glMatrixMode (GL_MODELVIEW); + +}; + +void MenuOverlay::DrawGameRunning() { + right = static_cast (Engine::GetWindowWidth()); + bottom = static_cast (Engine::GetWindowHeight()); + + int i; + for (i = 0; i < mModel->GetPlayerLives(); i++) { + mShipSprite.DrawAt2D (right - 32 - i*20, bottom - 16); + } +} + +void MenuOverlay::DrawPlayerDied () { + std::ostringstream topbar_stream; + topbar_stream << "You died ..."; + Engine::DrawGLString ( right * 0.5 - 80, bottom * 0.5 - 8, topbar_stream.str().c_str ()); +} + +void MenuOverlay::DrawGameOverScreen() { + std::ostringstream topbar_stream; + topbar_stream << "That was pathetic! "; + Engine::DrawGLString ( right * 0.5 - 80, bottom * 0.5 - 8, topbar_stream.str().c_str ()); +} + +void MenuOverlay::DrawGameMenu() { + Engine::DrawGLString ( right * 0.5 - 100, bottom * 0.5 - 8 - 64, "A s t e r o i d s"); + Engine::DrawGLString ( right * 0.5 - 100, bottom * 0.5 - 8 - 32, "Main Menu"); + + if (mModel->GetGameState() == GameStatePaused) + Engine::DrawGLString ( right * 0.5 - 80, bottom * 0.5 - 8 - 16, "[Return] - Resume Game"); + else + Engine::DrawGLString ( right * 0.5 - 80, bottom * 0.5 - 8 - 16, "[Return] - Start Game"); + + Engine::DrawGLString ( right * 0.5 - 80, bottom * 0.5 - 8, "[Escape] - Quit"); +} + +void MenuOverlay::DrawGameLevelComplete() { + Engine::DrawGLString ( right * 0.5 - 80, bottom * 0.5 - 8 - 16, "Congratulations - You rock!"); + Engine::DrawGLString ( right * 0.5 - 80, bottom * 0.5 - 8, "[Return] - Next level ..."); +} + + +} diff --git a/asteroids/MenuOverlay.h b/asteroids/MenuOverlay.h new file mode 100644 index 0000000..2f4cd27 --- /dev/null +++ b/asteroids/MenuOverlay.h @@ -0,0 +1,41 @@ +#ifndef MENUOVERLAY +#define MENUOVERLAY + +namespace Engine { +class OverlayBase; +} + +#include "OverlayBase.h" +#include "Sprite.h" + +namespace asteroids { + +class Model; + +class MenuOverlay : public Engine::OverlayBase { + public: + MenuOverlay () { + }; + void Init (); + virtual ~MenuOverlay() {}; + + virtual bool OnKeyDown (const SDL_keysym &keysym); + virtual void Draw (); + + void DrawGameOverScreen (); + void DrawGameMenu(); + void DrawGameLevelComplete (); + void DrawGamePaused (); + void DrawGameRunning (); + void DrawPlayerDied (); + + void SetModel (Model *model) { mModel = model; }; + + private: + Model *mModel; + Engine::Sprite mShipSprite; +}; + +} + +#endif /* MENUOVERLAY */ diff --git a/asteroids/Model.cc b/asteroids/Model.cc new file mode 100644 index 0000000..e5d2765 --- /dev/null +++ b/asteroids/Model.cc @@ -0,0 +1,213 @@ +#include +#include + +#include "Model.h" +#include "Physics.h" +#include "PhysicsBase.h" + +#include "EntityFactory.h" + +#include + +namespace asteroids { + +static Model* ModelInstance = NULL; + +/* + * Inherited Module functions + */ +int Model::OnInit (int argc, char* argv[]) { + int result = Engine::ModelBase::OnInit (argc, argv); + + ModelInstance = this; + + mGameState = GameStateMainMenu; + mLastGameState = GameStateMainMenu; + + Engine::LogMessage ("Model Initialization!"); + + return result; +} + +void Model::Process () { + if (mLastGameState == mGameState) { + if (mGameState == GameStateRunning) { + Engine::ModelBase::Process(); + } + return; + } + + // when we are here we know that something has changed so we need to take + // some action. + Engine::LogDebug ("Switching from %s->%s", GetStringGameState(mLastGameState), GetStringGameState(mGameState)); + + if (mLastGameState == GameStateMainMenu && mGameState == GameStateRunning) { + mPlayerLives = 3; + mLevel = 1; + DoLoadLevel ("./data/levels/default.txt"); + } + else if (mLastGameState == GameStateRunning && mGameState == GameStatePlayerDied) { + mPlayerLives --; + + ClearEntities(); + + if (mPlayerLives == 0) + mGameState = GameStateGameOver; + } + else if (mLastGameState == GameStateLevelComplete && mGameState == GameStateRunning) + DoLoadLevel ("./data/levels/default.txt"); + else if (mLastGameState == GameStatePlayerDied && mGameState == GameStateRunning) + DoLoadLevel ("./data/levels/default.txt"); + else if (mLastGameState == GameStateRunning && mGameState == GameStateGameOver) + ClearEntities(); + + // ... and we have to set the last game state to the current gamestate + // otherwise we end up in an infinit loop of performing the switching + // action. + mLastGameState = mGameState; +} + +int Model::DoLoadLevel (const char* filename) { + Engine::LogMessage ("Loading level from %s", filename); + std::fstream level_file (filename, std::ios::in); + + if (!level_file) { + Engine::LogError ("Unable to open file %s for writing!", filename); + exit (-1); + } + + ClearEntities(); + mAsteroids.clear(); + + std::string entity_type_str; + + int entity_count = 0; + + while (level_file >> entity_type_str) { + if (entity_type_str[0] == '#') { + std::cout << "Read Comment: " << entity_type_str; + getline (level_file, entity_type_str); + std::cout << entity_type_str << std::endl; + continue; + } + + GameEntityType entity_type = GameEntityTypeUnknown; + + if (entity_type_str == "GameEntityTypeShip") + entity_type = GameEntityTypeShip; + else if (entity_type_str == "GameEntityTypeAsteroid") + entity_type = GameEntityTypeAsteroid; + else { + Engine::LogError ("Unknown Entity type: %s", entity_type_str.c_str()); + exit (-1); + } + + Engine::EntityBase* entity = CreateEntity (entity_type); + + bool is_player; + level_file >> is_player; + + if (is_player) + mPlayerEntityId = entity->mId; + + level_file >> entity->mPhysicState->mPosition[0]; + level_file >> entity->mPhysicState->mPosition[1]; + level_file >> entity->mPhysicState->mPosition[2]; + + level_file >> entity->mPhysicState->mOrientation[0]; + level_file >> entity->mPhysicState->mOrientation[1]; + level_file >> entity->mPhysicState->mOrientation[2]; + + level_file >> entity->mPhysicState->mVelocity[0]; + level_file >> entity->mPhysicState->mVelocity[1]; + level_file >> entity->mPhysicState->mVelocity[2]; + + level_file >> entity->mPhysicState->mAngleVelocity; + + entity_count ++; + } + + level_file.close(); + + Engine::LogDebug ("%d Entities loaded!", mEntities.size()); + + return 0; +} + +int Model::DoSaveLevel (const char* filename) { + Engine::LogMessage ("Saving level to %s", filename); + std::fstream level_file (filename, std::ios::out); + + if (!level_file) { + Engine::LogError ("Unable to open file %s for writing!", filename); + exit (-1); + } + + level_file << "# Format" << std::endl; + level_file << "# " << std::endl; + + std::map::iterator iter = mEntities.begin(); + unsigned int player_id = GetPlayerEntityId(); + + for (iter = mEntities.begin(); iter != mEntities.end(); iter++) { + Engine::EntityBase* game_entity = iter->second; + + level_file << GetStringGameEntityType((GameEntityType)game_entity->mType) << "\t" + // this stores the player id + << (game_entity->mId == player_id) << "\t" + << game_entity->mPhysicState->mPosition[0] << "\t" + << game_entity->mPhysicState->mPosition[1] << "\t" + << game_entity->mPhysicState->mPosition[2] << "\t" + << game_entity->mPhysicState->mOrientation[0] << "\t" + << game_entity->mPhysicState->mOrientation[1] << "\t" + << game_entity->mPhysicState->mOrientation[2] << "\t" + << game_entity->mPhysicState->mVelocity[0] << "\t" + << game_entity->mPhysicState->mVelocity[1] << "\t" + << game_entity->mPhysicState->mVelocity[2] << "\t" + << game_entity->mPhysicState->mAngleVelocity << "\t" + << std::endl; + } + + level_file.close(); + return 0; +} + +void Model::OnCreateEntity (const int type, const unsigned int id) { + GameEntityType entity_type = (GameEntityType) type; + + if (entity_type == GameEntityTypeAsteroid) { + mAsteroids.push_back (id); + } +} + +void Model::OnKillEntity (const Engine::EntityBase *entity) { + GameEntityType entity_type = (GameEntityType) entity->mType; + + if (entity_type == GameEntityTypeAsteroid) { + unsigned int i; + + for (i = 0; i < mAsteroids.size(); i++) { + if (mAsteroids.at(i) == entity->mId) { + std::vector::iterator entity_iter = mAsteroids.begin() + i; + mAsteroids.erase (entity_iter); + break; + } + } + + if (mAsteroids.size() == 0) { + SetGameState (GameStateLevelComplete); + } + } +} + +float Model::GetWorldWidth () { + return static_cast(mPhysics)->GetWorldWidth(); +} + +float Model::GetWorldHeight () { + return static_cast(mPhysics)->GetWorldHeight(); +} + +} + + diff --git a/asteroids/Model.h b/asteroids/Model.h new file mode 100644 index 0000000..45c83ce --- /dev/null +++ b/asteroids/Model.h @@ -0,0 +1,49 @@ +#ifndef _MODEL_H +#define _MODEL_H + +#include "ModelBase.h" +#include "AsteroidsEnums.h" + +namespace asteroids { + +class Model : public Engine::ModelBase { + public: + virtual void Process(); + int DoLoadLevel (const char* filename); + int DoSaveLevel (const char* filename); + + void SetGameState (const GameState &state) { + mLastGameState = mGameState; + mGameState = state; + }; + GameState GetGameState () { return mGameState; }; + + int GetPlayerLives () { return mPlayerLives; }; + + float GetWorldWidth (); + float GetWorldHeight (); + + protected: + /** \brief Initializes the system */ + virtual int OnInit (int argc, char* argv[]); + virtual void OnRegisterCommands (); + + virtual void OnCreateEntity (const int type, const unsigned int id); + virtual void OnKillEntity (const Engine::EntityBase *entity); + + private: + /** \brief Keeps a list of all asteroids */ + std::vector mAsteroids; + + GameState mGameState; + GameState mLastGameState; + + int mPlayerLives; + int mLevel; + + friend class View; +}; + +} + +#endif // _MODEL_H diff --git a/asteroids/ModelCommands.cc b/asteroids/ModelCommands.cc new file mode 100644 index 0000000..c539e3d --- /dev/null +++ b/asteroids/ModelCommands.cc @@ -0,0 +1,48 @@ +#include "Model.h" + +namespace asteroids { + +static Model *ModelInstance = NULL; + +bool Cmd_SaveLevel (const std::vector args) { + assert (ModelInstance); + + if (args.size() != 1) { + Engine::CommandSetErrorString ("usage: savelevel "); + return false; + } + + if (ModelInstance->DoSaveLevel (args[0].c_str()) > 0) + return true; + + // ToDo: Maybe some error output? + + return false; +} + +bool Cmd_LoadLevel (const std::vector args) { + assert (ModelInstance); + + if (args.size() != 1) { + Engine::CommandSetErrorString ("usage: loadlevel "); + return false; + } + + if (ModelInstance->DoLoadLevel (args[0].c_str()) > 0) + return true; + + // ToDo: Maybe some error output? + + return false; +} + +void Model::OnRegisterCommands () { + ModelInstance = this; + + Engine::ModelBase::OnRegisterCommands (); + + Engine::AddCommand ("savelevel", Cmd_SaveLevel); + Engine::AddCommand ("loadlevel", Cmd_LoadLevel); +} + +} diff --git a/asteroids/Physics.cc b/asteroids/Physics.cc new file mode 100644 index 0000000..620edc5 --- /dev/null +++ b/asteroids/Physics.cc @@ -0,0 +1,58 @@ +#include +#include + +#include "Model.h" +#include "Physics.h" + +#include "EntityFactory.h" + +#include + +namespace asteroids { + +static Physics* PhysicsInstance = NULL; + +/* + * Inherited Module functions + */ +int Physics::OnInit (int argc, char* argv[]) { + Engine::PhysicsBase::OnInit (argc, argv); + + Engine::LogMessage ("Physics Initialization!"); + + PhysicsInstance = this; + + mWorldWidth = 16; + mWorldHeight = 16; + + return 0; +} + +int Physics::Simulate (float msec, Engine::ModelBase *model) { + int result = Engine::PhysicsBase::Simulate (msec, model); + + Engine::EntityPhysicState* entity = NULL; + std::map::iterator entity_iter; + + for (entity_iter = mEntities.begin (); + entity_iter != mEntities.end(); + entity_iter++) { + entity = entity_iter->second; + + if (entity->mPosition[0] > mWorldWidth * 0.5) + entity->mPosition[0] -= mWorldWidth; + if (entity->mPosition[0] < - mWorldWidth * 0.5) + entity->mPosition[0] += mWorldWidth; + + if (entity->mPosition[2] > mWorldHeight * 0.5) + entity->mPosition[2] -= mWorldHeight; + if (entity->mPosition[2] < - mWorldHeight * 0.5) + entity->mPosition[2] += mWorldHeight; + } + + return result; +} + +} + + diff --git a/asteroids/Physics.h b/asteroids/Physics.h new file mode 100644 index 0000000..c33fcab --- /dev/null +++ b/asteroids/Physics.h @@ -0,0 +1,34 @@ +#ifndef _PHYSICS_H +#define _PHYSICS_H + +#include "PhysicsBase.h" + +namespace Engine { + class Model; +} + +namespace asteroids { + +class Physics : public Engine::PhysicsBase { + public: + virtual int Simulate (float msec, Engine::ModelBase* model = NULL); + + void SetWorldSize (float width, float height) { + mWorldWidth = width; + mWorldHeight = height; + } + + float GetWorldWidth () { return mWorldWidth; } + float GetWorldHeight () { return mWorldHeight; } + + protected: + virtual int OnInit (int argc, char* argv[]); + + private: + float mWorldWidth; + float mWorldHeight; +}; + +} + +#endif // _PHYSICS_H diff --git a/asteroids/RocketEntity.cc b/asteroids/RocketEntity.cc new file mode 100644 index 0000000..8cd835c --- /dev/null +++ b/asteroids/RocketEntity.cc @@ -0,0 +1,17 @@ +#include "Model.h" + +#include "RocketEntity.h" +#include "Controller.h" + +#include + +namespace asteroids { + +void RocketEntity::Update (float delta_sec) { + mSecToLive -= delta_sec; + + if (mSecToLive <= 0.) + Engine::KillEntity (mId); +} + +} diff --git a/asteroids/RocketEntity.h b/asteroids/RocketEntity.h new file mode 100644 index 0000000..4adcb76 --- /dev/null +++ b/asteroids/RocketEntity.h @@ -0,0 +1,39 @@ +#ifndef _ROCKETENTITY_H +#define _ROCKETENTITY_H + +#include "EntityBase.h" +#include "AsteroidsEnums.h" + +#include "Sprite.h" + +namespace asteroids { + +struct RocketEntityPhysicState : public Engine::EntityPhysicState { + RocketEntityPhysicState () { + mBaseType = Engine::EntityBaseTypeParticle; + mType = GameEntityTypeRocket; + } + virtual ~RocketEntityPhysicState() {}; +}; + +struct RocketEntity: public Engine::EntityBase { + RocketEntity () { + mBaseType = Engine::EntityBaseTypeParticle; + mType = GameEntityTypeRocket; + + mSecToLive = 3.; + } + virtual ~RocketEntity() {}; + + virtual void Update (float delta_sec); + virtual bool CollisionEvent (Engine::EntityBase *entity) { + Engine::LogMessage ("Rocket BOOM"); + return false; + } + + float mSecToLive; +}; + +} + +#endif // _ROCKETENTITY_H diff --git a/asteroids/ShipEntity.cc b/asteroids/ShipEntity.cc new file mode 100644 index 0000000..ab63b14 --- /dev/null +++ b/asteroids/ShipEntity.cc @@ -0,0 +1,122 @@ +#include "Engine.h" + +#include "Model.h" + +#include "ShipEntity.h" +#include "RocketEntity.h" +#include "Controller.h" +#include "AsteroidsEvents.h" + +#include "coll2d.h" + +namespace asteroids { + +static Engine::Variable var_ship_acceleration ("ship_acceleration", "10"); +static Engine::Variable var_ship_maxspeed ("ship_maxspeed", "10"); +static Engine::Variable var_ship_rotationspeed ("ship_rotationspeed", "180"); + +void ShipEntity::Update (float delta_sec) { + if (!mPhysicState || !mControllerState) + return; + + // If we die, we have to decrease the fade timer + if (!mAlive) { + mFadeTimer -= delta_sec; + + if (mFadeTimer <= 0.) { + Model *model = (Model*) Engine::EngineGetModel(); + model->SetGameState (GameStatePlayerDied); + } + return; + } + + mState = Idle; + + // the local velocity + vector3d local_velocity = mPhysicState->mVelocity; + mPhysicState->LocalizeRotation (local_velocity); + + // set the local velocity as the current state of the keys are + if (mControllerState->GetKey (EntityKeyStateForward)) { + local_velocity[0] += delta_sec * var_ship_acceleration.GetFloatValue(); + mState = Accelerating; + } + + // now transform these to global velocities + mPhysicState->GlobalizeRotation (local_velocity); + + // now we can update the new global velocity + mPhysicState->SetVelocity(local_velocity); + + if (mControllerState->GetKey (EntityKeyStateTurnLeft)) { + mPhysicState->mOrientation[1] += delta_sec * var_ship_rotationspeed.GetFloatValue(); + } + if (mControllerState->GetKey (EntityKeyStateTurnRight)) { + mPhysicState->mOrientation[1] -= delta_sec * var_ship_rotationspeed.GetFloatValue(); + } + + // Check for the maximum speed + float speed = mPhysicState->mVelocity.length(); + if (speed > var_ship_maxspeed.GetFloatValue()) { + mPhysicState->mVelocity *= var_ship_maxspeed.GetFloatValue() / speed; + } +} + +bool ShipEntity::CollisionEvent (Engine::EntityBase* entity) { + GameEntityType other_type = (GameEntityType) entity->mType; + + if (other_type == GameEntityTypeAsteroid) { + Engine::LogMessage ("You died!"); + + mPhysicState->mStatic = true; + + mAlive = false; + mFadeTimer = 3.; + mState = Dying; + + Engine::EventBasePtr explode_event (new Engine::EventBase()); + explode_event->mEventType = EventShipExplode; + explode_event->mEventUnsignedInt = mId; + QueueEvent (explode_event); + + return true; + } else if (other_type == GameEntityTypeRocket) { + Engine::LogMessage ("You just killed yourself!"); + + mPhysicState->mStatic = true; + + mAlive = false; + mFadeTimer = 1.; + mState = Dying; + + return true; + } + + return false; +} + +void ShipEntity::Attack () { + if (!mAlive) + return; + + Engine::LogMessage ("ATTACK"); + + Engine::EntityPhysicState* entity_physic = Engine::GetEntityPhysicState (mId); + vector3d attack_dir (1., 0., 0.); + + entity_physic->GlobalizeRotation (attack_dir); + + RocketEntity *rocket_entity = (RocketEntity*) Engine::CreateEntity (GameEntityTypeRocket); + + rocket_entity->mSecToLive = 1.75; + + RocketEntityPhysicState *rocket_physics = (RocketEntityPhysicState*) rocket_entity->mPhysicState; + rocket_physics->mPosition = attack_dir; + rocket_physics->mPosition *= mPhysicState->mRadius; + rocket_physics->mPosition += entity_physic->mPosition; + rocket_physics->mOrientation = entity_physic->mOrientation; + rocket_physics->mVelocity = attack_dir.normalize(); + rocket_physics->mVelocity *= var_ship_maxspeed.GetFloatValue() + 0.1; +} + +} diff --git a/asteroids/ShipEntity.h b/asteroids/ShipEntity.h new file mode 100644 index 0000000..02ac7ea --- /dev/null +++ b/asteroids/ShipEntity.h @@ -0,0 +1,58 @@ +#ifndef _SHIPENTITY_H +#define _SHIPENTITY_H + +#include "EntityBase.h" +#include "AsteroidsEnums.h" +#include "Sprite.h" + +namespace asteroids { + +struct ShipEntityPhysicState : public Engine::EntityPhysicState { + ShipEntityPhysicState () { + mType = GameEntityTypeShip; + mBaseType = Engine::EntityBaseTypeActor; + + mAcceleration = 10.; + mMaxSpeed = 10.; + mRotationSpeed = 180.; + } + + virtual ~ShipEntityPhysicState() {}; + + float mAcceleration; + float mMaxSpeed; + float mRotationSpeed; +}; + +struct ShipEntity: public Engine::EntityBase { + enum State { + Idle = 0, + Accelerating, + Rotating, + Shooting, + Dying + }; + + ShipEntity () { + mType = GameEntityTypeShip; + mBaseType = Engine::EntityBaseTypeActor; + + mAlive = true; + mFadeTimer = 0.; + mState = Idle; + } + + virtual ~ShipEntity() {}; + + virtual void Attack (); + virtual void Update (float delta_sec); + virtual bool CollisionEvent (Engine::EntityBase *entity); + + bool mAlive; + float mFadeTimer; + State mState; +}; + +} + +#endif // _SHIPENTITY_H diff --git a/asteroids/View.cc b/asteroids/View.cc new file mode 100644 index 0000000..1e7cb4a --- /dev/null +++ b/asteroids/View.cc @@ -0,0 +1,359 @@ +#include "View.h" +#include "CameraBase.h" +#include "MenuOverlay.h" +#include "SimpleConsoleOverlay.h" + +#include "Engine.h" +#include "Physics.h" +#include "Model.h" +#include "EventsBase.h" + +#include "ShipEntity.h" +#include "AsteroidEntity.h" +#include "AsteroidsEvents.h" +#include "RocketEntity.h" + +#include + +// #define DRAW_BOUNDARIES + +#ifdef DRAW_BOUNDARIES + #include "coll2d.h" + #include "DrawingsGL.h" +#endif + +using namespace std; + +namespace asteroids { + +int View::OnInit (int argc, char* argv[]) { + ViewBase::OnInit (argc, argv); + + // We want menu + mMenuOverlay = new MenuOverlay; + mMenuOverlay->SetModel ((Model*) mModel); + mMenuOverlay->Init(); + AddOverlay (mMenuOverlay); + + // We want the console + Engine::SimpleConsoleOverlay *console = new Engine::SimpleConsoleOverlay; + // We also want to display the log bar + console->SetDrawLogBar (true); + AddOverlay (console); + + // This is a simple star field that makes the game so spacy + int i; + for (i = 0; i < 200; i++) { + BackgroundStar star; + star.position[0] = rand() / float(RAND_MAX); + star.position[1] = rand() / float(RAND_MAX); + star.position[2] = rand() / float(RAND_MAX); + + mBackgroundStars.push_back (star); + } + + mAsteroidSprite.LoadFromPNG ("./data/textures/asteroid.png"); + mShipSprite.LoadFromPNG ("./data/textures/ship.png"); + + mShipThrustSprite.LoadFromPNG ("./data/textures/ship_thrust.png"); + mShipThrustSprite.SetAnimation (4, 8); + + mShipPartsSprite.LoadFromPNG ("./data/textures/ship_parts.png"); + mShipPartsSprite.SetSubSpriteCount (10); + + mAccelerateEventHandler = new AccelerateEventHandler (this); + Engine::RegisterListener (mAccelerateEventHandler, EventAccelerateStart); + Engine::RegisterListener (mAccelerateEventHandler, EventAccelerateStop); + + mShipExplodeEventHandler = new ShipExplodeEventHandler (this); + Engine::RegisterListener (mShipExplodeEventHandler, EventShipExplode); + + return 0; +} + +void View::OnDestroy() { + delete mAccelerateEventHandler; +} + +/* + * Event Handlers + */ +bool View::AccelerateEventHandler::HandleEvent (const Engine::EventBasePtr &event) const { + if (event->mEventType == EventAccelerateStart) + mView->mShipThrustSprite.ResetAnimation(); + + Engine::LogMessage ("Received Acceleration Event: %d", event->mEventType); + return true; +} + +bool View::ShipExplodeEventHandler::HandleEvent (const Engine::EventBasePtr &event) const { + if (event->mEventType == EventShipExplode) { + Engine::EntityBase *ship_entity = Engine::GetEntity (event->mEventUnsignedInt); + vector3d position = ship_entity->mPhysicState->mPosition; + vector3d orientation = ship_entity->mPhysicState->mOrientation; + vector3d velocity = ship_entity->mPhysicState->mVelocity; + + unsigned int i; + mView->mShipPartsEntityIds.clear(); + + for (i = 0; i < mView->mShipPartsSprite.GetSubSpriteCount(); i++) { + Engine::EntityBase* part_sprite_particle = Engine::CreateEntity (GameEntityTypeShipPart); + part_sprite_particle->mPhysicState->mPosition = position; + part_sprite_particle->mPhysicState->mOrientation = orientation; + part_sprite_particle->mPhysicState->mVelocity = velocity; + part_sprite_particle->mPhysicState->mVelocity = vector3d (velocity[0] * (rand()/float(RAND_MAX)) * 1.7, 0., velocity[2] * (rand()/float(RAND_MAX)) * 1.5); + part_sprite_particle->mPhysicState->mAngleVelocity = (rand()/float(RAND_MAX) - 0.5 ) * 100.; + + mView->mShipPartsEntityIds.push_back(part_sprite_particle->mId); + } + } + + Engine::LogMessage ("Received Ship Explode Event: %d", event->mEventType); + return true; +} + +/* + * Module specific functions + */ +void View::UpdateCamera () { + mCamera->SetEye ( + 0., + 9.5, + 0. + ); + mCamera->SetPointOfIntrest ( + 0., + 0., + 0. + ); + mCamera->SetUp ( + 0., + 0., + -1. + ); + + mCamera->Update (); +} + +void View::DrawStars() { + unsigned int i; + + float world_width, world_height; + world_width = static_cast(mModel)->GetWorldWidth(); + world_height = static_cast(mModel)->GetWorldHeight(); + vector3d velocity (1., 0., 0.); + + glPushMatrix(); + glTranslatef(-world_width * 0.5, 0, -world_height * 0.5); + glColor3f (1., 1., 1.); + glPointSize(2.); + glBegin(GL_POINTS); + float z_value; + for (i = 0; i < mBackgroundStars.size(); i++) { +// glPointSize (2. + 300. *mBackgroundStars.at(i).position[1]); + z_value = mBackgroundStars.at(i).position[1] + 0.1; + + glColor3f (z_value, z_value, z_value); + glVertex3f (mBackgroundStars.at(i).position[0] * world_width, + -1., + mBackgroundStars.at(i).position[2] * world_height); + + mBackgroundStars.at(i).position -= vector3d(Engine::GetFrameDuration() * 0.7 * mBackgroundStars.at(i).position[1] / world_width, 0., 0.); + + if (mBackgroundStars.at(i).position[0] < 0.) + mBackgroundStars.at(i).position[0] += 1.; + if (mBackgroundStars.at(i).position[0] >= 1.) + mBackgroundStars.at(i).position[0] -= 1.; + } + + glEnd(); + glPopMatrix(); + +} + +void View::DrawWorld() { + std::map::iterator entity_iterator; + + Model *game_model = static_cast (mModel); + + DrawStars (); + + if ( game_model->GetGameState() != GameStateRunning) { + return; + } + + ViewBase::DrawWorld(); + + for (entity_iterator = game_model->mEntities.begin (); + entity_iterator != game_model->mEntities.end(); + entity_iterator++) { + Engine::EntityBase* entity = entity_iterator->second; + + // Perform multiple drawing if the entity is at the border + Physics* game_physics = (Physics*) game_model->mPhysics; + float world_width = game_physics->GetWorldWidth(); + float world_height = game_physics->GetWorldHeight(); + + // Drawing at the original position: + glPushMatrix (); + glTranslatef (entity->mPhysicState->mPosition[0], + entity->mPhysicState->mPosition[1], + entity->mPhysicState->mPosition[2]); + + glRotatef(entity->mPhysicState->mOrientation[0], 0., 0., 1.); + glRotatef(entity->mPhysicState->mOrientation[1], 0., 1., 0.); + glRotatef(entity->mPhysicState->mOrientation[2], 1., 0., 0.); + + glColor3f (1., 1., 1.); + DrawEntity (entity); + + glPopMatrix (); + + // If we move out the right side + if (entity->mPhysicState->mPosition[0] + entity->mPhysicState->mRadius * 2 + >= world_width * 0.5) { + + glPushMatrix (); + glTranslatef (entity->mPhysicState->mPosition[0] - world_width, + entity->mPhysicState->mPosition[1], + entity->mPhysicState->mPosition[2]); + + glRotatef(entity->mPhysicState->mOrientation[0], 0., 0., 1.); + glRotatef(entity->mPhysicState->mOrientation[1], 0., 1., 0.); + glRotatef(entity->mPhysicState->mOrientation[2], 1., 0., 0.); + + glColor3f (1., 1., 1.); + DrawEntity (entity); + + glPopMatrix (); + } + + // if we move out the left side + if (entity->mPhysicState->mPosition[0] - entity->mPhysicState->mRadius * 2 + < - world_width * 0.5) { + glPushMatrix (); + glTranslatef (entity->mPhysicState->mPosition[0] + world_width, + entity->mPhysicState->mPosition[1], + entity->mPhysicState->mPosition[2]); + + glRotatef(entity->mPhysicState->mOrientation[0], 0., 0., 1.); + glRotatef(entity->mPhysicState->mOrientation[1], 0., 1., 0.); + glRotatef(entity->mPhysicState->mOrientation[2], 1., 0., 0.); + + glColor3f (1., 1., 1.); + DrawEntity (entity); + + glPopMatrix (); + } + + // If we move out the bottom side + if (entity->mPhysicState->mPosition[2] + entity->mPhysicState->mRadius * 2 + >= world_height * 0.5) { + + glPushMatrix (); + glTranslatef (entity->mPhysicState->mPosition[0], + entity->mPhysicState->mPosition[1], + entity->mPhysicState->mPosition[2] - world_height); + + glRotatef(entity->mPhysicState->mOrientation[0], 0., 0., 1.); + glRotatef(entity->mPhysicState->mOrientation[1], 0., 1., 0.); + glRotatef(entity->mPhysicState->mOrientation[2], 1., 0., 0.); + + glColor3f (1., 1., 1.); + DrawEntity (entity); + + glPopMatrix (); + } + + // if we move out the left side + if (entity->mPhysicState->mPosition[2] - entity->mPhysicState->mRadius * 2 + < - world_height* 0.5) { + glPushMatrix (); + glTranslatef (entity->mPhysicState->mPosition[0], + entity->mPhysicState->mPosition[1], + entity->mPhysicState->mPosition[2] + world_height); + + glRotatef(entity->mPhysicState->mOrientation[0], 0., 0., 1.); + glRotatef(entity->mPhysicState->mOrientation[1], 0., 1., 0.); + glRotatef(entity->mPhysicState->mOrientation[2], 1., 0., 0.); + + glColor3f (1., 1., 1.); + DrawEntity (entity); + + glPopMatrix (); + } + } + +} + +void View::DrawEntity (Engine::EntityBase *entity) { + if (entity->mType == GameEntityTypeAsteroid) + DrawAsteroid ((AsteroidEntity*) entity); + else if (entity->mType == GameEntityTypeShip) + DrawShip ((ShipEntity*) entity); + else if (entity->mType == GameEntityTypeRocket) + DrawRocket ((RocketEntity*) entity); + else if (entity->mType == GameEntityTypeShipPart) + DrawShipPart (entity); + else { + Engine::LogError ("Cannot draw entity: unknown type '%d'", entity->mType); + } +} + +/// \todo: Update of the animation ?? +void View::DrawShip (ShipEntity *ship) { + if (!ship->mAlive) + return; + + mShipSprite.SetScale (2. * ship->mPhysicState->mRadius / mShipSprite.GetHeight()); + mShipThrustSprite.SetScale (2. * ship->mPhysicState->mRadius / mShipSprite.GetHeight()); + + if (ship->mState == ShipEntity::Accelerating) { + mShipThrustSprite.UpdateAnimation (Engine::GetFrameDuration()); + mShipThrustSprite.DrawAt(-0.5, 0., 0.); + } + + mShipSprite.DrawAt(0., 0., 0.); + +#ifdef DRAW_BOUNDARIES + glColor3f (1., 1., 1.); + DrawCircle (ship->mPhysicState->mRadius, 20); +#endif +} + +void View::DrawAsteroid (AsteroidEntity *asteroid) { + mAsteroidSprite.SetScale (2. * asteroid->mPhysicState->mRadius / mAsteroidSprite.GetWidth()); + mAsteroidSprite.DrawAt(0., 0., 0.); + +#ifdef DRAW_BOUNDARIES + glColor3f (1., 1., 1.); + DrawCircle (asteroid->mPhysicState->mRadius, 20); +#endif +} + +void View::DrawRocket (RocketEntity *rocket) { + glColor3f (1., 1., 1.); + glBegin (GL_QUADS); + glVertex3f (-0.25, 0., 0.05); + glVertex3f (0.05, 0., 0.05); + glVertex3f (0.05, 0., -0.05); + glVertex3f (-0.25, 0., -0.05); + glEnd (); +} + +void View::DrawShipPart (Engine::EntityBase *entity) { + unsigned int i; + mShipPartsSprite.SetScale (1. / mShipSprite.GetHeight()); + + for (i = 0; i < mShipPartsEntityIds.size(); i++) { + if (mShipPartsEntityIds.at(i) == entity->mId) { + mShipPartsSprite.DrawSubAt (i, 0., 0., 0.); + } + } +#ifdef DRAW_BOUNDARIES + glColor3f (1., 1., 1.); + DrawCircle (entity->mPhysicState->mRadius, 20); +#endif +} + +} diff --git a/asteroids/View.h b/asteroids/View.h new file mode 100644 index 0000000..485eae8 --- /dev/null +++ b/asteroids/View.h @@ -0,0 +1,75 @@ +#ifndef _VIEW_H +#define _VIEW_H + +#include "ViewBase.h" +#include "mathlib.h" +#include "Sprite.h" +#include "EntityBase.h" + +namespace asteroids { + +class MenuOverlay; +struct ShipEntity; +struct AsteroidEntity; +struct RocketEntity; + +struct BackgroundStar { + vector3d position; +}; + +/** \brief Performs the actual drawing based on Camera and Model + */ +class View : public Engine::ViewBase { + protected: + /** \brief Initializes the system */ + int OnInit (int argc, char* argv[]); + void OnDestroy (); + + /** \brief Updates the camera for further drawing */ + virtual void UpdateCamera (); + + private: + virtual void DrawWorld (); + void DrawStars (); + + void DrawEntity (Engine::EntityBase *entity); + void DrawShip (ShipEntity *ship); + void DrawAsteroid (AsteroidEntity *asteroid); + void DrawRocket (RocketEntity *asteroid); + void DrawShipPart (Engine::EntityBase *entity); + + MenuOverlay *mMenuOverlay; + std::vector mBackgroundStars; + + std::vector mShipPartsEntityIds; + + Engine::Sprite mAsteroidSprite; + Engine::Sprite mShipSprite; + Engine::Sprite mShipThrustSprite; + Engine::Sprite mShipPartsSprite; + + class AccelerateEventHandler : public Engine::EventListenerBase { + public: + explicit AccelerateEventHandler (View *view) : mView (view) {}; + virtual bool HandleEvent (const Engine::EventBasePtr &event) const; + private: + View *mView; + }; + + class ShipExplodeEventHandler : public Engine::EventListenerBase { + public: + explicit ShipExplodeEventHandler (View *view) : mView (view) {}; + virtual bool HandleEvent (const Engine::EventBasePtr &event) const; + private: + View *mView; + }; + + AccelerateEventHandler *mAccelerateEventHandler; + ShipExplodeEventHandler *mShipExplodeEventHandler; + + friend class AccelerateEventHandler; +}; + +} + +#endif // _VIEW_H diff --git a/asteroids/main.cc b/asteroids/main.cc new file mode 100644 index 0000000..e51ecb0 --- /dev/null +++ b/asteroids/main.cc @@ -0,0 +1,44 @@ +#include +#include + +#include "Engine.h" + +#include "Controller.h" +#include "View.h" +#include "Model.h" +#include "Physics.h" +#include "EntityFactory.h" + +using namespace std; + +int main (int argc, char* argv[]) { + cout << "Game Start" << endl; + + Engine::Engine engine; + + engine.SetEntityFactory (new asteroids::EntityFactory); + engine.SetController (new asteroids::Controller); + engine.SetModel (new asteroids::Model); + engine.SetPhysics (new asteroids::Physics); + engine.SetView (new asteroids::View); + + SetLogPrintLevel (Engine::LogLevelDebug); + + if (engine.Init (argc, argv) != 0) { + cout << "Could not start engine!" << endl; + exit (-1); + } + + engine.GetView()->SetGridSize (8,8); + dynamic_cast(engine.GetPhysics())->SetWorldSize (28, 20); + + SetLogPrintLevel (Engine::LogLevelDebug); + + engine.MainLoop (); + + engine.Destroy (); + + cout << "Game Quit" << endl; + + return 0; +} diff --git a/codingstyle.txt b/codingstyle.txt new file mode 100644 index 0000000..999bf02 --- /dev/null +++ b/codingstyle.txt @@ -0,0 +1,12 @@ +Indent: Tabs! + +class Uppercase { + int OnInit () + void OnDestroy (); + + void SetValue (); + int GetValue (); + + void SomeCoolFunction (); +}; + diff --git a/data/fonts/console.ttf b/data/fonts/console.ttf new file mode 100644 index 0000000..bfff77e Binary files /dev/null and b/data/fonts/console.ttf differ diff --git a/data/levels/default.txt b/data/levels/default.txt new file mode 100644 index 0000000..c673d2a --- /dev/null +++ b/data/levels/default.txt @@ -0,0 +1,5 @@ +# Format +# +GameEntityTypeShip 1 0 0 0 0 90 0 0 0 0 0 +GameEntityTypeAsteroid 0 5 0 -2 0 0 0 -0.2 0 -0.1 -10 +GameEntityTypeAsteroid 0 -2 0 2 0 0 0 0.3 0 0.1 5 diff --git a/data/textures/asteroid.png b/data/textures/asteroid.png new file mode 100644 index 0000000..11c9f9a Binary files /dev/null and b/data/textures/asteroid.png differ diff --git a/data/textures/ship.png b/data/textures/ship.png new file mode 100644 index 0000000..80ca2b9 Binary files /dev/null and b/data/textures/ship.png differ diff --git a/data/textures/ship_parts.png b/data/textures/ship_parts.png new file mode 100644 index 0000000..785e3df Binary files /dev/null and b/data/textures/ship_parts.png differ diff --git a/data/textures/ship_thrust.png b/data/textures/ship_thrust.png new file mode 100644 index 0000000..cb48150 Binary files /dev/null and b/data/textures/ship_thrust.png differ diff --git a/engine/CMake/FindFreeType2.cmake b/engine/CMake/FindFreeType2.cmake new file mode 100644 index 0000000..3abf606 --- /dev/null +++ b/engine/CMake/FindFreeType2.cmake @@ -0,0 +1,28 @@ +# Tries to find FREETYPE2 (http://freetype2.bespin.org) a simple and fast +# network library by Lee Salzman +# + +SET (FREETYPE2_FOUND FALSE) + +FIND_PATH (FREETYPE2_INCLUDE_DIR freetype/freetype.h /usr/include/ /usr/local/include/ /usr/include/freetype2 /usr/local/include/freetype2 $ENV{FREETYPE2_PATH}/include $ENV{FREETYPE2_INCLUDE_PATH}) + +FIND_LIBRARY (FREETYPE2_LIBRARIES NAMES freetype PATHS /usr/lib /usr/local/lib $ENV{FREETYPE2_PATH} $ENV{FREETYPE2_PATH}/lib ENV{FREETYPE2_LIBRARY_PATH}) + +IF (FREETYPE2_INCLUDE_DIR AND FREETYPE2_LIBRARIES) + SET (FREETYPE2_FOUND TRUE) +ENDIF (FREETYPE2_INCLUDE_DIR AND FREETYPE2_LIBRARIES) + +IF (FREETYPE2_FOUND) + IF (NOT FREETYPE2_FIND_QUIETLY) + MESSAGE(STATUS "Found FREETYPE2: ${FREETYPE2_LIBRARIES}") + ENDIF (NOT FREETYPE2_FIND_QUIETLY) +ELSE (FREETYPE2_FOUND) + IF (FREETYPE2_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find FREETYPE2") + ENDIF (FREETYPE2_FIND_REQUIRED) +ENDIF (FREETYPE2_FOUND) + +MARK_AS_ADVANCED ( + FREETYPE2_INCLUDE_DIR + FREETYPE2_LIBRARIES + ) diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt new file mode 100644 index 0000000..c7a3530 --- /dev/null +++ b/engine/CMakeLists.txt @@ -0,0 +1,60 @@ +CMAKE_MINIMUM_REQUIRED (VERSION 2.6) + +LIST( APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMake ) + +FIND_PACKAGE (SDL REQUIRED) +FIND_PACKAGE (OpenGL REQUIRED) +FIND_PACKAGE (PNG REQUIRED) +FIND_PACKAGE (FreeType2 REQUIRED) + +ADD_SUBDIRECTORY ( libraries ) + +SET ( ENGINE_SRCS + CameraBase.cc + ControllerBase.cc + EntityBase.cc + EntityFactoryBase.cc + GameEntityBase.cc + ModelBase.cc + PhysicsBase.cc + PhysicsEntityBase.cc + ViewBase.cc + EventsBase.cc + + Commands.cc + DrawingsGL.cc + EngineCommands.cc + Variables.cc + VariablesCommands.cc + SimpleConsoleOverlay.cc + Sprite.cc + + Engine.cc + Logging.cc + ) + +INCLUDE_DIRECTORIES ( + ${PROJECT_SOURCE_DIR} + libraries/mathlib/ + libraries/coll2d/include + libraries/oglft/ + ${FREETYPE2_INCLUDE_DIR} + ) + +IF ( WIN32 ) + ADD_LIBRARY ( Engine STATIC ${ENGINE_SRCS} ) +ELSE ( WIN32 ) + ADD_LIBRARY ( Engine SHARED ${ENGINE_SRCS} ) +ENDIF ( WIN32 ) + +TARGET_LINK_LIBRARIES ( Engine + ${SDL_LIBRARY} + ${OPENGL_LIBRARIES} + ${PNG_LIBRARIES} + mathlib + oglft + coll2d + ) + +ADD_SUBDIRECTORY ( tests ) + diff --git a/engine/CameraBase.cc b/engine/CameraBase.cc new file mode 100644 index 0000000..5a5c540 --- /dev/null +++ b/engine/CameraBase.cc @@ -0,0 +1,49 @@ +#include "CameraBase.h" + +#include +#include + +namespace Engine { + +/* + * Inherited Module functions + */ +int CameraBase::OnInit (int argc, char* argv[]) { + LogDebug ("Camera Init"); + + mEye[0] = 0.; + mEye[1] = 1.; + mEye[2] = 1.; + + mPointOfIntrest[0] = 0.; + mPointOfIntrest[1] = 0.; + mPointOfIntrest[2] = 0.; + + mUp[0] = 0.; + mUp[1] = 1.; + mUp[2] = 0.; + + mFOVY = 90.; + + return 0; +} + +void CameraBase::OnDestroy () { + LogDebug ("Camera Destroy"); +} + +/* + * Module specific functions + */ +void CameraBase::Update () { + glMatrixMode (GL_MODELVIEW); + + glLoadIdentity (); + gluLookAt(mEye[0], mEye[1], mEye[2], + mPointOfIntrest[0], mPointOfIntrest[1], mPointOfIntrest[2], + mUp[0], mUp[1], mUp[2]); + +} + +} + diff --git a/engine/CameraBase.h b/engine/CameraBase.h new file mode 100644 index 0000000..77d67bd --- /dev/null +++ b/engine/CameraBase.h @@ -0,0 +1,61 @@ +#ifndef _CAMERABASE_H +#define _CAMERABASE_H + +#include "Engine.h" + +namespace Engine { + +class Module; + +/** \brief Controls from where the View is looking to + */ +class CameraBase : public Module { + public: + /** updates the projection and modelview matrices for the camera */ + void Update (); + float GetFOVY () { + return mFOVY; + } + + /** sets the point where the camera is looking to */ + void SetPointOfIntrest (float poi_x, float poi_y, float poi_z) { + mPointOfIntrest[0] = poi_x; + mPointOfIntrest[1] = poi_y; + mPointOfIntrest[2] = poi_z; + } + /** sets the position where the camera is located */ + void SetEye (float eye_x, float eye_y, float eye_z) { + mEye[0] = eye_x; + mEye[1] = eye_y; + mEye[2] = eye_z; + } + /** returns the position of the eye */ + void GetEye (float *eye_out) { + eye_out[0] = mEye[0]; + eye_out[1] = mEye[1]; + eye_out[2] = mEye[2]; + } + + /** sets the up direction of the camera */ + void SetUp (float up_x, float up_y, float up_z) { + mUp[0] = up_x; + mUp[1] = up_y; + mUp[2] = up_z; + } + + protected: + /** \brief Initializes the system */ + int OnInit (int argc, char* argv[]); + /** \brief Destroys the system (must be called!) */ + void OnDestroy (); + + float mPointOfIntrest[3]; + float mEye[3]; + float mUp[3]; + + float mFOVY; +}; + +} + +#endif // _CAMERABASE_H diff --git a/engine/Commands.cc b/engine/Commands.cc new file mode 100644 index 0000000..b2d2f09 --- /dev/null +++ b/engine/Commands.cc @@ -0,0 +1,245 @@ +#include "Commands.h" + +namespace Engine { + +static Commands *CommandsInstance = NULL; + +void trim_command_str (std::string &command_str) { + std::string::size_type start = command_str.find_first_not_of (" \t"); + if (start != std::string::npos) + command_str = command_str.substr (start); + + std::string::size_type end = command_str.find_first_of ("#;\n\r\0"); + + if (end != std::string::npos) + command_str = command_str.substr (0, end); +} + +/* + * Inherited Module functions + */ +int Commands::OnInit (int argc, char* argv[]) { + LogDebug ("Commands Init"); + mErrorString = ""; + if (CommandsInstance) { + LogError ("Commands module already initialized!"); + return -1; + } + + CommandsInstance = this; + + return 0; +} +void Commands::OnDestroy () { + LogDebug ("Commands Destroy"); + + if (CommandsInstance) + CommandsInstance = NULL; +} + +/* + * Module specific functions + */ +std::string Commands::GetCommandName (const std::string &command) { + return command.substr (0, command.find_first_of (" ;\n\0")); +} + +std::vector Commands::ParseArgs (std::string &argument_str) { + std::vector args; + std::string::size_type pos = argument_str.find_first_not_of (" "); + std::string::size_type next_token = 0; + + while ( argument_str.length ()> pos && argument_str.find_first_of (" \"", pos) != std::string::npos) { + next_token = argument_str.find_first_of (" \"", pos); + + if (next_token != std::string::npos && argument_str[next_token] =='"') { + pos = next_token; + next_token = argument_str.find_first_of ('"', pos + 1); + if (next_token != std::string::npos) { + args.push_back (argument_str.substr (pos + 1, next_token - pos - 1)); + } + pos = argument_str.find_first_not_of (" ", next_token + 1); + + } else { + args.push_back (argument_str.substr (pos, next_token - pos)); + pos = argument_str.find_first_not_of (" ", next_token + 1); + } + } + + if (pos != std::string::npos) + args.push_back (argument_str.substr (pos, argument_str.length ())); + + return args; +} + +void Commands::AddCommand (const std::string &name, command_cb callback){ + LogDebug ("Adding Command '%s' at %x", name.c_str(), (void *) callback); + + mCommandsCallbacks[name] = callback; + + return; +} + +bool Commands::RunCommand (const std::string &command){ + LogDebug ("Running Command: %s", command.c_str()); + + std::string cmd_name = GetCommandName (command); + std::string args; + + if (command.length () > cmd_name.length () + 1) + args = command.substr (cmd_name.length () + 1, command.length ()); + + if (mCommandsCallbacks.find (cmd_name) != mCommandsCallbacks.end()) { + std::vector argv = ParseArgs (args); + if (mCommandsCallbacks[cmd_name] (argv)) { + mErrorString = ""; + return true; + } else { + return false; + } + } else { + LogWarning ("Command '%s' not registered!", cmd_name.c_str ()); + } + + SetErrorString ("Command '" + cmd_name + "' does not exist!"); + return false; +} + +void Commands::QueueCommand (const std::string &command){ + mCommandQueue.push (command); +} + +bool Commands::QueueExecute (){ + bool result = true; + + while (mCommandQueue.size() > 0) { + result = RunCommand (mCommandQueue.front()); + if (!result) { + while (!mCommandQueue.empty()) + mCommandQueue.pop(); + + return false; + } + mCommandQueue.pop(); + } + + return true; +} + +void Commands::SetErrorString (const std::string &error_str){ + mErrorString = error_str; + LogWarning ("Command Error: %s", error_str.c_str ()); +} + +std::string Commands::GetErrorString (){ + return mErrorString; +} + + +/* + * Global functions + */ +bool CommandsInitialized () { + if (!CommandsInstance) { + LogError ("Commands System not yet initialized!"); + return false; + } + + return true; +} + +void AddCommand (const std::string &name, command_cb callback){ + if (!CommandsInitialized ()) + return; + + CommandsInstance->AddCommand (name, callback); +} + +bool RunCommand (const std::string &command){ + if (!CommandsInitialized ()) + return false; + + return CommandsInstance->RunCommand (command); +} + +void QueueCommand (const std::string &command){ + if (!CommandsInitialized ()) + return; + + CommandsInstance->QueueCommand (command); +} + +bool CommandQueueExecute (){ + if (!CommandsInitialized ()) + return false; + + return CommandsInstance->QueueExecute (); +} + +void CommandSetErrorString (const std::string &error_str){ + if (!CommandsInitialized ()) + return; + + CommandsInstance->SetErrorString (error_str); +} + +std::string CommandGetErrorString (){ + if (!CommandsInitialized ()) + return false; + + return CommandsInstance->GetErrorString(); +} + +/* + * Commands of the Command system + */ +bool Cmd_Exec (const std::vector args) { + if (!CommandsInitialized()) + return false; + + if (args.size() != 1) { + CommandsInstance->SetErrorString("usage: exec "); + return false; + } + + std::ifstream exec_file; + exec_file.open(args[0].c_str(), std::ios_base::in); + + if (!exec_file) { + std::ostringstream error_msg; + error_msg << "exec failed: could not open file '" + << args[0] << "'"; + CommandsInstance->SetErrorString(error_msg.str()); + return false; + } + + int linecounter = 0; + while (!exec_file.eof()) { + std::string line; + getline (exec_file, line); + linecounter++; + + trim_command_str (line); + + if (line.size() == 0) + continue; + + if (!CommandsInstance->RunCommand (line)) { + std::ostringstream error_msg; + error_msg << "exec failed running command '" << line + << "' in file " << args[0] << ":" << linecounter << "."; + CommandsInstance->SetErrorString (error_msg.str()); + return false; + } + } + + exec_file.close(); + return true; +} + +void Commands::OnRegisterCommands () { + AddCommand ("exec", Cmd_Exec); +} + +} + diff --git a/engine/Commands.h b/engine/Commands.h new file mode 100644 index 0000000..cff3bcc --- /dev/null +++ b/engine/Commands.h @@ -0,0 +1,70 @@ +#ifndef _COMMANDS_H +#define _COMMANDS_H + +#include "Engine.h" + +namespace Engine { + +class Module; + +/** \brief The callback signature for commands */ +typedef bool (*command_cb) (std::vector); + +/** \brief Contains all the facilities to parse/add/execute/... commands + * + * \todo make the command system case insensitive + * + * This system is mainly used for passing instructions to the Model and + * modifying it in this way. Commands can be added with a name (string) and a + * function (callback). It also contains a CommandQueue to faciliate delayed + * execution of commands and also does the parsing of functions passed to it. + * + * Functions that should be callable by the Commands module must have the + * following signature: + * \code + * bool MyFunc_cmd (std::vector args); + * \endcode + * To keep things readable one is advised to add \c _cmd at the end of the + * function so that it is clear that this function is meant to be called by + * the Commands module. + * + * Scope: Globally visible (since commands must be added) + * + */ +class Commands : public Module { + public: + /** \brief Adds the function callback as command for the given name*/ + void AddCommand (const std::string &name, command_cb callback); + /** \brief Executes the given command immediately */ + bool RunCommand (const std::string &command); + /** \brief Adds the given command to the command queue */ + void QueueCommand (const std::string &command); + /** \brief Executes the command queue */ + bool QueueExecute (); + /** \brief When a command fails it sets the error with this function */ + void SetErrorString (const std::string &error_str); + /** \brief Returns the error string */ + std::string GetErrorString (); + + protected: + /** \brief Initializes the system */ + virtual int OnInit (int argc, char* argv[]); + /** \brief Destroys the system (must be called!) */ + virtual void OnDestroy (); + /** \brief Registers commands relevant to the Command system */ + virtual void OnRegisterCommands (); + + /** \brief All registered commands are in this map */ + std::map mCommandsCallbacks; + /** \brief Queue for the commands */ + std::queue mCommandQueue; + /** \brief Holds the error message of a failed command */ + std::string mErrorString; + + private: + std::string GetCommandName (const std::string &command); + std::vector ParseArgs (std::string &argument_str); +}; + +} +#endif // _COMMANDS_H diff --git a/engine/CommandsGlobal.h b/engine/CommandsGlobal.h new file mode 100644 index 0000000..cf7ca7f --- /dev/null +++ b/engine/CommandsGlobal.h @@ -0,0 +1,24 @@ +#ifndef _COMMANDSGLOBAL_H +#define _COMMANDSGLOBAL_H + +namespace Engine { + +typedef bool (*command_cb) (std::vector); + +/** \brief Adds the function callback as command for the given name*/ +void AddCommand (const std::string &name, command_cb callback); +/** \brief Executes the given command immediately */ +bool RunCommand (const std::string &command); +/** \brief Adds the given command to the command queue */ +void QueueCommand (const std::string &command); +/** \brief Executes the command queue */ +bool CommandQueueExecute (); +/** \brief When a command fails it sets the error with this function */ +void CommandSetErrorString (const std::string &error_str); +/** \brief Returns the error string */ +std::string CommandGetErrorString (); + +} + +#endif /* _COMMANDSGLOBAL_H */ + diff --git a/engine/ControllerBase.cc b/engine/ControllerBase.cc new file mode 100644 index 0000000..6d1533b --- /dev/null +++ b/engine/ControllerBase.cc @@ -0,0 +1,264 @@ +#include "ControllerBase.h" + +#include "ModelBase.h" +#include "ViewBase.h" +#include "CommandsGlobal.h" + +#include "keytable.h" + +namespace Engine { + +static ControllerBase *ControllerInstance = NULL; + +/* + * Helper functions + */ + +/** \brief Converts the SDL_BUTTON_* to a value we know */ +MouseButton convert_sdl_button (Uint8 button) { + MouseButton mouse_button; + switch (button) { + case SDL_BUTTON_LEFT: + mouse_button = MouseButtonLeft; + break; + case SDL_BUTTON_MIDDLE: + mouse_button = MouseButtonMiddle; + break; + case SDL_BUTTON_RIGHT: + mouse_button = MouseButtonRight; + break; + case SDL_BUTTON_WHEELUP: + mouse_button = MouseButtonWheelUp; + break; + case SDL_BUTTON_WHEELDOWN: + mouse_button = MouseButtonWheelDown; + break; + default: + mouse_button = MouseButtonUnknown; + break; + } + + return mouse_button; +} + +/* + * Inherited Module functions + */ +int ControllerBase::OnInit (int argc, char* argv[]) { + LogDebug ("Controller Init"); + + // clear all bindings + int i; + for (i = 0; i < BINDING_KEYS_LAST; i++) + mBindings[i] = ""; + + ControllerInstance = this; + + return 0; +} + +void ControllerBase::OnDestroy () { + ControllerInstance = NULL; + + LogDebug ("Controller Destroy"); +} + +/* + * Module specific functions + */ +bool ControllerBase::BindKey (int key, const char *command) { + if (key <= 0 || key >= BINDING_KEYS_LAST) { + LogError ("Could not bind to key with index '%d': invalid index!", key); + return false; + } + + mBindings[key] = command; + + return true; +} + +void ControllerBase::Process () { + ProcessEvents (); + + mView->CalcWorldCoordinates (mMouseScreenPosition[0], mMouseScreenPosition[1], + 0., mMouseWorldPosition); + + /* + LogMessage ("Screenpos = %2d,%2d Worldpos = %f,%f,%f", + mMouseScreenPosition[0], mMouseScreenPosition[1], + mMouseWorldPosition[0], mMouseWorldPosition[1], mMouseWorldPosition[2]); + */ +} + +void ControllerBase::ProcessEvents () { + SDL_Event event; + + while (SDL_PollEvent(&event)) { + /* We are only worried about SDL_KEYDOWN and SDL_KEYUP events */ + switch (event.type) { + case SDL_KEYDOWN: + OnKeyDown (event.key.keysym); + break; + + case SDL_KEYUP: + OnKeyUp (event.key.keysym); + break; + + case SDL_MOUSEMOTION: + OnMouseMotion(event.motion.x, event.motion.y); + break; + + case SDL_MOUSEBUTTONDOWN: + OnMouseButtonDown (event.button.button, event.button.x, event.button.y); + break; + + case SDL_MOUSEBUTTONUP: + OnMouseButtonUp (event.button.button, event.button.x, event.button.y); + break; + + case SDL_VIDEORESIZE: + OnVideoResize (event.resize.w, event.resize.h); + break; + + case SDL_QUIT: + EngineSetStatus (EngineStatusStopping); + break; + + default: + break; + } + } +} + +/** \brief Keyboard processing */ +bool ControllerBase::OnKeyDown (const SDL_keysym &keysym) { + if (mView->SendKeyDown (keysym)) + return true; + + if (mBindings[keysym.sym].size () != 0) { + QueueCommand (mBindings[keysym.sym]); + return true; + } + + return false; +} + +/** \brief Keyboard processing */ +bool ControllerBase::OnKeyUp (const SDL_keysym &keysym) { + if (mView->SendKeyUp (keysym)) + return true; + + if (mBindings[keysym.sym].size () != 0) { + if (mBindings[keysym.sym][0] == '+') { + std::string upcommand = mBindings[keysym.sym]; + upcommand[0] = '-'; + QueueCommand (upcommand); + return true; + } + } + + return false; +} + +/** \brief Mouse processing */ +bool ControllerBase::OnMouseButtonDown (Uint8 button, Uint16 xpos, Uint16 ypos) { + MouseButton mouse_button = convert_sdl_button (button); + + if (mView->SendMouseButtonDown (button, xpos, ypos)) + return true; + + if (mBindings[mouse_button].size () != 0) { + QueueCommand (mBindings[mouse_button]); + return true; + } + + return false; +} + +/** \brief Mouse processing */ +bool ControllerBase::OnMouseButtonUp (Uint8 button, Uint16 xpos, Uint16 ypos) { + MouseButton mouse_button = convert_sdl_button (button); + + if (mView->SendMouseButtonUp (button, xpos, ypos)) + return true; + + if (mBindings[mouse_button].size () != 0) { + if (mBindings[mouse_button][0] == '+') { + std::string upcommand = mBindings[mouse_button]; + upcommand[0] = '-'; + QueueCommand (upcommand); + return true; + } + } + + return false; +} + +/** \brief Mouse processing */ +bool ControllerBase::OnMouseMotion (const int xnew, const int ynew) { + mMouseScreenPosition[0] = xnew; + mMouseScreenPosition[1] = ynew; + return false; +} + +/** \brief Video */ +bool ControllerBase::OnVideoResize (int width, int height) { + mView->Resize (width, height); + + return true; +} + +int convert_keystring (const char *key_val) { + std::string keystr (key_val); + + // convert the keystr to lowercase + for (std::string::iterator iter = keystr.begin(); iter != keystr.end(); iter++) + (*iter) = tolower(*iter); + + // now we search through the table for he value we were given + int i = 0; + while (key_table[i].keynum != keytable_last) { + if (keystr.compare(key_table[i].keystr) == 0) + return key_table[i].keynum; + + i++; + } + + return 0; +} + +/* + * Commands for the ControllerBase + */ +bool Cmd_Bind (const std::vector args) { + if (ControllerInstance == NULL) { + CommandSetErrorString("Could not bind key: Controller not yet initialized!"); + return false; + } + + if (args.size() != 2) { + CommandSetErrorString("usage: bind "); + return false; + } + + int key = convert_keystring (args[0].c_str()); + + if (key == 0) { + std::ostringstream error_msg; + error_msg << "bind failed: invalid key '" << args[0] << '"'; + CommandSetErrorString(error_msg.str()); + return false; + } + + if (!ControllerInstance->BindKey (key, args[1].c_str())) + return false; + + return true; +} + +void ControllerBase::OnRegisterCommands () { + AddCommand ("bind", Cmd_Bind); +} + +} + diff --git a/engine/ControllerBase.h b/engine/ControllerBase.h new file mode 100644 index 0000000..483f031 --- /dev/null +++ b/engine/ControllerBase.h @@ -0,0 +1,92 @@ +#ifndef _CONTROLLERBASE_H +#define _CONTROLLERBASE_H + +#include "Engine.h" + +namespace Engine { + +class ModelBase; +class Console; +class Module; + +/** \brief Defines the number of keys (keyboard + mous) that we can bind to. + * + * As the keysym enum of SDL has about 320 keys defined and we might have some + * more we set this define to 400 which should suffice. See also the file + * keytable.h + */ +#define BINDING_KEYS_LAST 400 + +/** \brief Converts a string into the corresponding keycode */ +int convert_keystring (const char *key_val); + +/** \brief All input is sent here and distributed from here + * + * Distributes and modifies the Model and indirectly the view by modifying the + * camera. It also holds the configuration of the keybindings and sends + * commands to the CommandQueue. + */ + +class ControllerBase : public Module { + public: + /** \brief Processes all inputs and performs all the controlling for the + * current frame. */ + void Process (); + /** \brief Returns the current mouse position in screen coordinates */ + void GetMouseScreenPosition (int *pos_out) { + pos_out[0] = mMouseScreenPosition[0]; + pos_out[1] = mMouseScreenPosition[1]; + } + void GetMouseWorldPosition (float *pos_out) { + pos_out[0] = mMouseWorldPosition[0]; + pos_out[1] = mMouseWorldPosition[1]; + pos_out[2] = mMouseWorldPosition[2]; + } + bool BindKey (int key, const char *command); + + protected: + /** \brief Initializes the system */ + virtual int OnInit (int argc, char* argv[]); + /** \brief Destroys the system (must be called!) */ + virtual void OnDestroy (); + /** \brief Registering of the commands of the ControllerBase */ + virtual void OnRegisterCommands (); + + /** \brief Processes all Events reported by SDL_PollEvent */ + virtual void ProcessEvents (); + + /** \brief Keyboard processing */ + bool OnKeyDown (const SDL_keysym &keysym); + /** \brief Keyboard processing */ + bool OnKeyUp (const SDL_keysym &keysym); + + /** \brief Mouse processing */ + bool OnMouseButtonDown (Uint8 button, Uint16 xpos, Uint16 ypos); + /** \brief Mouse processing */ + bool OnMouseButtonUp (Uint8 button, Uint16 xpos, Uint16 ypos); + /** \brief Mouse processing */ + bool OnMouseMotion (const int xnew, const int ynew); + + /** \brief Resizes the size of the View */ + bool OnVideoResize (int width, int height); + + /** \brief Needs the Model to modify it */ + ModelBase * mModel; + /** \brief Input might be sent to the Console, hence it is here */ + Console * mConsole; + /** \brief The View which can get modified by Controller */ + ViewBase *mView; + + /** \brief Stores the current mouse position in screen coordinates */ + int mMouseScreenPosition[2]; + /** \brief Stores the current mouse position on the y=0 plane in wolrd * coordinates */ + float mMouseWorldPosition[3]; + + /** \brief Contains all the bindings for the keyboard */ + std::string mBindings[BINDING_KEYS_LAST]; + + friend class Engine; +}; + +} +#endif // _CONTROLLERBASE_H diff --git a/engine/DrawingsGL.cc b/engine/DrawingsGL.cc new file mode 100644 index 0000000..567e90c --- /dev/null +++ b/engine/DrawingsGL.cc @@ -0,0 +1,247 @@ +#include + +#include "mathlib.h" +#include "DrawingsGL.h" + +#include + +static void CubeVertices () { + // front + glVertex3f (0.5, -0.5, 0.5); + glVertex3f (0.5, -0.5, -0.5); + glVertex3f (0.5, 0.5, -0.5); + glVertex3f (0.5, 0.5, 0.5); + // back + glVertex3f (-0.5, 0.5, 0.5); + glVertex3f (-0.5, 0.5, -0.5); + glVertex3f (-0.5, -0.5, -0.5); + glVertex3f (-0.5, -0.5, 0.5); + // left + glVertex3f (-0.5, -0.5, 0.5); + glVertex3f (0.5, -0.5, 0.5); + glVertex3f (0.5, 0.5, 0.5); + glVertex3f (-0.5, 0.5, 0.5); + // right + glVertex3f (-0.5, 0.5, -0.5); + glVertex3f (0.5, 0.5, -0.5); + glVertex3f (0.5, -0.5, -0.5); + glVertex3f (-0.5, -0.5, -0.5); + // bottom + glVertex3f (-0.5, -0.5, -0.5); + glVertex3f (0.5, -0.5, -0.5); + glVertex3f (0.5, -0.5, 0.5); + glVertex3f (-0.5, -0.5, 0.5); + // top + glVertex3f (-0.5, 0.5, 0.5); + glVertex3f (0.5, 0.5, 0.5); + glVertex3f (0.5, 0.5, -0.5); + glVertex3f (-0.5, 0.5, -0.5); +} + +void DrawWireCube () { + glBegin (GL_LINE_STRIP); + CubeVertices (); + glEnd (); +} + +void DrawSolidCube () { + glBegin (GL_QUADS); + CubeVertices (); + glEnd (); +} + +void DrawCircle () { + int i, segments; + segments = 20; + double x, z, rad, drad; + + drad = (M_PI * 2) / segments; + + // Top + glBegin (GL_TRIANGLE_FAN); + glVertex3f (0., 0.5, 0.); + for (i = 0; i <= segments; i++) { + rad = drad * i; + sincos (rad, &z, &x); + glVertex3f (x * 0.5, 0., -z * 0.5); + } + glEnd (); +} + +void DrawTorus () { + int i, segments; + segments = 20; + double x, z, rad, drad; + + drad = (M_PI * 2) / segments; + + // Top + glBegin (GL_TRIANGLE_FAN); + glVertex3f (0., 0.5, 0.); + for (i = 0; i <= segments; i++) { + rad = drad * i; + sincos (rad, &z, &x); + glVertex3f (x * 0.5, 0.5, -z * 0.5); + } + glEnd (); + + // Bottom + glBegin (GL_TRIANGLE_FAN); + glVertex3f (0., -0.5, 0.); + for (i = 0; i <= segments; i++) { + rad = -drad * i; + sincos (rad, &z, &x); + glVertex3f (x * 0.5, -0.5, -z * 0.5); + } + glEnd (); + + // Sides + glBegin (GL_QUAD_STRIP); + for (i = 0; i <= segments; i++) { + rad = drad * i; + sincos (rad, &z, &x); + glVertex3f (x * 0.5, 0.5, -z * 0.5); + glVertex3f (x * 0.5, -0.5, -z * 0.5); + } + glEnd (); +} + +void DrawPoint (float r, float x, float y, float z) { + glPointSize (r); + glBegin (GL_POINTS); + glVertex3f (x, y, z); + glEnd (); + glPointSize (1.); +} + +void DrawAxis() { + glColor4f(1., 1., 1., 1.); + + glBegin(GL_LINES); + glVertex3f(0., 0., 0.); + glVertex3f(1., 0., 0.); + glVertex3f(0., 0., 0.); + glVertex3f(0., 1., 0.); + glVertex3f(0., 0., 0.); + glVertex3f(0., 0., 1.); + glEnd(); + + // X + glPushMatrix(); + glTranslatef(1., 0., 0.); + glColor3f(1., 0., 0.); + glScalef(0.2, 0.1, 0.1); + DrawCone(10); + glTranslatef(0.4, 0., 0.); + // glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18,'x'); + glPopMatrix(); + + // Y + glPushMatrix(); + glTranslatef(0., 1., 0.); + glRotatef(90, 0., 0., 1.); + glColor3f(0., 1., 0.); + glScalef(0.2, 0.1, 0.1); + DrawCone(10); + glTranslatef(0.4, 0., 0.); + // glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18,'y'); + glPopMatrix(); + + // Z + glPushMatrix(); + glTranslatef(0., 0., 1.); + glRotatef(90, 0., -1., 0.); + glColor3f(0., 0., 1.); + glScalef(0.2, 0.1, 0.1); + DrawCone(10); + glPopMatrix(); +} + +void DrawDisc(float radius, int segments) { + int i; + float radiant; + glBegin(GL_TRIANGLE_FAN); + glVertex3f(0., 0., 0.); + for (i = 0; i <= segments; i++) { + radiant = (float) i * ((2 * M_PI) / (float) segments); + glVertex3f(radius * cos(radiant), 0., radius * sin(radiant)); + } + glEnd(); +} + +void DrawCircle(float radius, int segments) { + int i; + float radiant; + glBegin(GL_LINE_STRIP); + glVertex3f(0., 0., 0.); + for (i = 0; i <= segments; i++) { + radiant = (float) i * ((2 * M_PI) / (float) segments); + glVertex3f(radius * cos(radiant), 0., radius * sin(radiant)); + } + glEnd(); +} + +void DrawCone(int segments) { + int i; + float radiant; + + glBegin(GL_TRIANGLE_FAN); + glVertex3f(1., 0., 0.); + for (i = 0; i <= segments; i++) { + radiant = (float) (i * (2. * M_PI) / (float) segments); + + glVertex3f(0., cos(radiant), sin(radiant)); + } + + glEnd(); +} + +void DrawVector(vector3d start, vector3d end) { + vector3d direction (end - start); + + glColor3f(1., 1., 1.); + glLineWidth(2.); + glBegin(GL_LINES); + glVertex3f(start[0], start[1], start[2]); + glVertex3f(end[0], end[1], end[2]); + glEnd(); + glLineWidth(1.); + + vector3d right; + vector3d up; + vector3d direction_norm = direction / direction.length(); + float cone_radius = 0.15; + vector3d conebottom = end - direction_norm * 0.3; + + // Draw the tip + if (fabs(direction[0]) > fabs(direction[1]) && fabs(direction[2]) > fabs(direction[1])) { + up.setValues (0., 1., 0.); + vector3d right = direction.cross (up); + right = right / right.length(); + + int i; + int segments = 20.; + float rad_delta = 2. * M_PI / (float) segments; + double s,c; + + glColor3f(0.2, 0.2, 0.5); + glPointSize (10.); + glBegin (GL_TRIANGLE_FAN); + glVertex3f (end[0], end[1], end[2]); + + for (i = 0; i <= segments; i ++) { + sincos (i * rad_delta, &s, &c); + glVertex3f (conebottom[0] + right[0] * cone_radius * c, conebottom[1] + s * cone_radius, conebottom[2] + right[2] * cone_radius * c); + } + + glEnd (); + glBegin (GL_TRIANGLE_FAN); + glVertex3f (conebottom[0], conebottom[1], conebottom[2]); + for (i = 0; i <= segments; i ++) { + sincos (i * rad_delta, &s, &c); + glVertex3f (conebottom[0] + right[0] * cone_radius * c, conebottom[1] + s * cone_radius, conebottom[2] + right[2] * cone_radius * c); + } + + glEnd (); + } +} diff --git a/engine/DrawingsGL.h b/engine/DrawingsGL.h new file mode 100644 index 0000000..b3bc9c6 --- /dev/null +++ b/engine/DrawingsGL.h @@ -0,0 +1,21 @@ +#ifndef _DRAWINGSGL_H +#define _DRAWINGSGL_H + +#ifdef WIN32 + #include +#endif + +struct vector3d; + +void DrawWireCube (); +void DrawSolidCube (); +void DrawCircle (); +void DrawTorus (); +void DrawPoint (float r, float x, float y, float z); +void DrawAxis(); +void DrawDisc(float radius, int segments); +void DrawCircle(float radius, int segments); +void DrawCone(int segments); +void DrawVector(vector3d start, vector3d end); + +#endif /* _DRAWINGSGL_H */ diff --git a/engine/Engine.cc b/engine/Engine.cc new file mode 100644 index 0000000..660792d --- /dev/null +++ b/engine/Engine.cc @@ -0,0 +1,353 @@ +#include "Engine.h" +#include "Logging.h" +#include "Variables.h" +#include "Commands.h" + +#include "PhysicsBase.h" +#include "CameraBase.h" + +#include "ViewBase.h" +#include "ModelBase.h" +#include "ControllerBase.h" + +#include "EntityFactoryBase.h" +#include "EventsBase.h" + +#include +#include +#include +#include +#include + +int vasprintf (char **result, const char *format, va_list *string) { + return 0; +} + +namespace Engine { + +/* Globaly visible classes */ +Engine* EngineInstance = NULL; + +/* + * Inherited Module functions + */ +int Engine::OnInit (int argc, char* argv[]) { + EngineInstance = this; + mStatus = EngineStatusUndefined; + + /* Initialization of the base modules */ + if (mLogging == NULL) + mLogging = new Logging (); + + if (! mLogging ) { + SetError ("Could not allocate memory for Logging!"); + exit (-1); + } + mLogging->Init (argc, argv); + SetStatus (EngineStatusInitializing); + + if (mEventManager == NULL) + mEventManager = new EventManager(); + + mEventManager->Init (argc, argv); + + if (mVariables == NULL) + mVariables = new Variables (); + if (! mVariables ) { + SetError ("Could not allocate memory for Variables!"); + exit (-1); + } + mVariables->Init (argc, argv); + + if (mCommands == NULL) + mCommands = new Commands (); + if (! mCommands ) { + SetError ("Could not allocate memory for Commands!"); + exit (-1); + } + mCommands->Init (argc, argv); + + // Initialize the SDL + LogDebug ("Initializing SDL"); + if ( SDL_Init ( SDL_INIT_VIDEO ) < 0) { + LogError ("Error initializing SDL: %s", SDL_GetError ()); + exit (-1); + } + + + /* Model */ + if (mModel == NULL) + mModel = new ModelBase (); + + if (! mModel ) { + SetError ("Could not allocate memory for Model!"); + exit (-1); + } + + /* Initialization of the modules for the model */ + if (mEntityFactory == NULL) + mEntityFactory = new EntityFactoryBase; + + mModel->mEntityFactory = mEntityFactory; + + + /* Physics */ + if (mPhysics == NULL) { + mPhysics = new PhysicsBase (); + + if (! mPhysics ) { + SetError ("Could not allocate memory for Physics!"); + exit (-1); + } + } + + mModel->mPhysics = mPhysics; + + mEntityFactory->Init (argc, argv); + mModel->mPhysics->Init (argc, argv); + mModel->Init (argc, argv); + + /* View */ + if (mView == NULL) + mView = new ViewBase (); + + if (! mView ) { + SetError ("Could not allocate memory for View!"); + exit (-1); + } + + mView->mCamera = new CameraBase (); + mView->mCamera->Init (argc, argv); + + mView->mModel = mModel; + mView->Init (argc, argv); + + /* Controller */ + if (mController == NULL) + mController = new ControllerBase (); + + if (! mController ) { + SetError ("Could not allocate memory for Controller!"); + exit (-1); + } + + mController->mModel = mModel; + mController->mView = mView; + + mController->Init (argc, argv); + + /* Now register the commands */ + mLogging->RegisterCommands (); + mVariables->RegisterCommands (); + mCommands->RegisterCommands (); + + mModel->mPhysics->RegisterCommands (); + mModel->RegisterCommands (); + + mView->mCamera->RegisterCommands (); + mView->RegisterCommands (); + + mController->RegisterCommands (); + + RegisterCommands (); + + /* Now we are done */ + SetStatus (EngineStatusInitialized); + + return 0; +} + +void Engine::OnDestroy () { + SetStatus (EngineStatusDestroying); + + // Quit the SDL + SDL_Quit (); + + if (mController) { + mController->Destroy (); + delete mController; + mController = NULL; + } + + if (mView) { + if (mView->mCamera ) { + mView->mCamera->Destroy (); + delete mView->mCamera; + } + + mView->Destroy (); + delete mView; + mView = NULL; + } + + if (mModel) { + mModel->Destroy (); + + if (mModel->mPhysics ) { + mModel->mPhysics->Destroy (); + delete mModel->mPhysics; + } + + delete mModel; + mModel = NULL; + } + + if (mCommands) { + mCommands->Destroy (); + delete mCommands; + mCommands = NULL; + } + + if (mVariables) { + mVariables->Destroy (); + delete mVariables; + mVariables = NULL; + } + + SetStatus (EngineStatusStopped); + + if (mEventManager) { + mEventManager->Destroy(); + delete mEventManager; + mEventManager = NULL; + } + + if (mLogging) { + mLogging->Destroy (); + delete mLogging; + mLogging = NULL; + } + + mStatus = EngineStatusUndefined; + EngineInstance = NULL; +} + +/* + * Module specific functions + */ +void Engine::SetStatus (EngineStatus new_status) { + LogDebug ("EngineStatus Change: '%d' -> '%d'", mStatus, new_status); + mStatus = new_status; +} + +EngineStatus Engine::GetStatus () { + return mStatus; +} + +void Engine::OnMainLoop () { + SetStatus (EngineStatusRunning); + + while (mStatus == EngineStatusRunning) { + mController->Process (); + mCommands->QueueExecute (); + mEventManager->Process(); + mModel->UpdateTimer (); + mModel->Process (); + mView->Draw (); + } +} + +void Engine::SetError (const char* str, ...) { + static char msg[LOG_MAX_MESSAGE_LENGTH]; + static int last_length = LOG_MAX_MESSAGE_LENGTH; + int i; + + for (i = 0; i < last_length; i++) + msg[i] = 0; + + va_list ap; + va_start(ap, str); + vsprintf(msg, str, ap); + va_end(ap); + + mErrorString = msg; + + last_length = strlen (msg); +} + +const char* Engine::GetError () { + return mErrorString.c_str(); +} + +/* + * Global functions + */ +void EngineSetError (const char* str, ...) { + if (EngineInstance == NULL) { + std::cerr << "Error: Engine Instance not yet initialized!" << std::endl; + assert (0); + } + + static char msg[LOG_MAX_MESSAGE_LENGTH]; + static int last_length = LOG_MAX_MESSAGE_LENGTH; + int i; + + for (i = 0; i < last_length; i++) + msg[i] = 0; + + va_list ap; + va_start(ap, str); + vsprintf(msg, str, ap); + va_end(ap); + + EngineInstance->SetError (msg); + + last_length = strlen (msg); +} + +const char* EngineGetError () { + if (EngineInstance == NULL) { + std::cerr << "Error: Engine Instance not yet initialized!" << std::endl; + assert (0); + } + + return EngineInstance->GetError (); +} + +void EngineSetStatus (const EngineStatus status) { + if (EngineInstance == NULL) { + std::cerr << "Error: Engine Instance not yet initialized!" << std::endl; + assert (0); + } + + EngineInstance->SetStatus (status); +} + +EngineStatus EngineGetStatus () { + if (EngineInstance == NULL) { + std::cerr << "Error: Engine Instance not yet initialized!" << std::endl; + assert (0); + } + + return EngineInstance->GetStatus (); +} + +ModelBase* EngineGetModel () { + if (EngineInstance == NULL) { + std::cerr << "Error: Engine Instance not yet initialized!" << std::endl; + assert (0); + } + + return EngineInstance->GetModel(); +} + +ViewBase* EngineGetView () { + if (EngineInstance == NULL) { + std::cerr << "Error: Engine Instance not yet initialized!" << std::endl; + assert (0); + } + + return EngineInstance->GetView(); +} + +ControllerBase* EngineGetController () { + if (EngineInstance == NULL) { + std::cerr << "Error: Engine Instance not yet initialized!" << std::endl; + assert (0); + } + + return EngineInstance->GetController(); +} + +} + diff --git a/engine/Engine.h b/engine/Engine.h new file mode 100644 index 0000000..0be3c54 --- /dev/null +++ b/engine/Engine.h @@ -0,0 +1,167 @@ +#ifndef _ENGINE_H +#define _ENGINE_H + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Module.h" + +// Some ugly #defines + +/** \brief Defines the number of keys that can be defined for an EntityController + * + * The current state of the controls for an entity is stored in an std::bitset + * and each bit tells us whether the key is pressed or not. Unfortunately we + * have to know the number of bits that are to be reserved in advance so this + * is hard coded into the library for now. + */ +#define ENTITY_CONTROLLER_MAX_KEY_STATES 64 + +namespace Engine { + +class ModelBase; +class ViewBase; +class ControllerBase; +class PhysicsBase; +class EntityFactoryBase; +class EventManager; + +class Logging; +class Commands; +class Variables; +class Variable; + +enum EngineStatus { + EngineStatusUndefined = 0, + EngineStatusInitializing, + EngineStatusInitialized, + EngineStatusRunning, + EngineStatusStopping, + EngineStatusStopped, + EngineStatusDestroying +}; + +/** \brief The outermost class which contains just everything! + * + * Engine::Engine takes care of initializing, running and destroying the whole + * Engine. + * + * When Initialize called, the submodules are created by Engine and + * initializes them. There are two initialization phases: + * - Basemodules initialization: + * The first phase creates the base modules such as Engine::Logging, + * Engine::Variables and Engine::Commands and calls Initialize () on them. + * - ModelViewController initialization: + * In the second phase at first the Submodules for each Engine::ModelBase, Engine::ViewBase and + * Engine::ControllerBase are allocated an initialized. Then the Engine::ModelBase, + * Engine::View and Engine::ControllerBase Modules are initialized. + */ +class Engine : public Module { + public: + Engine () { + mModel = NULL; + mView = NULL; + mController = NULL; + mLogging = NULL; + mCommands = NULL; + mVariables = NULL; + mEntityFactory = NULL; + mEventManager = NULL; + } + + virtual void MainLoop () { + OnMainLoop (); + } + + void SetModel (ModelBase *model) { mModel = model; }; + ModelBase* GetModel () { return mModel; }; + void SetView (ViewBase *view) { mView = view; }; + ViewBase* GetView () { return mView; }; + void SetPhysics (PhysicsBase *physics) { mPhysics = physics; }; + PhysicsBase* GetPhysics () { return mPhysics; }; + void SetEntityFactory (EntityFactoryBase *entityfactory) { mEntityFactory = entityfactory; }; + + void SetController (ControllerBase *controller) { mController = controller; }; + ControllerBase* GetController () { return mController; }; + void SetLogging (Logging *logging) { mLogging = logging; }; + void SetCommands (Commands *commands) { mCommands = commands; }; + void SetVariables (Variables *variables) { mVariables = variables; }; + + void SetError (const char* str, ...); + const char* GetError (); + void SetStatus (const EngineStatus new_status); + EngineStatus GetStatus (); + + private: + // Engine must not be created with the standard constructor! + // It must be ensured, that the correct EntityFactory is used! + protected: + virtual int OnInit (int argc, char* argv[]); + virtual void OnDestroy (); + virtual void OnRegisterCommands (); + + void OnMainLoop (); + + ModelBase *mModel; + ViewBase *mView; + ControllerBase *mController; + PhysicsBase *mPhysics; + EntityFactoryBase *mEntityFactory; + EventManager *mEventManager; + + Logging *mLogging; + Commands *mCommands; + Variables *mVariables; + + std::string mErrorString; + EngineStatus mStatus; +}; + +} + +/* + * Global visible functions and classes + */ + +namespace Engine { +/* Globaly visible classes */ +extern Engine* EngineInstance; + +/** \brief Sets the Engine::mErrorString to the given message */ +void EngineSetError (const char* str, ...); +/** \brief Returns Engine::mErrorString */ +const char* EngineGetError (); + +/** \brief Sets the current state of the Engine */ +void EngineSetStatus (const EngineStatus status); +/** \brief Returns the current state of the Engine */ +EngineStatus EngineGetStatus (); + +/** \brief Global access functions for the Model */ +ModelBase* EngineGetModel (); +/** \brief Global access functions for the View */ +ViewBase* EngineGetView (); +/** \brief Global access functions for the Controller */ +ControllerBase* EngineGetController (); + +} + +/* Include the globally visible declarations of the other modules */ +#include "LoggingGlobal.h" +#include "VariablesGlobal.h" +#include "CommandsGlobal.h" +#include "ModelBaseGlobal.h" +#include "ViewBaseGlobal.h" +#include "EventsBaseGlobal.h" + +#endif // _ENGINE_H diff --git a/engine/EngineCommands.cc b/engine/EngineCommands.cc new file mode 100644 index 0000000..e8a7863 --- /dev/null +++ b/engine/EngineCommands.cc @@ -0,0 +1,15 @@ +#include "Engine.h" + +namespace Engine { + +bool Cmd_EngineQuit (std::vector args) { + EngineSetStatus (EngineStatusStopping); + + return true; +} + +void Engine::OnRegisterCommands () { + AddCommand ("quit", Cmd_EngineQuit); +} + +} diff --git a/engine/EntityBase.cc b/engine/EntityBase.cc new file mode 100644 index 0000000..a1fa0d0 --- /dev/null +++ b/engine/EntityBase.cc @@ -0,0 +1,45 @@ +#include "EntityBase.h" + +#include "coll2d.h" + +namespace Engine { + +bool EntityControllerState::GetKey (int state) { + assert (state < ENTITY_CONTROLLER_MAX_KEY_STATES && state >= 0); + + return mKeyState.test (state); +} + +void EntityControllerState::SetKey (int state) { + assert (state < ENTITY_CONTROLLER_MAX_KEY_STATES && state >= 0); + + LogDebug ("Setting Entity Key State %d", state); + mKeyState.set (state); +} + +void EntityControllerState::UnsetKey (int state) { + assert (state < ENTITY_CONTROLLER_MAX_KEY_STATES && state >= 0); + + LogDebug ("Unsetting Entity Key State %d", state); + mKeyState.reset (state); +} + +void EntityBase::SetControllerKeyState (int state) { + if (!mControllerState) { + LogError ("Error when trying to send a KeyState to an Entity that has no EntityControllerState!"); + assert (0); + } + + mControllerState->SetKey (state); +} + +void EntityBase::UnsetControllerKeyState (int state) { + if (!mControllerState) { + LogError ("Error when trying to send a KeyState to an Entity that has no EntityControllerState!"); + assert (0); + } + + mControllerState->UnsetKey (state); +} + +} diff --git a/engine/EntityBase.h b/engine/EntityBase.h new file mode 100644 index 0000000..0c466c8 --- /dev/null +++ b/engine/EntityBase.h @@ -0,0 +1,154 @@ +#ifndef _ENTITYBASE_H +#define _ENTITYBASE_H + +#include "Engine.h" +#include "mathlib.h" + +#include "EntityBaseTypes.h" + +namespace coll2d { + class Shape; +} + +namespace Engine { + +/** \brief Contains all the information for the physical simulation of the + * Entity + * + * Every Engine::Entity that should be simulated in the Engine::Physics module + * must have a Engine::EntityPhysicState counterpart. It represents the state + * of the Entity in the physical world of the engine. + * + * The Copy-Constructor and Assignment Operator is defined in EntityPhysics.cc + * as they require a call to coll2d::Shape::getCopy() which again requires the + * inclusion of coll2d.h which should stay out of Entity.h for faster + * compilation. + */ +struct EntityPhysicState { + /** \brief Standard constructor */ + EntityPhysicState (): + mId (0), + mBaseType (EntityBaseTypeNone), + mPosition (0., 0., 0.), + mOrientation (0., 0., 0.), + mVelocity (0., 0., 0.), + mAngleVelocity (0.), + mRadius (0.), + mShape (NULL), + mStatic (false), + mAlive (true), + mContactNormals() {}; + + virtual ~EntityPhysicState() {}; + + /** \brief Copy constructor */ + EntityPhysicState (const EntityPhysicState& state); + + /** \brief Assignment operator */ + EntityPhysicState& operator= (const EntityPhysicState& state); + + unsigned int mId; + /** \brief The type from the game's perspective */ + int mType; + /** \brief The type from the engine's perspective */ + EntityBaseType mBaseType; + + vector3d mPosition; + vector3d mOrientation; + vector3d mVelocity; + float mAngleVelocity; + float mRadius; + + coll2d::Shape* mShape; + bool mStatic; + + /** \brief If false, the entity will be removed at the end of the frame. */ + bool mAlive; + + vector3d &GetPosition (); + vector3d &GetVelocity (); + vector3d &GetOrientation (); + float &GetAngleVelocity (); + + void SetPosition (const vector3d &position); + void SetVelocity (const vector3d &velocity); + void SetOrientation (const vector3d &orientation); + void SetAngleVelocity (const float &angle_velocity); + + /** \brief Transforms the given vector in local space to world coordinates */ + void Globalize (vector3d &vec); + /** \brief Transforms the given vector from world coordinates to local + * coordinates */ + void Localize (vector3d &vec); + /** \brief Performs only the rotational part of Globalize() */ + void GlobalizeRotation (vector3d &vec); + /** \brief Performs only the rotational part of Localize() */ + void LocalizeRotation (vector3d &vec); + + /** \brief Updates the shape for collision detection */ + void UpdateShape (); + + /** \brief Contains all the normal vectors of the planes with which we are in contact. */ + std::map mContactNormals; +}; + +/** \brief Represents the current state of the Entity controller + * + * This struct stores the current state for all EntityControllerKeyStates + * (e.g. whether the keys are currently pressed or not). + * + * \todo [Low] The current design is very unflexible. Is there a better way? + */ +struct EntityControllerState { + std::bitset mKeyState; + + bool GetKey (int state); + void SetKey (int state); + void UnsetKey (int state); +}; + +/** \brief Represents the base of everything that exists in the engine + * + * An Engine::Entity has different representations in the different + * submodules. Engine::EntityPhysicState stores the physical representation of + * the entity like its position, velocity, orientation, and shape. + * Engine::EntityVisualState is used for drawing an Entity by Engine::View. + */ +struct EntityBase { + unsigned int mId; + /** \brief The type from the game's perspective */ + int mType; + /** \brief The type from the engine's perspective */ + EntityBaseType mBaseType; + + EntityPhysicState *mPhysicState; + EntityControllerState *mControllerState; + + /** \brief Applies the state of the EntityControllerState to the EntityPhysicState etc. */ + virtual void Update (float delta_sec) {}; + + /** \brief Adds the given state to the current movement state of the Entity */ + virtual void SetControllerKeyState (int state); + /** \brief Removes the given state to the current movement state of the Entity */ + virtual void UnsetControllerKeyState (int state); + + /** \brief Helper function to set the id to all substates */ + void SetId (unsigned int id) { + mId = id; + + if (mPhysicState) + mPhysicState->mId = id; + } + + /** Executes game logic for the collision event + * \returns true if it was able to react for this type of entity, false + * false otherwise + */ + virtual bool CollisionEvent (EntityBase* entity_state) { + return false; + } +}; + +} + +#endif // _ENTITYBASE_H diff --git a/engine/EntityBaseTypes.h b/engine/EntityBaseTypes.h new file mode 100644 index 0000000..e1755f6 --- /dev/null +++ b/engine/EntityBaseTypes.h @@ -0,0 +1,59 @@ +#ifndef _ENTITYBASETYPES_H +#define _ENTITYBASETYPES_H + +namespace Engine { + +struct EntityBase; +struct EntityPhysicState; +struct EntityControllerState; + +/** \brief Represents the different types of an entity from the Engines' perspective + * + * The different EntityBaseTypes define their basic behaviour when it comes to + * events such as collisions between two Entities. + * + * Here is an overview how collisions are handled: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
EntityBaseTypeParticleEntityBaseTypeBlockEntityBaseTypeActor
EntityBaseTypeParticle-CC
EntityBaseTypeBlockCCC
EntityBaseTypeActorCCC
+ * + * A 'C' means that Engine::Physics::CheckPossibleCollisionPair() will return + * true (this results that the two types cannot penetrate each other). + */ +enum EntityBaseType { + EntityBaseTypeNone = 0, /**< is the default type and does not collide with anything */ + EntityBaseTypeBlock, /**< represents walls etc. that block Particles, Actors and other Blocks */ + EntityBaseTypeParticle, /**< is used for projectiles or Entities that just move + but are "dumb" apart from that */ + EntityBaseTypeActor, /**< is the type for objects that interact with each other */ + EntityBaseTypeLast /**< marks the maximum number of EntityBaseType */ +}; + + +} + +#endif // _ENTITYBASETYPES_H diff --git a/engine/EntityFactoryBase.cc b/engine/EntityFactoryBase.cc new file mode 100644 index 0000000..4cf1c82 --- /dev/null +++ b/engine/EntityFactoryBase.cc @@ -0,0 +1,117 @@ +#include + +#include "EntityFactoryBase.h" + +#include "EntityBase.h" +#include "coll2d.h" + +namespace Engine { + +int EntityFactoryBase::OnInit (int argc, char* argv[]) { + LogMessage ("Initializing EntityFactory"); + + return 0; +} + +EntityPhysicState* EntityFactoryBase::CreateEntityPhysicState (int type) { + // In this simple factory the type is simply seen as the EntityBaseType. + // However to prevent errors we do a simple check vor validity. + if (type < 0 || type >> EntityBaseTypeLast ) { + LogError ("Cannot create Entity with type %d: invalid type!", type); + assert (0); + } + EntityBaseType base_type = (EntityBaseType) type; + + // Create the Entity + EntityPhysicState* entity_physics = new EntityPhysicState (); + if (!entity_physics) { + LogError ("Could not allocate enough memory for EntityPhysicState of type '%d'", type); + assert (0); + } + + // default values for all entities + entity_physics->mBaseType = base_type; + + // specific values for each Entity base_type + if (base_type == EntityBaseTypeNone) { + entity_physics->mShape = new coll2d::Sphere (0.01); + assert (entity_physics->mShape); + } else if (base_type == EntityBaseTypeActor) { + entity_physics->mShape = new coll2d::Sphere (0.4); + assert (entity_physics->mShape); + } else if (base_type == EntityBaseTypeBlock) { + entity_physics->mShape = new coll2d::Polygon (4); + assert (entity_physics->mShape); + + static_cast (entity_physics->mShape)->setVertice (0, vector3d (-0.5, 0., 0.5)); + static_cast (entity_physics->mShape)->setVertice (1, vector3d (0.5, 0., 0.5)); + static_cast (entity_physics->mShape)->setVertice (2, vector3d (0.5, 0., -0.5)); + static_cast (entity_physics->mShape)->setVertice (3, vector3d (-0.5, 0., -0.5)); + } else if (base_type == EntityBaseTypeParticle) { + entity_physics->mShape = new coll2d::Sphere (0.05); + assert (entity_physics->mShape); + } else { + LogError ("No EntityPhysicState defined for Entity base_type '%d'", base_type); + assert (0); + } + + return entity_physics; +} + +EntityControllerState* EntityFactoryBase::CreateEntityControllerState (int type) { + // In this simple factory the type is simply seen as the EntityBaseType. + // However to prevent errors we do a simple check vor validity. + if (type < 0 || type >> EntityBaseTypeLast ) { + LogError ("Cannot create Entity with type %d: invalid type!", type); + assert (0); + } + EntityBaseType base_type = (EntityBaseType) type; + + // Create the Entity + EntityControllerState* entity_controller = NULL; + + // specific values for each Entity base_type + if (base_type == EntityBaseTypeNone) { + } else if (base_type == EntityBaseTypeActor) { + entity_controller = new EntityControllerState (); + if (!entity_controller) { + LogError ("Could not allocate enough memory for EntityControllerState of type '%d'", type); + assert (0); + } + } else if (base_type == EntityBaseTypeBlock) { + } else if (base_type == EntityBaseTypeParticle) { + } else { + LogError ("No EntityPhysicState defined for Entity base_type '%d'", base_type); + assert (0); + } + + return entity_controller; +} + +EntityBase* EntityFactoryBase::CreateEntity (int type) { + // In this simple factory the type is simply seen as the EntityBaseType. + // However to prevent errors we do a simple check vor validity. + if (type < 0 || type >> EntityBaseTypeLast ) { + LogError ("Cannot create Entity with type %d: invalid type!", type); + assert (0); + } + EntityBaseType base_type = (EntityBaseType) type; + + // Create the Entity + EntityBase *entity = new EntityBase; + entity->mBaseType = base_type; + + if (!entity) { + LogError ("Could not allocate enough memory for EntityVisualState of type '%d'", type); + assert (0); + } + + entity->mPhysicState = CreateEntityPhysicState (type); + entity->mControllerState = CreateEntityControllerState (type); + + return entity; +} + +} + + diff --git a/engine/EntityFactoryBase.h b/engine/EntityFactoryBase.h new file mode 100644 index 0000000..d82e8ec --- /dev/null +++ b/engine/EntityFactoryBase.h @@ -0,0 +1,35 @@ +#ifndef _ENTITYFACTORYBASE_H +#define _ENTITYFACTORYASE_H + +#include "Module.h" + +namespace Engine { + + struct EntityBase; + + struct EntityPhysicState; + struct EntityControllerState; + + /** \brief Takes care of the creation of user-defined Entities + * + * To be able to define custom types of Entities we use a Object Factory for + * which the actual CreateEntityXYZState () function can be overloaded with + * the creation of custom types. + * + * The method EntityFactory::CreateEntity() will be called by the + * Model::CreateEntity() function which also takes care of registering the + * Entity to required submodules. + */ + class EntityFactoryBase : public Module { + public: EntityFactoryBase () {}; + virtual EntityPhysicState* CreateEntityPhysicState (int type); + virtual EntityControllerState* CreateEntityControllerState (int type); + + virtual EntityBase* CreateEntity (int type); + + protected: + virtual int OnInit (int argc, char* argv[]); + }; +} + +#endif // _ENTITYFACTORYBASE_H diff --git a/engine/EnumToString.h b/engine/EnumToString.h new file mode 100644 index 0000000..9e728ca --- /dev/null +++ b/engine/EnumToString.h @@ -0,0 +1,21 @@ +// File name: "EnumToString.h" +// +// From: http://www.codeproject.com/KB/cpp/C___enums_to_strings.aspx +// +// Thanks to Marcos F. Cardoso + +#undef DECL_ENUM_ELEMENT +#undef BEGIN_ENUM +#undef END_ENUM + +#ifndef GENERATE_ENUM_STRINGS + #define DECL_ENUM_ELEMENT( element ) element + #define BEGIN_ENUM( ENUM_NAME ) typedef enum tag##ENUM_NAME + #define END_ENUM( ENUM_NAME ) ENUM_NAME; \ + const char* GetString##ENUM_NAME(enum tag##ENUM_NAME index); +#else + #define DECL_ENUM_ELEMENT( element ) #element + #define BEGIN_ENUM( ENUM_NAME ) const char* gs_##ENUM_NAME [] = + #define END_ENUM( ENUM_NAME ) ; const char* GetString##ENUM_NAME(enum \ + tag##ENUM_NAME index){ return gs_##ENUM_NAME [index]; } +#endif diff --git a/engine/EventsBase.cc b/engine/EventsBase.cc new file mode 100644 index 0000000..221055b --- /dev/null +++ b/engine/EventsBase.cc @@ -0,0 +1,88 @@ +#include "EventsBase.h" +#include "Logging.h" + +namespace Engine { + +EventManager* EventManagerInstance = NULL; + +int EventManager::OnInit (int argc, char* argv[]) { + EventManagerInstance = this; + + return 0; +} + +void EventManager::OnDestroy() { + EventManagerInstance = NULL; +} + +bool EventManager::RegisterListener (const EventListenerBase *listener, const int event_type) { + LogDebug ("Registering Event listener %x for event type %d", listener, event_type); + + mEventTypeListeners[event_type].push_back(listener); + return true; +} + +void EventManager::Process () { + while (mQueuedEvents.size() > 0) { + TriggerEvent (mQueuedEvents.front()); + mQueuedEvents.pop(); + } +} + +bool EventManager::QueueEvent (const EventBasePtr &event) { + if (!HasEventTypeListener(event->mEventType)) + return false; + + mQueuedEvents.push (event); + + return true; +} + +bool EventManager::TriggerEvent (const EventBasePtr &event) { + if (!HasEventTypeListener (event->mEventType)) + return false; + + std::vector::iterator listener_iter = mEventTypeListeners[event->mEventType].begin(); + for ( ; listener_iter != mEventTypeListeners[event->mEventType].end(); listener_iter++) { + if ((*listener_iter)->HandleEvent (event)) + return true; + } + + return false; +} + +/* + * Global Functions + */ +/** \brief Registers a listener to a given event type */ +bool RegisterListener (const EventListenerBase *listener, const int event_type) { + if (!EventManagerInstance) { + LogError ("Could not register EventListenerBase: EventManager not initialized!"); + return false; + } + + return EventManagerInstance->RegisterListener (listener, event_type); +} + +/** \brief Calls all event listeners to handle the events */ +bool QueueEvent (const EventBasePtr &event) { + if (!EventManagerInstance) { + LogError ("Could not queue event: EventManager not initialized!"); + return false; + } + + return EventManagerInstance->QueueEvent (event); +} + +/** \brief Calls the listener handlers immediately */ +bool TriggerEvent (const EventBasePtr &event) { + if (!EventManagerInstance) { + LogError ("Could not trigger event: EventManager not initialized!"); + return false; + } + + return EventManagerInstance->TriggerEvent (event); +} + +}; + diff --git a/engine/EventsBase.h b/engine/EventsBase.h new file mode 100644 index 0000000..fa3be18 --- /dev/null +++ b/engine/EventsBase.h @@ -0,0 +1,88 @@ +#ifndef EVENTSBASE_H +#define EVENTSBASE_H + +#include "Module.h" + +#include + +#include +#include +#include +#include + +namespace Engine { + +/** \brief Contains all information relevant to the Event */ +struct EventBase { + int mEventType; + + /** \brief This might later be used for de-/serializing of the event data */ + std::string mEventData; + float mEventFloat; + int mEventInt; + unsigned int mEventUnsignedInt; +}; + +typedef boost::shared_ptr EventBasePtr; + +/** \brief Takes care of the handling of the Event + * + * Processing of the Event is done in the EventListenerBase::HandleEvent() + * function. + */ +class EventListenerBase { + public: + virtual ~EventListenerBase() {}; + /** \brief Handles the Event */ + virtual bool HandleEvent (const EventBasePtr &event) const = 0; + void just_for_the_vtable() {}; + + /** \brief For debugging */ + std::string mName; +}; + +typedef boost::shared_ptr EventListenerPtr; + +/** \brief Keeps track of all the EventListenerBase objects and receives Event notifications. + */ +class EventManager : public Module { + public: + virtual ~EventManager() {}; + + /** \brief Registers a listener to a given event type */ + bool RegisterListener (const EventListenerBase *listener, const int event_type); + /** \brief Calls all event listeners to handle the events */ + void Process (); + /** \brief Queues the until Process() gets called */ + bool QueueEvent (const EventBasePtr &event); + /** \brief Calls the listener handlers immediately */ + bool TriggerEvent (const EventBasePtr &event); + + /** \brief Returns true if there is a listener for a given event type */ + bool HasEventTypeListener (const int event_type) { + return mEventTypeListeners.find(event_type) != mEventTypeListeners.end(); + } + /** \brief Returns the number of listeners for a given event type */ + unsigned int GetEventTypeListenerCount (const int event_type) { + if (!HasEventTypeListener (event_type)) + return 0; + + return (mEventTypeListeners.find(event_type))->second.size(); + } + unsigned int GetQueuedEventCount () { + return mQueuedEvents.size(); + } + + protected: + virtual int OnInit (int argc, char* argv[]); + virtual void OnDestroy(); + + private: + /** \brief Contains for each event type the list of listeners */ + std::map > mEventTypeListeners; + std::queue mQueuedEvents; +}; + +} + +#endif /* EVENTSBASE_H */ diff --git a/engine/EventsBaseGlobal.h b/engine/EventsBaseGlobal.h new file mode 100644 index 0000000..f83a8b8 --- /dev/null +++ b/engine/EventsBaseGlobal.h @@ -0,0 +1,16 @@ +#ifndef EVENTSBASEGLOBAL_H +#define EVENTSBASEGLOBAL_H + +#include "EventsBase.h" + +namespace Engine { + +/** \brief Registers a listener to a given event type */ +bool RegisterListener (const EventListenerBase *listener, const int event_type); +/** \brief Calls all event listeners to handle the events */ +bool QueueEvent (const EventBasePtr &event); +/** \brief Calls the listener handlers immediately */ +bool TriggerEvent (const EventBasePtr &event); + +} +#endif /* EVENTSBASEGLOBAL_H */ diff --git a/engine/GameEntityBase.cc b/engine/GameEntityBase.cc new file mode 100644 index 0000000..147f875 --- /dev/null +++ b/engine/GameEntityBase.cc @@ -0,0 +1,7 @@ +#include "EntityBase.h" +#include "ModelBase.h" + +namespace Engine { + +} + diff --git a/engine/Logging.cc b/engine/Logging.cc new file mode 100644 index 0000000..e9a8479 --- /dev/null +++ b/engine/Logging.cc @@ -0,0 +1,173 @@ +#include "Logging.h" + +#include +#include +#include +#include + +namespace Engine { + +static Logging *LoggingInstance = NULL; +static LogLevel requested_level = LOG_DEFAULT_LEVEL; + +/* + * Inherited Module functions + */ +int Logging::OnInit (int argc, char* argv[]) { + mPrintLevel = requested_level; + + Log (LogLevelDebug, "Logging Init"); + LoggingInstance = this; + + return 0; +} + +void Logging::OnDestroy () { + Log (LogLevelDebug, "Logging Destroy"); + + LoggingInstance = NULL; +} + +/* + * Module specific functions + */ +void Logging::Log (LogLevel level, const char *str, ...) { + if (level < mPrintLevel) + return; + + static char msg[LOG_MAX_MESSAGE_LENGTH]; + static int last_length = LOG_MAX_MESSAGE_LENGTH; + static int i; + + for (i = 0; i < last_length; i++) + msg[i] = 0; + + va_list ap; + va_start(ap, str); + vsprintf(msg, str, ap); + va_end(ap); + + last_length = strlen (msg); + assert (last_length < LOG_MAX_MESSAGE_LENGTH); + + std::cout << msg << std::endl; + + LogEntry log_entry (level, msg); + mLogEntries.push_back (log_entry); +} + +void Logging::SetLogPrintLevel (LogLevel print_level) { + mPrintLevel = print_level; +} + +const LogEntry &Logging::GetLastEntry () { + static LogEntry null_message (LogLevelMessage, ""); + + if (mLogEntries.size() > 0) { + return mLogEntries[mLogEntries.size() - 1]; + } + + return null_message; +} + +/* + * Globally visible functions + */ +void SetLogPrintLevel (LogLevel print_level) { + if (!LoggingInstance) { + requested_level = print_level; + return; + } + + LoggingInstance->SetLogPrintLevel (print_level); +} + +void LogError (const char* str, ...) { + assert (LoggingInstance); + + static char msg[LOG_MAX_MESSAGE_LENGTH]; + static int last_length = LOG_MAX_MESSAGE_LENGTH; + static int i; + + for (i = 0; i < last_length; i++) + msg[i] = 0; + + va_list ap; + va_start(ap, str); + vsprintf(msg, str, ap); + va_end(ap); + + last_length = strlen (msg); + assert (last_length < LOG_MAX_MESSAGE_LENGTH); + + LoggingInstance->Log (LogLevelError, msg); +} + +void LogWarning (const char* str, ...) { + assert (LoggingInstance); + + static char msg[LOG_MAX_MESSAGE_LENGTH]; + static int last_length = LOG_MAX_MESSAGE_LENGTH; + static int i; + + for (i = 0; i < last_length; i++) + msg[i] = 0; + + va_list ap; + va_start(ap, str); + vsprintf(msg, str, ap); + va_end(ap); + + last_length = strlen (msg); + assert (last_length < LOG_MAX_MESSAGE_LENGTH); + + LoggingInstance->Log (LogLevelWarning, msg); +} + +void LogMessage (const char* str, ...) { + assert (LoggingInstance); + + static char msg[LOG_MAX_MESSAGE_LENGTH]; + static int last_length = LOG_MAX_MESSAGE_LENGTH; + static int i; + + for (i = 0; i < last_length; i++) + msg[i] = 0; + + va_list ap; + va_start(ap, str); + vsprintf(msg, str, ap); + va_end(ap); + + last_length = strlen (msg); + assert (last_length < LOG_MAX_MESSAGE_LENGTH); + + LoggingInstance->Log (LogLevelMessage, msg); +} + +void LogDebug (const char* str, ...) { + assert (LoggingInstance); + static char msg[LOG_MAX_MESSAGE_LENGTH]; + static int last_length = LOG_MAX_MESSAGE_LENGTH; + static int i; + + for (i = 0; i < last_length; i++) + msg[i] = 0; + + va_list ap; + va_start(ap, str); + vsprintf(msg, str, ap); + va_end(ap); + + last_length = strlen (msg); + assert (last_length < LOG_MAX_MESSAGE_LENGTH); + + LoggingInstance->Log (LogLevelDebug, msg); +} + +const LogEntry& GetLastLogEntry () { + assert (LoggingInstance); + return LoggingInstance->GetLastEntry (); +} + +} diff --git a/engine/Logging.h b/engine/Logging.h new file mode 100644 index 0000000..6bb401a --- /dev/null +++ b/engine/Logging.h @@ -0,0 +1,34 @@ +#ifndef _LOGGING_H +#define _LOGGING_H + +#include "Engine.h" + +namespace Engine { +class Module; + +/** \brief All logging goes through this class + */ +class Logging : public Module { + public: + void Log (LogLevel level, const char *str, ...); + void SetLogPrintLevel (LogLevel print_level); + /** \brief Returns the last LogEntry that was sent to the Logging module + */ + const LogEntry& GetLastEntry (); + + protected: + /** \brief Initializes the system */ + virtual int OnInit (int argc, char* argv[]); + /** \brief Destroys the system (must be called!) */ + virtual void OnDestroy (); + + private: + LogLevel mPrintLevel; + /** \brief Stores all log messages that were sent to the Logging module + * \todo Restrict the number of entries to be stored! + */ + std::vector mLogEntries; +}; + +} +#endif // _LOGGING_H diff --git a/engine/LoggingGlobal.h b/engine/LoggingGlobal.h new file mode 100644 index 0000000..ffe5322 --- /dev/null +++ b/engine/LoggingGlobal.h @@ -0,0 +1,45 @@ +#ifndef _LOGGINGLOBAL_H +#define _LOGGINGLOBAL_H + +namespace Engine { + +enum LogLevel { + LogLevelDebug = 0, + LogLevelWarning, + LogLevelMessage, + LogLevelError +}; + +/** \brief Represents a log message along with its level + */ +struct LogEntry { + LogEntry (LogLevel level, const char* message) { + mLevel = level; + mMessage = message; + } + + /** \brief the level of the message */ + LogLevel mLevel; + /** \brief the message itself */ + const char *mMessage; +}; + +/* Global visible functions */ + +/** \brief Sets the level for which messages should be printed out */ +void SetLogPrintLevel (LogLevel print_level); + +/** \brief Sends the Message to the Logging system */ +void LogError (const char* str, ...); +/** \brief Sends the Message to the Logging system */ +void LogWarning (const char* str, ...); +/** \brief Sends the Message to the Logging system */ +void LogMessage (const char* str, ...); +/** \brief Sends the Message to the Logging system */ +void LogDebug (const char* str, ...); +/** \brief Returns the last LogEntry sent to the Logging system */ +const LogEntry &GetLastLogEntry (); + +} + +#endif // _LOGGINGLOBAL_H diff --git a/engine/ModelBase.cc b/engine/ModelBase.cc new file mode 100644 index 0000000..62cc529 --- /dev/null +++ b/engine/ModelBase.cc @@ -0,0 +1,295 @@ +#include "ModelBase.h" + +#include "PhysicsBase.h" +#include "EntityBase.h" +#include "EntityFactoryBase.h" + +#include + +#include + +namespace Engine { + +static ModelBase* ModelInstance = NULL; + +/* + * Inherited Module functions + */ +int ModelBase::OnInit (int argc, char* argv[]) { + LogDebug ("Model Init"); + + ModelInstance = this; + mKilledEntities.clear (); + mEntityIdCounter = 0; + mDeltaSec = 0.; + + return 0; +} + +void ModelBase::OnDestroy () { + LogDebug ("Model Destroy"); + + std::map::iterator iter = mEntities.begin (); + std::map::iterator next = iter; + + // Since DestroyEntity () also erases the entry in mEntities iter gets + // invalid. Therefore we have to store the iterator that follows iter + while (iter != mEntities.end ()) { + next = iter; + next ++; + DestroyEntity (iter->first); + iter = next; + } + + ModelInstance = NULL; +} + +/* + * Model specific functions + */ +void ModelBase::Process () { + // Process the controllers and state of all entities + std::map::iterator entity_iter = mEntities.begin(); + do { + if (entity_iter->second == NULL) { + LogError ("Entity with id %d does not exist!", entity_iter->first); + assert (0); + } + entity_iter->second->Update(mDeltaSec); + entity_iter++; + } while (entity_iter != mEntities.end()); + + // simulate the world + mPhysics->Simulate (mDeltaSec, this); + + // remove killed entities + unsigned int i; + for (i = 0; i < mKilledEntities.size(); i++) + UnregisterEntity (mKilledEntities[i]); + + mKilledEntities.clear(); +} + +EntityBase* ModelBase::CreateEntity (int type) { + // Create a new entity id + unsigned int entity_id = CreateEntityId(); + + // Call the event + OnCreateEntity (type, entity_id); + + // Create the entity + EntityBase* result = mEntityFactory->CreateEntity (type); + result->SetId (entity_id); + + // And register it + RegisterEntity (result); + + return result; +} + +void ModelBase::RegisterEntity (EntityBase* entity) { + unsigned int id = entity->mId; + LogDebug ("Registering Entity with id '%d'", id); + + if (mEntities.find(id) != mEntities.end ()) { + LogError ("Replacing Entity with id '%d'", id); + assert (0); + } + + if (entity->mPhysicState) + mPhysics->RegisterEntity (entity->mPhysicState); + + mEntities[id] = entity; +} + +void ModelBase::KillEntity (const unsigned int id) { + std::map::iterator iter = mEntities.find (id); + + if (iter == mEntities.end ()) { + LogError ("Could not kill Entity with id '%d': Entity not found!", id); + assert (0); + return; + } else { + EntityBase *entity = iter->second; + + // call the event handler + OnKillEntity (entity); + + if (entity->mPhysicState) { + entity->mPhysicState->mAlive = false; + } + + mKilledEntities.push_back (iter->first); + } +} + +void ModelBase::UnregisterEntity (const unsigned int id) { + std::map::iterator iter = mEntities.find (id); + + if (iter == mEntities.end ()) { + LogError ("Could not unregister Entity with id '%d': Entity not found!", id); + assert (0); + return; + } else { + EntityBase *entity = iter->second; + if (entity->mPhysicState) { + mPhysics->UnregisterEntity (id); + entity->mPhysicState = NULL; + } + + delete entity; + + mEntities.erase (iter); + } +} + +EntityBase* ModelBase::GetEntity (const unsigned int id) { + std::map::iterator iter = mEntities.find (id); + + if (iter != mEntities.end ()) { + return iter->second; + } + + return NULL; +} + +unsigned int ModelBase::CreateEntityId () { + return ++mEntityIdCounter; +} + +void ModelBase::ClearEntities () { + LogDebug ("Clearing all %d entities.", mEntities.size()); + + std::map::iterator iter = mEntities.begin (); + std::map::iterator next = iter; + + // Since DestroyEntity () also erases the entry in mEntities iter gets + // invalid. Therefore we have to store the iterator that follows iter + while (iter != mEntities.end ()) { + next = iter; + next ++; + DestroyEntity (iter->first); + iter = next; + } + + mEntityIdCounter = 0; +} + +unsigned int ModelBase::GetPlayerEntityId () { + return mPlayerEntityId; +} + +/** + * \param collision_time The time when the collision occured relative to the start of the simulation frame + */ +void ModelBase::SendEntityCollisionEvent (const unsigned int reference_entity_id, + const unsigned int incidence_entity_id, float collision_time, vector3d normal) { + // Check whether we report the same contact over and over + static unsigned int last_reference_entity_id = 0; + static unsigned int last_incidence_entity_id = 0; + static float last_collision_time = -1.; + + if (!reference_entity_id || !incidence_entity_id) + return; + + if (last_reference_entity_id != 0) { + if (last_reference_entity_id == reference_entity_id + && last_incidence_entity_id == incidence_entity_id + && last_collision_time == collision_time) { + return; + } + } + + // update the last_* entries + last_reference_entity_id = reference_entity_id; + last_incidence_entity_id = incidence_entity_id; + last_collision_time = collision_time; + + // now we need to get the Entities and check whether we should + // call EntityBase::CollisionEvent() + EntityBase* reference_entity = GetEntity (reference_entity_id); + EntityBase* incidence_entity = GetEntity (incidence_entity_id); + + LogDebug ("Received collision type ref = %d type inc = %d", + reference_entity->mType, + incidence_entity->mType); + + // If the incidence entity accepted the collision we can return, otherwise + // we perform the symmetric collision event. + if (incidence_entity->CollisionEvent (reference_entity)) + return; + + reference_entity->CollisionEvent (incidence_entity); +} + +/* + * Global functions + */ +unsigned int GetPlayerEntityId () { + if (!ModelInstance) { + LogError ("Couldn't create Entity: Model not initialized!"); + assert (0); + } + + return ModelInstance->GetPlayerEntityId (); +} + +float GetFrameDuration () { + if (!ModelInstance) { + LogError ("Couldn't create Entity: Model not initialized!"); + assert (0); + } + + return ModelInstance->GetFrameDuration (); +} + +EntityBase * CreateEntity (int type) { + if (!ModelInstance) { + LogError ("Couldn't create Entity: Model not initialized!"); + assert (0); + } + + EntityBase *result = ModelInstance->CreateEntity (type); + + return result; +} + +void DestroyEntity (unsigned int id) { + if (!ModelInstance) { + LogError ("Couldn't destroy Entity: Model not initialized!"); + assert (0); + } + + ModelInstance->UnregisterEntity (id); +} + +void KillEntity (unsigned int id) { + if (!ModelInstance) { + LogError ("Couldn't kill Entity: Model not initialized!"); + assert (0); + } + + ModelInstance->KillEntity (id); +} + +EntityBase * GetEntity (unsigned int id) { + if (!ModelInstance) { + LogError ("Couldn't execute GetEntity(): Model not initialized!"); + assert (0); + } + + return ModelInstance->GetEntity (id); +} + +EntityPhysicState * GetEntityPhysicState (unsigned int id) { + EntityBase *entity = GetEntity (id); + + if (entity) + return entity->mPhysicState; + + return NULL; +} + +} + + diff --git a/engine/ModelBase.h b/engine/ModelBase.h new file mode 100644 index 0000000..6b86498 --- /dev/null +++ b/engine/ModelBase.h @@ -0,0 +1,140 @@ +#ifndef _MODELBASE_H +#define _MODELBASE_H + +#include "Engine.h" +#include "EntityBase.h" + +namespace Engine { + +class Module; +class PhysicsBase; +class Events; +class EntityFactoryBase; + +struct EntityBase; + +/** \brief Represents the current state of the Engine + * + * Represents the State of the Engine and is unaware of anything except itself. + * It manages all Entities, Physics, etc. + * + * It has the following subsystems: + * - Events + * - Physics + * - Variables + * + * \note + * To create an Entity one must call Engine::CreateEntity() which will + * allocate the Entity and performs all the required registrations by calling + * Model::RegisterEntity(). + */ +class ModelBase : public Module { + public: + /** Performs simulation, gamelogic, etc */ + virtual void Process (); + /** Updates the timer */ + void UpdateTimer () { + static float last_frame = 0; + float current_frame = static_cast (SDL_GetTicks ()) * 1.0e-3; + mDeltaSec = current_frame - last_frame; + last_frame = current_frame; + } + float GetFrameDuration () { + return mDeltaSec; + } + + /** Adds the given Entity to the Model */ + void RegisterEntity (EntityBase *entity); + /** Removes the Entity with the given id */ + void UnregisterEntity (const unsigned int id); + /** Marks an Engine::Entity as killed so that it gets removed after the + * simulation */ + void KillEntity (const unsigned int id); + + /** Creates an Entity of the given type + * + * This calls the CreateEntity() function on the Model::mEntityFactory to + * create the proper Entity and registers it to the required submodules + * (so far only Model::mPhysics). + */ + EntityBase* CreateEntity (int type); + /** Returns the Entity with the given id */ + EntityBase* GetEntity (const unsigned int id); + /** Returns a unused id for an Entity */ + unsigned int CreateEntityId (); + /** Removes all Entities */ + void ClearEntities (); + + /** Returns the id of the entity the player is currently controlling */ + unsigned int GetPlayerEntityId (); + + /** Notifies the gamelogic of a collision event */ + void SendEntityCollisionEvent (const unsigned int reference_entity_id, + const unsigned int incidence_entity_id, float collision_time, vector3d normal); + + protected: + /** \brief Initializes the system */ + virtual int OnInit (int argc, char* argv[]); + /** \brief Destroys the system (must be called!) */ + virtual void OnDestroy (); + + /** \brief Gets called when an entity of the given type is being created */ + virtual void OnCreateEntity (const int type, const unsigned int id) {}; + /** \brief Gets called when an entity is marked to be killed */ + virtual void OnKillEntity (const EntityBase *entity) {}; + + + PhysicsBase *mPhysics; + Events *mEvents; + + /** \brief Creates Entities */ + EntityFactoryBase *mEntityFactory; + + /** \brief contains all Engine::Entities */ + std::map mEntities; + /** \brief contains all Engine::Entities that ceased to exist */ + std::vector mKilledEntities; + + unsigned int mEntityIdCounter; + unsigned int mPlayerEntityId; + + float mDeltaSec; + + friend class ViewBase; + friend class Engine; + friend class Controller; +}; + +/** \brief Creates an Entity of the given type and registers at the required modules * + * + * It allocates the memory needed to represent that specific type of Entity. + * At also performs the required registrations at the various Modules. + * + * All Entities that are created with this function get also destroyed in + * Model::Destroy () by calling DestroyEntity () for this Entity. + * + * \note All Entities must be created with this function if you intend to keep + * the different Modules in sync! + */ +EntityBase * CreateEntity (int type); + +/** \brief Frees the Memory required by the Entity and unregisters it + * + * This function also frees the memory the Entity is using in its + * different representitions such as EntityVisualState and EntityPhysicState. + * + * It also unregisters the Entity from the different Modules where it was + * registered by CreateEntity. + */ +void DestroyEntity (unsigned int id); +/** \brief Tells the model that the entity is no more existant in the game + * world */ +void KillEntity (unsigned int id); +/** \brief Returns the Entity with the given id, NULL otherwise */ +EntityBase * GetEntity (unsigned int id); +/** \brief Returns the physical state of the Entity with the given id, NULL otherwise */ +EntityPhysicState * GetEntityPhysicState (unsigned int id); + +} + +#endif // _MODEL_H diff --git a/engine/ModelBaseGlobal.h b/engine/ModelBaseGlobal.h new file mode 100644 index 0000000..c17ff8b --- /dev/null +++ b/engine/ModelBaseGlobal.h @@ -0,0 +1,15 @@ +#ifndef _MODELGLOBAL_H +#define _MODELGLOBAL_H + +namespace Engine { + +/** \brief Adds the function callback as command for the given name*/ +unsigned int GetPlayerEntityId (); + +/** \brief Returns the duration of the frame in seconds */ +float GetFrameDuration (); + +} + +#endif /* _MODELGLOBAL_H */ + diff --git a/engine/Module.h b/engine/Module.h new file mode 100644 index 0000000..d7ac06a --- /dev/null +++ b/engine/Module.h @@ -0,0 +1,51 @@ +#ifndef MODULE_H +#define MODULE_H + +namespace Engine { + +/** \brief Base class for the separate modules + * + * All Modules that are managed by the Engine::Engine base Module. Everything + * else is a Submodule of that or a Submodule of a Submodule. + */ +class Module { + public: + /** \brief Initializes the Module + * + * \note This function must only return if it was able to initialize + * successfully, otherwise it should log an error with LogError and exit + * the program itself! + * + * In general at first the Submodules get initialized and then the Module + * that has pointers to the Submodules. Exception: The main Module + * Engine::Engine. + * + * \note + * The function Engine::OnInit () performs the allocation + * and Initialization of all its Submodules. + */ + virtual int Init (int argc, char* argv[]) { return OnInit (argc, argv); } + /** \brief Frees the used memory of the Module + * + * The Destroy function is always called at first for the Module itself + * and then for its submodules so that a Module is still completely + * working. Again for the main Module Engine::Engine the reverse is the + * case (such as in Module::Init). + */ + virtual void Destroy () { OnDestroy (); } + /** \brief Calls the function that registers its commands to the + * Commandsystem */ + virtual void RegisterCommands () { OnRegisterCommands (); } + + protected: + /** \brief The actual function being called when Init () is called */ + virtual int OnInit (int argc, char* argv[]) = 0; + /** \brief Frees the memory */ + virtual void OnDestroy () { }; + /** \brief Registers the commands of the Module */ + virtual void OnRegisterCommands () { }; +}; + +} + +#endif /* MODULE_H */ diff --git a/engine/OverlayBase.h b/engine/OverlayBase.h new file mode 100644 index 0000000..b2644e5 --- /dev/null +++ b/engine/OverlayBase.h @@ -0,0 +1,25 @@ +#ifndef OVERLAY +#define OVERLAY + +#include + +namespace Engine { + +class OverlayBase { + public: + OverlayBase () {}; + virtual ~OverlayBase() {}; + + virtual bool OnKeyDown (const SDL_keysym &keysym) { return false; }; + virtual bool OnKeyUp (const SDL_keysym &keysym) { return false; }; + virtual bool OnMouseButtonUp (Uint8 button, Uint16 xpos, Uint16 ypos) { return false; }; + virtual bool OnMouseButtonDown (Uint8 button, Uint16 xpos, Uint16 ypos) { return false; }; + + virtual void Draw () = 0; + + void _strange_function_for_vtable () {}; +}; + +} + +#endif /* OVERLAY */ diff --git a/engine/PhysicsBase.cc b/engine/PhysicsBase.cc new file mode 100644 index 0000000..6a4a64f --- /dev/null +++ b/engine/PhysicsBase.cc @@ -0,0 +1,604 @@ +#include "PhysicsBase.h" +#include "EntityBase.h" + +// needed for GetEntityGameState() in ProcessCollisionEvent() +#include "ModelBase.h" + +#include "coll2d.h" + +#define EPSILON 1.0e-4 + +namespace Engine { + +/* + * Inherited Module functions + */ +int PhysicsBase::OnInit (int argc, char* argv[]) { + LogDebug ("Physics Init"); + mEntities.clear (); + + return 0; +} + +void PhysicsBase::OnDestroy () { + LogDebug ("Physics Destroy"); +} + +/* + * Module specific functions + */ + +void PhysicsBase::Move (float delta_msec) { + EntityPhysicState* entity = NULL; + std::map::iterator entity_iter; + std::map::iterator collision_iter; + vector3d velocity, orientation; + + for (entity_iter = mEntities.begin (); entity_iter != mEntities.end(); entity_iter++){ + entity = entity_iter->second; + velocity = entity->GetVelocity (); + + if (velocity.length2() == 0.) + continue; + + CheckContactCache (entity); + + entity->SetPosition (entity->GetPosition () + velocity * delta_msec); + entity->SetOrientation (vector3d(0., entity->GetOrientation()[1] + entity->GetAngleVelocity() * delta_msec, 0.)); + } +} + +int PhysicsBase::Simulate (float msec, ModelBase* model) { + vector3d velocity, orientation; + + unsigned int entity_a_id, entity_b_id; + int resolve_steps = 0;; + float current_time = 0, stepsize = msec / (float) 10; + + while (current_time < msec) { + if (msec - current_time < stepsize) + stepsize = msec - current_time; + + coll2d::CollisionInfo info; + bool collision_result = false; + + collision_result = CalcNextCollision (stepsize, entity_a_id, entity_b_id, info); + + if (collision_result == 0) { + // there is no collision, so we integrate to the end of + // the timestep + current_time += stepsize; + Move (stepsize); + } else { + LogDebug ("Collision between %u and %u", entity_a_id, entity_b_id); + float collision_remaining_step = stepsize; + resolve_steps = 0; + + // This is the collision loop. It loops until there is no collision any + // more within the stepsize. + while (collision_remaining_step > 0.) { + // with this we count how often we iterate through and can + // detect certain false behaviour + resolve_steps ++; + + // info.time tells us in what time the contact will happen and + // theoretically we can move to that point, however due to round-off + // errors we might end up in a situation where the Entities overlap + // which we must prevent to keep the collision detection working. + // + // Therefore we only move to a portion of the time and resolve the + // collision then. This alpha variable controls how much of the time + // we skip. + float alpha = 0.001; + + // If the timestep is already very small, we try to resolve it + // immediately. + if (info.time < EPSILON ) { + // if the timestep is too small we simply resolve the collision and + // do not move beforehang + LogDebug ("Time step too small ==> Resolving Step immediately"); + ResolveCollision (0., entity_a_id, entity_b_id, info); + // Send the collision event to the Model (if we have one) + if (model) { + model->SendEntityCollisionEvent (entity_a_id, entity_b_id, info.time, info.normal); + } + // collision_remaining_step -= 1.0e-4; + } else { + Move (info.time * (1 - alpha)); + LogDebug ("Resolving Step between %u and %u t = %f alpha = %f", entity_a_id, entity_b_id, info.time, alpha); + ResolveCollision (info.time * (1 - alpha), entity_a_id, entity_b_id, info); + // Send the collision event to the Model (if we have one) + if (model) { + model->SendEntityCollisionEvent (entity_a_id, entity_b_id, info.time, info.normal); + } + collision_remaining_step -= info.time * (1 - alpha); + } + + // At this point we resolved the collision we had found. Now we need + // to check whether there is a new one + collision_result = CalcNextCollision (collision_remaining_step, entity_a_id, entity_b_id, info); + + // If there was none, we happily move on to the end of the frame and + // set collision_remaining_step to 0 so that we jump out of the + // collision loop. + if (!collision_result) { + Move (collision_remaining_step); + collision_remaining_step = 0.; + break; + } + + assert (resolve_steps <= 50); + } + } + } + /* Postcondition: we simulated exactly msec milliseconds, no less, no more! */ + assert (current_time == msec); + + return 0; +} + +void PhysicsBase::RegisterEntity (EntityPhysicState* entity) { + unsigned int id = entity->mId; + LogDebug ("Registering EntityPhysicState with id '%d'", id); + if (mEntities.find (id) == mEntities.end ()) + mEntities[id] = entity; + else { + LogError ("Physics already has registered an Entity with id '%d'", id); + assert (0); + } +} + +void PhysicsBase::UnregisterEntity (const unsigned int id) { + std::map::iterator iter = mEntities.find(id); + LogDebug ("Unegistering EntityPhysicState with id '%d'", id); + + if (iter != mEntities.end ()) { + // Remove all the references of existing contacts to the Entity that is + // going to be deleted. + std::map::iterator temp_iter, contact_iter = iter->second->mContactNormals.begin(); + + while (contact_iter != iter->second->mContactNormals.end()) { + temp_iter = contact_iter; + contact_iter ++; + + ContactCacheRemove (id, temp_iter->first); + } + + // Remove the Entity from mEntities and erase the Entity + EntityPhysicState* entity_physic_state = iter->second; + mEntities.erase (iter); + + // We also have to delete the state! + if (entity_physic_state->mShape) + delete entity_physic_state->mShape; + + delete entity_physic_state; + } else { + LogError ("Could not unegister EntityPhysicState with id '%d': Entity not found!", id); + assert (0); + } +} + +/** + * This function also sorts out collisions that might be invalid such as + * Particle-Particle collision and the like. + */ +inline bool PhysicsBase::CheckPossibleCollisionPair (EntityPhysicState* entity_a, EntityPhysicState* entity_b) { + // no collision checks against itself + if (entity_a == entity_b) + return false; + + // no checks if both are static + if (entity_a->mStatic && entity_a->mStatic) + return false; + + // no checks if both are particles + if (entity_a->mBaseType == EntityBaseTypeParticle + && entity_b->mBaseType == EntityBaseTypeParticle) + return false; + + // no checks if both are particles + if (entity_a->mBaseType == EntityBaseTypeBlock + && entity_b->mBaseType == EntityBaseTypeBlock) + return false; + + return true; +} + +/** + * \param stepsize The timestep for which we want to check whether a + * collision occurs. + * \param reference_entity_id If a collision occurs the Id of the entity that + * is the reference entity is stored in this variable. + * \param incidence_entity_id If a collision occurs the Id of the entity that + * crashes into the reference entity is stored in this variable. + * \param info Contains information about the collision (normal, time, point) + * + * This function calls PhysicsBase::CheckPossibleCollisionPair() to sort out + * invalid collision pairs. + */ +bool PhysicsBase::CalcNextCollision ( + float stepsize, + unsigned int &reference_entity_id, + unsigned int &incidence_entity_id, + coll2d::CollisionInfo &info) { + std::map::iterator collision_iter; + std::map::iterator collision_ref; + + // We have to find the next collision, so we check everything and + // take the first one that will happen. + coll2d::CollisionInfo temp_info; + info.time = 2.; + + for (collision_ref = mEntities.begin (); collision_ref != mEntities.end (); collision_ref ++) { + if (collision_ref->second->mId != 0) { + collision_ref->second->UpdateShape(); + } + } + + for (collision_ref = mEntities.begin (); collision_ref != mEntities.end (); collision_ref ++) { + if (collision_ref->second->mAlive == false) + continue; + for (collision_iter = collision_ref; collision_iter != mEntities.end (); collision_iter ++) { + if (collision_iter->second->mAlive == false) + continue; + + if (!CheckPossibleCollisionPair(collision_ref->second, collision_iter->second)) + continue; + + EntityPhysicState *entity_a, *entity_b; + entity_a = collision_ref->second; + entity_b = collision_iter->second; + + int coll2d_result = coll2d::check_collision (stepsize, entity_a->mShape, entity_b->mShape, &temp_info); + + if (!HandleColl2dError (coll2d_result, stepsize, entity_a, entity_b, temp_info)) { + LogError ("Could not handle coll2d error: %d\n", coll2d_result); + assert (0); + } + + if (coll2d_result > 0 && temp_info.time < info.time ) { + info = temp_info; + + assert (info.reference_shape >= 0); + if (info.reference_shape == 0) { + reference_entity_id = collision_ref->first; + incidence_entity_id = collision_iter->first; + } else { + reference_entity_id = collision_iter->first; + incidence_entity_id = collision_ref->first; + //assert (0); + } + } + } + } + if (info.time != 2.) + return true; + + return false; +} + +/** + * This function updates the velocity of the Entity with id of + * incidence_entity_id so that it no more is in collision with with Entity + * with id reference_entity_id. Additionally to that we also cache the + * contact normals and check against them so that the new velocity does not + * violate previous contacts. + * + * We store the cached contact normals both in the incidence and the reference + * entity and we have to make sure that these references get cleared once one + * of the two get deleted! This is done in PhysicsBase::UnregisterEntity(). + */ +void PhysicsBase::ResolveCollision (float stepsize, unsigned int reference_entity_id, unsigned int incidence_entity_id, coll2d::CollisionInfo &info) { + EntityPhysicState* reference_entity = mEntities[reference_entity_id]; + EntityPhysicState* incidence_entity = mEntities[incidence_entity_id]; + + if (!reference_entity || !incidence_entity) { + LogError ("Invalid entity IDs passed to %s: %u and %u", __FUNCTION__, reference_entity_id, incidence_entity_id); + } + assert (reference_entity && incidence_entity); + + // So far only resolving of collision of sphere collisions is + // allowed + assert (dynamic_cast (incidence_entity->mShape)); + + // First we calculate the velocity along the normal and then we calculate + // the velocity that is tangential to the plane and set the new velocity to + // it. + vector3d new_velocity = incidence_entity->mVelocity; + new_velocity -= reference_entity->mVelocity; + + // It should be greater zero otherwise there was an error in the collision + // detection. + assert (new_velocity.length2 ()); + + // The scalar proj tells us how far we went along the normal + float proj = new_velocity * info.normal; + // As there is a collision, and the incidence_entity is moving towards the + // plane, this value must be strictly smaller than zero, otherwise we would + // not penetrate the plane. + if (proj > 0.) { + LogError ("Projection invalid: %e", proj); + info.doPrint ("Collision Info:\n"); + } + assert (proj < 0.); + + // Collision handling start: + // This is the code that tells us how we deal with collisions and how to + // prevent them. To get real dynamics one has to adjust this section: + vector3d old_velocity = incidence_entity->mVelocity; + new_velocity = old_velocity + info.normal * proj * (-1.0); + incidence_entity->SetVelocity (new_velocity); + // Collision handling end + + // And we also add the normal to the cached contacts for both incidence + // and reference entity + ContactCacheAdd (incidence_entity, reference_entity, info.normal); + + // Now we check whether we are violating any of the cached contacts. For + // this we loop through all contacts and try to adjust the velocity of + // incidence_entity until we no more violate the cached contacts. + std::map::iterator iter = incidence_entity->mContactNormals.begin(); + int readjust = 0; + while (iter != incidence_entity->mContactNormals.end()) { + vector3d contact_normal = iter->second; + float contact_velocity = new_velocity * contact_normal; + + if (contact_velocity < 0.) { + if (reference_entity_id == iter->first) { + // In this case, the projection was not good enough. We simultaneously + // damp the velocity and push a little harder. If it was damped too + // much or we had to readjust too often, we set the velocity to zero. + LogDebug ("Resolved collision needs to be readjusted"); + new_velocity = incidence_entity->GetVelocity(); + new_velocity *= 0.5; + if (new_velocity.length2() < EPSILON) + new_velocity.setValues (0., 0., 0.); + else + new_velocity += info.normal * 0.001; + incidence_entity->SetVelocity (new_velocity); + readjust++; + + // More than 10 readjusts? -> set velocity to zero + if (readjust > 10) + new_velocity.setValues (0., 0., 0.); + + // and we redo all the checking: + iter = incidence_entity->mContactNormals.begin(); + continue; + } + + // In this case the proposed velocity is violating another contact + // and we set the velocity to zero. + LogDebug ("Cached collision: %e -> resetting velocity", contact_velocity); +// contact_normal.print ("contact normal: "); + new_velocity.setValues (0., 0., 0.); + incidence_entity->mVelocity = new_velocity; + } else if (contact_velocity > 0.01) { + // If we move sufficiently fast away from a contact we remove the + // contact from the cache. + + // * Attention! * As the function ContactCacheRemove() deletes an entry + // of mContactNormals we have to increase the iterator *before* we + // remove the cache. Otherwise the iterator might become invalid! + unsigned int contact_id = iter->first; + iter++; + + ContactCacheRemove (contact_id, incidence_entity_id); + + continue; + } + iter++; + } +} + +/* + * Contact Cache Functions + */ +void PhysicsBase::ContactCacheAdd (EntityPhysicState* incidence_entity, EntityPhysicState* reference_entity, vector3d normal) { + incidence_entity->mContactNormals[reference_entity->mId] = normal; + reference_entity->mContactNormals[incidence_entity->mId] = normal * -1.; + + LogDebug ("Adding normal (%f,%f,%f) id=%d to entity %d", + normal[0], normal[1], normal[2], + reference_entity->mId, incidence_entity->mId); + LogDebug ("Adding normal (%f,%f,%f) id=%d to entity %d", + normal[0] * -1., normal[1] * -1., normal[2] * -1., + incidence_entity->mId, reference_entity->mId); +} + +void PhysicsBase::ContactCacheRemove (unsigned int entity_a_id, unsigned int entity_b_id) { + assert (entity_a_id != entity_b_id); + +// LogDebug ("Removing start %d and %d", entity_a_id, entity_b_id); + + EntityPhysicState *entity_a, *entity_b; + +#ifdef WIN32 + entity_a = mEntities[entity_a_id]; + entity_b = mEntities[entity_b_id]; +#else + entity_a = mEntities.at(entity_a_id); + entity_b = mEntities.at(entity_b_id); +#endif + + // Check the entries exist + assert (entity_a->mContactNormals.find (entity_b_id) != entity_a->mContactNormals.end()); + assert (entity_b->mContactNormals.find (entity_a_id) != entity_b->mContactNormals.end()); + +#ifdef WIN32 + vector3d contact_normal = entity_a->mContactNormals[entity_b_id]; +#else + vector3d contact_normal = entity_a->mContactNormals.at(entity_b_id); +#endif + + LogDebug ("Removing normal (%f,%f,%f) id=%d from entity %d", + contact_normal[0], contact_normal[1], contact_normal[2], + entity_a_id, entity_b_id); + entity_a->mContactNormals.erase (entity_a->mContactNormals.find(entity_b_id)); + +#ifdef WIN32 + contact_normal = entity_b->mContactNormals[entity_a_id]; +#else + contact_normal = entity_b->mContactNormals.at(entity_a_id); +#endif + + LogDebug ("Removing normal (%f,%f,%f) id=%d from entity %d", + contact_normal[0], contact_normal[1], contact_normal[2], + entity_b_id, entity_a_id); + entity_b->mContactNormals.erase (entity_b->mContactNormals.find(entity_a_id)); + +// LogDebug ("Removing done!"); +} + +/** \brief Checks whether we are still in contact with the entities stored in mContactNormals + * + * To check whether we still are in contact, we modify temporarily the + * velocity of the given Entity that it moves towards the contact point (i.e. + * we add the negative normal to the velocity) and re-check for a collision. + * If the collision has a time value of 0.0 and the reported normal stays the + * same, we know, that the two Entities are still in contact. If not, we lost + * contact. + * + * We can skip the test, if the scalar product of the normal and velocity are + * positive (in this case we move away from the plane) + */ +void PhysicsBase::CheckContactCache (EntityPhysicState* entity) { + std::map::iterator contacts_iter, current_iter; + + contacts_iter = entity->mContactNormals.begin(); + while (contacts_iter != entity->mContactNormals.end()) { + // * Attention! * + // current_iter can be used throughout this environment and contacts_iter + // can already now be increased as it *must not* be used! This is due to + // the nature of ContactCachRemove() which might make contacts_iter + // invalid if we were using that. + current_iter = contacts_iter; + contacts_iter ++; + + unsigned int contact_entity_id = contacts_iter->first; + +#ifdef WIN32 + EntityPhysicState* contact_entity = mEntities[contact_entity_id]; +#else + EntityPhysicState* contact_entity = mEntities.at (contact_entity_id); +#endif + + vector3d normal = contacts_iter->second; + vector3d old_velocity = entity->GetVelocity(); + + // If we already move away from the normal, we delete the contact. + if (normal * old_velocity > 0.01) { + LogDebug ("Lost Contact with entity %d!", current_iter->first); + ContactCacheRemove (entity->mId, current_iter->first); + continue; + } else { + vector3d new_velocity (old_velocity); + new_velocity -= normal; + entity->SetVelocity (new_velocity); + + entity->UpdateShape(); + contact_entity->UpdateShape(); + + coll2d::CollisionInfo info; + int coll2d_result; + + coll2d_result = coll2d::check_collision (1.0, entity->mShape, contact_entity->mShape, &info); + + if (!HandleColl2dError (coll2d_result, 1.0, entity, contact_entity, info)) { + // error + LogError ("Error when performing collision check: %s\n", __FUNCTION__); + assert (0); + } + + if (coll2d_result == 0) { + // no contact, so delete it: + LogDebug ("Lost Contact with entity %d!", current_iter->first); + ContactCacheRemove (entity->mId, current_iter->first); + + entity->SetVelocity (old_velocity); + continue; + } else if ( coll2d_result > 0){ + // contact + if (info.time != 0. || normal != info.normal) { + LogError ("Something strange happened when checking for contacts in %s\n", __FUNCTION__); + assert (0); + } + } + + entity->SetVelocity (old_velocity); + } + } +} + +/** + * So far we ignore overlapping if one entity is an EntityBaseTypeActor and + * the other a EntityBaseTypeParticle. + * + * If this function returns true everything is ok and we can safely continue + * otherwise it is recommended to quit the application. + * + * \returns true if there was no error at all or we were able to deal with it + */ +bool PhysicsBase::HandleColl2dError (int coll2d_result, float stepsize, + EntityPhysicState* entity_a, EntityPhysicState* entity_b, coll2d::CollisionInfo &info) +{ + if (coll2d_result < 0) { + if (coll2d_result == CHECK_ERROR_OVERLAP) { + // this can happen if an Actor is faster than its thrown Particle, + // we ignore this for now + /// \todo Handle overlaps of Actors and Particles better or define clear guidelines. Note this also in Entity.h + if ( (entity_a->mBaseType == EntityBaseTypeParticle && entity_b->mBaseType == EntityBaseTypeActor) + || (entity_b->mBaseType == EntityBaseTypeParticle && entity_a->mBaseType == EntityBaseTypeActor) ) + LogDebug ("Ignoring CHECK_ERROR_OVERLAP"); + return true; + } else { + LogError ("coll2d Error: %d (stepsize = %f, id_a = %d, id_b = %d)", coll2d_result, stepsize, entity_a->mId, entity_b->mId); + entity_a->mShape->doPrint ("Debug: entity_a"); + entity_b->mShape->doPrint ("Debug: entity_b"); + return false; + } + } + + return true; +} + +EntityPhysicState* CreateEntityPhysicState (EntityBaseType type, unsigned int id) { + EntityPhysicState* entity_physics = new EntityPhysicState (); + if (!entity_physics) { + LogError ("Could not allocate enough memory for EntityPhysicState of type '%d'", type); + assert (0); + } + // default values for all Entities + entity_physics->mId = id; + entity_physics->mBaseType = type; + + // specific values for each Entity type + if (type == EntityBaseTypeNone) { + entity_physics->mShape = new coll2d::Sphere (0.01); + assert (entity_physics->mShape); + } else if (type == EntityBaseTypeActor) { + entity_physics->mShape = new coll2d::Sphere (0.4); + assert (entity_physics->mShape); + } else if (type == EntityBaseTypeBlock) { + entity_physics->mShape = new coll2d::Polygon (4); + assert (entity_physics->mShape); + + static_cast (entity_physics->mShape)->setVertice (0, vector3d (-0.5, 0., 0.5)); + static_cast (entity_physics->mShape)->setVertice (1, vector3d (0.5, 0., 0.5)); + static_cast (entity_physics->mShape)->setVertice (2, vector3d (0.5, 0., -0.5)); + static_cast (entity_physics->mShape)->setVertice (3, vector3d (-0.5, 0., -0.5)); + } else if (type == EntityBaseTypeParticle) { + entity_physics->mShape = new coll2d::Sphere (0.05); + assert (entity_physics->mShape); + } else { + LogError ("No EntityPhysicState defined for Entity type '%d'", type); + assert (0); + } + + return entity_physics; +} + +} + + diff --git a/engine/PhysicsBase.h b/engine/PhysicsBase.h new file mode 100644 index 0000000..9e651c1 --- /dev/null +++ b/engine/PhysicsBase.h @@ -0,0 +1,91 @@ +#ifndef _PHYSICSBASE_H +#define _PHYSICSBASE_H + +#include "Engine.h" +#include "EntityBase.h" + +namespace coll2d { + struct CollisionInfo; +} + +/// \todo get rid of this forward declaration somehow +struct vector3d; + +namespace Engine { + +class Module; +class Events; + +class EntityPhysicState; + +/** \brief Performs the physical simulation of all Entities + * + * This class defines how the physical simulation is performed. Especially how + * collisions are treated is defined in the function + * Physics::ResolveCollision() . This function can be modified to simulate + * dynamics effects such as friction, bouncing, stacking etc.. + * + * \todo When running along multiple boxes that are perfectly aligned one hangs from time to time at the last box + */ +class PhysicsBase : public Module { + public: + /** \brief Performs the simulation for the next msec milliseconds + * \param msec The amount of time that is to be simulated + * \param model A pointer to the model that will be used to pass on collision events.*/ + virtual int Simulate (float msec, ModelBase* model = NULL); + /** \brief Registers the physical state of an Entity */ + virtual void RegisterEntity (EntityPhysicState* entity); + /** \brief Unregisters the physical state of an Entity */ + virtual void UnregisterEntity (const unsigned int id); + + protected: + /** \brief Initializes the system */ + virtual int OnInit (int argc, char* argv[]); + /** \brief Destroys the system (must be called!) */ + virtual void OnDestroy (); + + /** \brief Moves all Entities for delta_msec milliseconds (can be + * negative!) */ + void Move (float delta_msec); + + /** \brief Checks whether two given Entities can collide */ + bool CheckPossibleCollisionPair (EntityPhysicState* entity_a, EntityPhysicState* entity_b); + /** \brief Calculates the next pair of Entities that will collide */ + bool CalcNextCollision (float stepsize, + unsigned int &reference_entity_id, + unsigned int &incidence_entity_id, + coll2d::CollisionInfo &info); + /** \brief Resolves the collision that CalcNextCollision has found + * It resolves a found collision and prevents interpenetration of cached contacts. + */ + void ResolveCollision (float stepsize, + unsigned int reference_entity_id, + unsigned int incidence_entity_id, + coll2d::CollisionInfo &info); + + /** \brief Contains all Entities with a physical state */ + std::map mEntities; + + private: + /** \brief Caches the contact information between two entities + */ + void ContactCacheAdd (EntityPhysicState* incidence_entity, EntityPhysicState* reference_entity, vector3d normal); + /** \brief Removes the contact cache information of two entities in contact + */ + void ContactCacheRemove (unsigned int entity_a_id, unsigned int entity_b_id); + /** \brief Checks whether the entries in the contact cache are still valid + */ + void CheckContactCache (EntityPhysicState* entity); + + /** \brief Allows to ignore certain kinds of errors reported by coll2d + */ + bool HandleColl2dError (int coll2d_result, float stepsize, + EntityPhysicState* entity_a, EntityPhysicState* entity_b, coll2d::CollisionInfo &info); +}; + +/** \brief Creates an EntityPhysicState with all the required information */ +EntityPhysicState* CreateEntityPhysicState (EntityBaseType type, unsigned int id); + +} + +#endif // _PHYSICSBASE_H diff --git a/engine/PhysicsEntityBase.cc b/engine/PhysicsEntityBase.cc new file mode 100644 index 0000000..043f836 --- /dev/null +++ b/engine/PhysicsEntityBase.cc @@ -0,0 +1,149 @@ +#include "EntityBase.h" + +#include "coll2d.h" + +namespace Engine { + +/** \brief Copy constructor */ +inline EntityPhysicState::EntityPhysicState (const EntityPhysicState& state): + mId (state.mId), + mBaseType (state.mBaseType), + mPosition (state.mPosition), + mOrientation (state.mOrientation), + mVelocity (state.mVelocity), + mAngleVelocity (state.mAngleVelocity), + mStatic (state.mStatic), + mAlive (state.mAlive), + mContactNormals(state.mContactNormals) +{ + mShape = state.mShape->getCopy(); +}; + +/** \brief Assignment operator */ +inline EntityPhysicState& EntityPhysicState::operator= (const EntityPhysicState& state) { + if (this != &state) { + mId = state.mId; + mBaseType = state.mBaseType; + mPosition = state.mPosition; + mOrientation = state.mOrientation; + mVelocity = state.mVelocity; + mAngleVelocity = state.mAngleVelocity; + mShape = state.mShape->getCopy(); + mStatic = state.mStatic; + mAlive = state.mAlive; + mContactNormals = state.mContactNormals; + } + + return *this; +} + +vector3d& EntityPhysicState::GetPosition () { + return mPosition; +} + +vector3d& EntityPhysicState::GetVelocity () { + return mVelocity; +} + +vector3d& EntityPhysicState::GetOrientation () { + return mOrientation; +} + +float& EntityPhysicState::GetAngleVelocity () { + return mAngleVelocity; +} + +void EntityPhysicState::SetPosition (const vector3d &position) { + mPosition = position; +} + +void EntityPhysicState::SetVelocity (const vector3d &velocity) { + mVelocity = velocity; +} + +void EntityPhysicState::SetOrientation (const vector3d &orientation) { + mOrientation = orientation; +} + +void EntityPhysicState::SetAngleVelocity (const float &angle_velocity) { + mAngleVelocity = angle_velocity; +} + +void EntityPhysicState::Globalize (vector3d &vec) { + // make a copy of the local coordinates + vector3d local (vec); + + // calculate sin and cos for the current rotation + float sintheta, costheta; + sintheta = sin (- mOrientation[1] * M_PI / 180); + costheta = cos (- mOrientation[1] * M_PI / 180); + + // rotation + vec[0] = - sintheta * local[2] + costheta * local[0]; + vec[1] = local[1]; + vec[2] = costheta * local[2] + sintheta * local[0] ; + + // and translation + vec += mPosition; +} + +void EntityPhysicState::Localize (vector3d &vec) { + // translation + vec -= mPosition; + + vector3d global (vec); + + // calculate sin and cos for the current rotation + float sintheta, costheta; + sintheta = sin (mOrientation[1] * M_PI / 180); + costheta = cos (mOrientation[1] * M_PI / 180); + + // rotation + vec[0] = - sintheta * global[2] + costheta * global[0]; + vec[1] = global[1]; + vec[2] = costheta * global[2] + sintheta * global[0] ; +} + +void EntityPhysicState::GlobalizeRotation (vector3d &vec) { + // make a copy of the local coordinates + vector3d local (vec); + + // calculate sin and cos for the current rotation + float sintheta, costheta; + sintheta = sin (- mOrientation[1] * M_PI / 180); + costheta = cos (- mOrientation[1] * M_PI / 180); + + // rotation + vec[0] = - sintheta * local[2] + costheta * local[0]; + vec[1] = local[1]; + vec[2] = costheta * local[2] + sintheta * local[0] ; +} + +void EntityPhysicState::LocalizeRotation (vector3d &vec) { + vector3d global (vec); + + // calculate sin and cos for the current rotation + float sintheta, costheta; + sintheta = sin (mOrientation[1] * M_PI / 180); + costheta = cos (mOrientation[1] * M_PI / 180); + + // rotation + vec[0] = - sintheta * global[2] + costheta * global[0]; + vec[1] = global[1]; + vec[2] = costheta * global[2] + sintheta * global[0] ; +} + + +void EntityPhysicState::UpdateShape () { + assert (mShape); + + mShape->setPosition (mPosition); + mShape->setAngle (mOrientation[1] * M_PI / 180); + + mShape->setVelocity (mVelocity); + mShape->setAngleVelocity (mAngleVelocity); +} + + +} + diff --git a/engine/SimpleConsoleOverlay.cc b/engine/SimpleConsoleOverlay.cc new file mode 100644 index 0000000..f4bef21 --- /dev/null +++ b/engine/SimpleConsoleOverlay.cc @@ -0,0 +1,220 @@ +#include "DrawingsGL.h" +#include "OverlayBase.h" +#include "SimpleConsoleOverlay.h" + +#include "OGLFT.h" + +#include +#include + +namespace Engine { + +static Variable Var_ConsoleTransparency ("consoletransparency", "0.2"); + +bool SimpleConsoleOverlay::OnKeyDown (const SDL_keysym &keysym) { + if (keysym.sym == SDLK_F8) { + if (mActive) { + // We have to call SetActive() to actually + // activate the unicode processing of SDL + SetActive (false); + } + else { + SetActive (true); + } + + return true; + } + + if (!mActive) + return false; + + // check for input that requires actions + switch (keysym.sym) { + case SDLK_ESCAPE: + SetActive (false); + return true; + break; + case SDLK_BACKSPACE: + if (mCurrentInput.size() > 0) + mCurrentInput = mCurrentInput.substr (0, mCurrentInput.size() - 1 ); + return true; + break; + case SDLK_RETURN: + if (mCurrentInput.size() == 0) { + mLastLines.push_back (""); + return true; + } + + mLastLines.push_back (mCurrentInput); + + // run the command and print out the error if there was one + if (!RunCommand (mCurrentInput)) { + mLastLines.push_back ("Error: " + CommandGetErrorString()); + } + mCurrentInput = ""; + return true; + + break; + default: + break; + } + + // if we got input of a character that we can write add it to the current + // input + if (keysym.unicode) { + if ((keysym.unicode & 0xFF80) == 0) { + mCurrentInput += keysym.unicode & 0x7F; + return true; + } else { + LogWarning ("Input key not supported!"); + return false; + } + } + + return true; +} + +void SimpleConsoleOverlay::Draw () { + // we switch to orthographic projection and draw the contents of the 2d + // overlay on top of the previous drawings + glMatrixMode (GL_PROJECTION); + glPushMatrix (); + glLoadIdentity (); + + // first we have to get the size of the current viewport to set up the + // orthographic projection correctly + GLint viewport[4]; + glGetIntegerv(GL_VIEWPORT, viewport); + gluOrtho2D (viewport[0], viewport[2], viewport[3], viewport[1]); + + glMatrixMode (GL_MODELVIEW); + glPushMatrix (); + glLoadIdentity (); + + // then we do the drawings + if (mDrawLogBar) + DrawLogBar (); + if (mActive) + DrawConsole (); + + glPopMatrix (); + + glMatrixMode (GL_PROJECTION); + glPopMatrix (); + + glMatrixMode (GL_MODELVIEW); + +}; + +void SimpleConsoleOverlay::DrawLogBar () { + // first we have to get the size of the current viewport to set up the + // orthographic projection correctly + GLint viewport[4]; + glGetIntegerv(GL_VIEWPORT, viewport); + + // we want to enable transparency + glDisable(GL_DEPTH_TEST); + glEnable (GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_SRC_ALPHA); + + float left = 0; + float right = static_cast (GetWindowWidth()); + float top = 0; +// float bottom = static_cast (GetWindowHeight()); + + // draw the background + glColor4f (0.2, 0.2, 0.2, 0.3); + glBegin (GL_QUADS); + glVertex2f (left, top); + glVertex2f (left, top + 16); + glVertex2f (right, top + 16); + glVertex2f (right, top); + glEnd (); + + glDisable (GL_BLEND); + + // draw the log + std::ostringstream topbar_stream; + topbar_stream << "Log: " << GetLastLogEntry().mMessage; + DrawGLString ( 10, 10, topbar_stream.str().c_str ()); + + // draw the FPS counter + topbar_stream.str (""); + topbar_stream << "FPS: " << GetFrameRate(); + DrawGLString (right - 64 , 10, topbar_stream.str().c_str ()); + + glEnable (GL_DEPTH_TEST); +} + +void SimpleConsoleOverlay::DrawConsole () { + // first we have to get the size of the current viewport to set up the + // orthographic projection correctly + GLint viewport[4]; + glGetIntegerv(GL_VIEWPORT, viewport); + + // we want to enable transparency + glDisable(GL_DEPTH_TEST); +// glEnable (GL_BLEND); +// glBlendFunc(GL_SRC_ALPHA, GL_ONE); +// glBlendFunc(GL_SRC_ALPHA, GL_SRC_ALPHA); + + // calculate the screen coordinates which defines the size of the + // console + mLeft = (viewport[2] - viewport[0]) * 0.5 - (viewport[2] - viewport[0]) * 0.45 ; + mRight = (viewport[2] - viewport[0]) * 0.5 + (viewport[2] - viewport[0]) * 0.45; + mTop = -1; // Do not draw the mTop line + mBottom = (viewport[3] - viewport[1]) * 0.7; + + // draw the background + glColor4f (0.2, 0.2, 0.2, Var_ConsoleTransparency.GetFloatValue()); + glBegin (GL_QUADS); + glVertex2f (mLeft, mTop); + glVertex2f (mLeft, mBottom); + glVertex2f (mRight, mBottom); + glVertex2f (mRight, mTop); + glEnd (); + + // draw borders + glColor3f (0.9, 0.9, 0.9); + glBegin (GL_LINE_STRIP); + glVertex2f (mLeft, mTop); + glVertex2f (mLeft, mBottom); + glVertex2f (mRight, mBottom); + glVertex2f (mRight, mTop); + glVertex2f (mLeft, mTop); + glEnd (); + + glDisable (GL_BLEND); + + // now draw the contents + DrawLastLines (); + DrawCurrentInput (); + + glEnable (GL_DEPTH_TEST); +} + +const std::vector SimpleConsoleOverlay::GetLastLines (const unsigned int n) { + assert (0); + std::vector result; + return result; +} + +void SimpleConsoleOverlay::DrawLastLines () { + unsigned int i; + for (i = 0; i < mLastLines.size(); i++) + DrawGLString (mLeft + 8, mTop + 12 + 12 *i, mLastLines[i].c_str()); +} + +void SimpleConsoleOverlay::DrawCurrentInput () { + // We add a '_' to the current input as a simple cursor + static std::string current_input; + current_input = mCurrentInput + "_"; + + // We add a '>' at the beginning of the input line to highlight it + DrawGLString (mLeft, mTop + 12 + 12 * (mLastLines.size()), ">"); + DrawGLString (mLeft + 8, mTop + 12 + 12 * (mLastLines.size()), current_input.c_str()); +} + + + +} diff --git a/engine/SimpleConsoleOverlay.h b/engine/SimpleConsoleOverlay.h new file mode 100644 index 0000000..64064fe --- /dev/null +++ b/engine/SimpleConsoleOverlay.h @@ -0,0 +1,69 @@ +#ifndef SIMPLECONSOLEOVERLAY +#define SIMPLECONSOLEOVERLAY + +#include "Variables.h" + +#include +#include + +namespace Engine { + +class OverlayBase; + +class SimpleConsoleOverlay : public OverlayBase { + public: + SimpleConsoleOverlay () { + mActive = false; + mDrawLogBar = false; + }; + virtual ~SimpleConsoleOverlay() {}; + + virtual bool OnKeyDown (const SDL_keysym &keysym); + virtual bool OnKeyUp (const SDL_keysym &keysym) { + if(mActive) + return true; + return false; + }; + + virtual void Draw (); + + void DrawLogBar (); + void DrawConsole (); + + /** \brief Returns the last n lines */ + const std::vector GetLastLines (const unsigned int n); + /** \brief Returns true if the Console is active */ + bool GetActive () { return mActive; }; + /** \brief Activates or deactivates the the Console */ + void SetActive (bool active) { + if (active) { + SDL_EnableUNICODE (1); + SDL_EnableKeyRepeat (500, 50); + } + else { + SDL_EnableUNICODE (-1); + SDL_EnableKeyRepeat (0, 100); + } + mActive = active; + }; + void SetDrawLogBar (bool value) { mDrawLogBar = value; }; + + /** \brief Draws the console with the current content */ + void DrawLastLines (); + void DrawCurrentInput (); + + private: + std::vector mLastLines; + std::string mCurrentInput; + bool mActive; + bool mDrawLogBar; + + float mLeft; + float mTop; + float mRight; + float mBottom; +}; + +} + +#endif /* SIMPLECONSOLEOVERLAY */ diff --git a/engine/Sprite.cc b/engine/Sprite.cc new file mode 100644 index 0000000..caf9dd3 --- /dev/null +++ b/engine/Sprite.cc @@ -0,0 +1,237 @@ +#include "Engine.h" +#include "Sprite.h" + +#include +#include +#include + +#include + +namespace Engine { + +/* + * Code is taken from http://en.wikibooks.org/wiki/OpenGL_Programming/Intermediate/Textures on + * Sunday, March 14 2010. + */ +bool Sprite::LoadFromPNG (const char *filename) { + LogDebug ("Loading png from %s", filename); + + //header for testing if it is a png + png_byte header[8]; + + //open file as binary + FILE *fp = fopen(filename, "rb"); + if (!fp) { + LogError ("Could not open file: %s", filename); + return false; + } + + //read the header + fread(header, 1, 8, fp); + + //test if png + int is_png = !png_sig_cmp(header, 0, 8); + if (!is_png) { + LogError ("Error opening png file %s: file is not a png file!", filename); + fclose(fp); + return false; + } + + //create png struct + png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, + NULL, NULL); + if (!png_ptr) { + LogError ("Error opening png file %s: unable to read png header", filename); + fclose(fp); + return (false); + } + + //create png info struct + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + LogError ("Error opening png file %s: unable to read png header", filename); + png_destroy_read_struct(&png_ptr, (png_infopp) NULL, (png_infopp) NULL); + fclose(fp); + return (false); + } + + //create png info struct + png_infop end_info = png_create_info_struct(png_ptr); + if (!end_info) { + LogError ("Error opening png file %s: unable to read png header", filename); + png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL); + fclose(fp); + return (false); + } + + //png error stuff, not sure libpng man suggests this. + if (setjmp(png_jmpbuf(png_ptr))) { + LogError ("Error opening png file %s: unable to read png header", filename); + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + fclose(fp); + return (false); + } + + //init png reading + png_init_io(png_ptr, fp); + + //let libpng know you already read the first 8 bytes + png_set_sig_bytes(png_ptr, 8); + + // read all the info up to the image data + png_read_info(png_ptr, info_ptr); + + //variables to pass to get info + int bit_depth, color_type; + png_uint_32 tmWidth, tmHeight; + + // get info about png + png_get_IHDR(png_ptr, info_ptr, &tmWidth, &tmHeight, &bit_depth, &color_type, + NULL, NULL, NULL); + + //update mWidth and mHeight based on png info + mWidth = tmWidth; + mHeight = tmHeight; + + // Update the png info struct. + png_read_update_info(png_ptr, info_ptr); + + // Row size in bytes. + int rowbytes = png_get_rowbytes(png_ptr, info_ptr); + + // Allocate the image_data as a big block, to be given to opengl + png_byte *image_data = new png_byte[rowbytes * mHeight]; + if (!image_data) { + //clean up memory and close stuff + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + fclose(fp); + return false; + } + + //row_pointers is for pointing to image_data for reading the png with libpng + png_bytep *row_pointers = new png_bytep[mHeight]; + if (!row_pointers) { + //clean up memory and close stuff + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + delete[] image_data; + fclose(fp); + return false; + } + // set the individual row_pointers to point at the correct offsets of image_data + for (unsigned int i = 0; i < mHeight; ++i) + row_pointers[mHeight - 1 - i] = image_data + i * rowbytes; + + //read the png into image_data through row_pointers + png_read_image(png_ptr, row_pointers); + + //Now generate the OpenGL texture object + glGenTextures(1, &mGlTextureName); + glBindTexture(GL_TEXTURE_2D, mGlTextureName); + glTexImage2D(GL_TEXTURE_2D,0, GL_RGBA, mWidth, mHeight, 0, + GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*) image_data); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + //clean up memory and close stuff + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + delete[] row_pointers; + delete[] image_data; + + fclose(fp); + + return true; +} + +void Sprite::DrawAt (float xpos, float ypos, float zpos) { + float u_start = 0., u_end = 1.; + if (mAnimation) { + int frame_index = floor ( (mAnimationFrameRate) * mAnimationTimer); + u_start = static_cast(frame_index) / mAnimationFrameCount; + u_end = (frame_index + 1.) / mAnimationFrameCount; + } + + glPushMatrix(); + glTranslatef (xpos -mScale * mWidth * 0.5, ypos, zpos -mScale * mHeight * 0.5); + glScalef (mScale, mScale, mScale); + glDisable(GL_DEPTH_TEST); + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glBindTexture (GL_TEXTURE_2D, mGlTextureName); + + glBegin(GL_QUADS); + glTexCoord2f (u_start, 0.); glVertex3f (0., 0., 0.); + glTexCoord2f (u_end, 0.); glVertex3f (0., 0., mHeight); + glTexCoord2f (u_end, 1.); glVertex3f (mWidth, 0., mHeight); + glTexCoord2f (u_start, 1.); glVertex3f (mWidth, 0.,0.); + glEnd(); + + glPopMatrix(); + + glDisable(GL_TEXTURE_2D); + glEnable(GL_DEPTH_TEST); +} + +void Sprite::DrawAt2D (float xpos, float ypos) { + float u_start = 0., u_end = 1.; + if (mAnimation) { + int frame_index = floor ( (mAnimationFrameRate) * mAnimationTimer); + u_start = static_cast(frame_index) / mAnimationFrameCount; + u_end = (frame_index + 1.) / mAnimationFrameCount; + } + + glPushMatrix(); + glTranslatef (xpos -mScale * mWidth * 0.5, ypos, 0.); + glScalef (mScale, mScale, mScale); + // \TODO Make 2d drawing of sprites a bit nicer, seems a bit overkill for such a simple function + glRotatef (-90, 0., 0., 1.); + glDisable(GL_DEPTH_TEST); + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glBindTexture (GL_TEXTURE_2D, mGlTextureName); + + glBegin(GL_QUADS); + glTexCoord2f (u_start, 0.); glVertex2f (0., 0.); + glTexCoord2f (u_end, 0.); glVertex2f (0., mHeight); + glTexCoord2f (u_end, 1.); glVertex2f (mWidth, mHeight); + glTexCoord2f (u_start, 1.); glVertex2f (mWidth,0.); + glEnd(); + + glPopMatrix(); + + glDisable(GL_TEXTURE_2D); + glEnable(GL_DEPTH_TEST); +} + +void Sprite::DrawSubAt (unsigned int index, float xpos, float ypos, float zpos) { + assert (index < mSubSpriteCount); + + float u_start = static_cast(index) / mSubSpriteCount; + float u_end = (index + 1.) / mSubSpriteCount; + + glPushMatrix(); + glTranslatef (xpos -mScale * mWidth * 0.5, ypos, zpos -mScale * mHeight * 0.5); + glScalef (mScale, mScale, mScale); + glDisable(GL_DEPTH_TEST); + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + glBindTexture (GL_TEXTURE_2D, mGlTextureName); + + glBegin(GL_QUADS); + glTexCoord2f (u_start, 0.); glVertex3f (0., 0., 0.); + glTexCoord2f (u_end, 0.); glVertex3f (0., 0., mHeight); + glTexCoord2f (u_end, 1.); glVertex3f (mWidth, 0., mHeight); + glTexCoord2f (u_start, 1.); glVertex3f (mWidth, 0.,0.); + glEnd(); + + glPopMatrix(); + + glDisable(GL_TEXTURE_2D); + glEnable(GL_DEPTH_TEST); +} + +} diff --git a/engine/Sprite.h b/engine/Sprite.h new file mode 100644 index 0000000..411856a --- /dev/null +++ b/engine/Sprite.h @@ -0,0 +1,71 @@ +#ifndef SPRITE_H +#define SPRITE_H + +#include + +namespace Engine { + +class Sprite { + public: + Sprite() { + mScale = 1.; + mWidth = 0; + mHeight = 0; + mGlTextureName = 0; + + mAnimation = false; + mSubSpriteCount = 1; + } + + bool LoadFromPNG (const char *filename); + void DrawAt (float xpos, float ypos, float zpos); + void DrawAt2D (float xpos, float ypos); + unsigned int GetWidth() { return mWidth; }; + unsigned int GetHeight() { return mHeight; }; + + void SetScale (float scale) { mScale = scale; }; + + void SetAnimation (int frame_count, float frame_rate) { + mAnimation = true; + mAnimationFrameCount = frame_count; + mAnimationFrameRate = frame_rate; + mAnimationTimer = 0.; + mWidth = static_cast(ceil (static_cast (mWidth / mAnimationFrameCount))); + } + void ResetAnimation () { + mAnimationTimer = 0.; + } + void UpdateAnimation (float seconds) { + mAnimationTimer += seconds; + while (mAnimationTimer >= mAnimationFrameCount / mAnimationFrameRate) { + mAnimationTimer -= mAnimationFrameCount / mAnimationFrameRate; + } + } + + void SetSubSpriteCount (const unsigned int count) { + mSubSpriteCount = count; + mWidth = static_cast(ceil (static_cast (mWidth / mSubSpriteCount))); + } + unsigned int GetSubSpriteCount () const { + return mSubSpriteCount; + } + void DrawSubAt (unsigned int index, float xpos, float ypos, float zpos); + + private: + float mScale; + + unsigned int mWidth; + unsigned int mHeight; + unsigned int mGlTextureName; + + unsigned int mSubSpriteCount; + + bool mAnimation; + int mAnimationFrameCount; + float mAnimationTimer; + float mAnimationFrameRate; +}; + +} + +#endif /* SPRITE_H */ diff --git a/engine/Variables.cc b/engine/Variables.cc new file mode 100644 index 0000000..6017385 --- /dev/null +++ b/engine/Variables.cc @@ -0,0 +1,159 @@ +#include "Variables.h" + +#include +#include + +namespace Engine { + +Variables* VariablesInstance = NULL; + +/* + * Inherited Module functions + */ +int Variables::OnInit (int argc, char* argv[]) { + LogDebug ("Variables Init"); + + VariablesInstance = this; + + /* This definition of a variable causes the delayed variables to be loaded, + * please keep it here! */ + static Variable VariableSystemUp_var ("variablesystemup", "true"); + + return 0; +} + +void Variables::OnDestroy () { + LogDebug ("Variables Destroy"); + + VariablesInstance = NULL; +} + +/* + * Module specific functions + */ +void Variables::RegisterVariable (const std::string &name, Variable *var) { + LogDebug ("Registering Variable '%s'", name.c_str ()); + if (mVariablesData.find (name) != mVariablesData.end()) { + // Variable already existing! + mVariablesData[name] = var; + + return; + } + + mVariablesData[name] = var; + + return; +} + +/* + * Variable class + */ +Variable* Variables::GetVariable (const std::string &name) { + if (mVariablesData.find (name) == mVariablesData.end()) { + // Variable not existing! + return NULL; + } + + return mVariablesData[name]; +} + +Variable::Variable (const std::string &name, const std::string &value) { + static std::vector delayed_variables; + + mName = name; + mStringValue = value; + mFloatValue = atof (value.c_str()); + + if (VariablesInstance == NULL) { + delayed_variables.push_back (this); + } else if (delayed_variables.size() > 0) { + LogDebug ("Loading delayed Variables"); + unsigned int i; + for (i = 0; i < delayed_variables.size(); i ++) + VariablesInstance->RegisterVariable (delayed_variables[i]->mName, delayed_variables[i]); + + delayed_variables.clear (); + + RegisterVariable (name); + } else { + RegisterVariable (name); + } +} + +void Variable::RegisterVariable (const std::string &name) { + if (! VariablesInstance ) { + LogError ("Unable to register Variable '%s': Variables System not initialized!", name.c_str()); + return; + } + + VariablesInstance->RegisterVariable (name, this); +} + +/* + * Global functions + */ +Variable* GetVariable (const std::string &name) { + if (! VariablesInstance ) { + LogError ("Unable to register Variable '%s': Variables System not initialized!", name.c_str()); + return NULL; + } + + return VariablesInstance->GetVariable (name); +} + +bool SetVariableValue (const std::string &name, const std::string value) { + if (! VariablesInstance ) { + LogError ("Unable to set Variable '%s': Variables System not initialized!", name.c_str()); + return false; + } + + Variable *var = VariablesInstance->GetVariable (name); + if (!var) { + return false; + } + + var->SetStringValue (value); + var->SetFloatValue (atof (value.c_str())); + + return true; +} + +std::string& GetVariableString (const std::string &name, std::string def) { + /* We use a static result variable for the case that def was not passed to + * is function */ + static std::string def_result = def; + + if (! VariablesInstance ) { + LogError ("Unable to register Variable '%s': Variables System not initialized!", name.c_str()); + return def_result; + } + + Variable *var = VariablesInstance->GetVariable (name); + if (!var) { + return def_result; + } + + return var->GetStringValue (); +} + +float &GetVariableFloat (const std::string &name, float def) { + /* We use a static result variable for the case that def was not passed to + * is function */ + static float def_result = def; + + if (! VariablesInstance ) { + LogError ("Unable to register Variable '%s': Variables System not initialized!", name.c_str()); + return def_result; + } + + Variable *var = VariablesInstance->GetVariable (name); + if (!var) { + return def_result; + } + + return var->GetFloatValue (); +} + + +} + diff --git a/engine/Variables.h b/engine/Variables.h new file mode 100644 index 0000000..daf6461 --- /dev/null +++ b/engine/Variables.h @@ -0,0 +1,37 @@ +#ifndef _VARIABLES_H +#define _VARIABLES_H + +#include "Engine.h" + +#include +#include +#include + +namespace Engine { + +class Module; +class Variable; + +/** \brief Manages all variables that can be changed by the Model itself + * + * \todo make the variable names case insensitive + * \todo only allow certain characters in variable names + */ +class Variables : public Module{ + public: + void RegisterVariable (const std::string &name, Variable *var); + Variable *GetVariable (const std::string &name); + + protected: + /** \brief Initializes the system */ + int OnInit (int argc, char* argv[]); + /** \brief Destroys the system (must be called!) */ + void OnDestroy (); + void OnRegisterCommands (); + + std::map mVariablesData; +}; + +} + +#endif // _VARIABLES_H diff --git a/engine/VariablesCommands.cc b/engine/VariablesCommands.cc new file mode 100644 index 0000000..a3ed9d9 --- /dev/null +++ b/engine/VariablesCommands.cc @@ -0,0 +1,27 @@ +#include "Variables.h" + +namespace Engine { + +bool Cmd_Set (const std::vector args) { + if (args.size() != 2) { + CommandSetErrorString ("Usage: set \nSets variables to value ."); + return false; + } + + Variable *test = GetVariable (args[0]); + if (test) { + test->SetStringValue (args[1]); + test->SetFloatValue (atof (args[1].c_str())); + return true; + } + + CommandSetErrorString ("Variable '" + args[0] +"' not found!"); + return false; +} + +void Variables::OnRegisterCommands () { + AddCommand ("set", Cmd_Set); +} + +} + diff --git a/engine/VariablesGlobal.h b/engine/VariablesGlobal.h new file mode 100644 index 0000000..1739cb5 --- /dev/null +++ b/engine/VariablesGlobal.h @@ -0,0 +1,68 @@ +#ifndef _VARIABLESGLOBAL_H +#define _VARIABLESGLOBAL_H + +namespace Engine { +/** \brief Represents a variable that can be modified and read within the game + * + * \note Variables \b MUST be declared as static variables, otherwise the memory that + * hold their values get invalidated and the system gets unstable! + */ +class Variable { + public: + /** \brief The constructor to be used when initializing a Variable + * + * \param name The name under which the variable is engine wide + * accessible + * \param value The value it is assigned to. + * + * The value string gets automatically converted to a float with atof (). + * Modification of a Variable must always be made with the Get*, Set* + * functions. + */ + Variable (const std::string &name, const std::string &value); + + /** \brief Returns the string value of the Variable */ + std::string& GetStringValue () { + return mStringValue; + } + /** \brief Returns the float value of the Variable */ + float& GetFloatValue () { + return mFloatValue; + } + void SetStringValue (const std::string &value) { + mStringValue = value; + } + void SetFloatValue (float value) { + mFloatValue = value; + } + + private: + /** \brief The default constructor must not be used. + * + * Use \code + * Variable (const std::string &name, const std::string &value) + * \endcode + * instead. + * */ + Variable () { assert (0); } + /** \brief Registeres this Variable with the Variables System */ + void RegisterVariable (const std::string &name); + + std::string mName; + std::string mStringValue; + float mFloatValue; + + friend class Variables; +}; + +/** \brief Provides access to a Variable stored under the given name */ +Variable* GetVariable (const std::string &name); +/** \brief Sets the vaule of the Variable */ +bool SetVariableValue (const std::string &name, const std::string &value); +/** \brief Returns the string value of the Variable with the given name */ +std::string& GetVariableString (const std::string &name, std::string def = ""); +/** \brief Returns the float value of the Variable with the given name */ +float& GetVariableFloat (const std::string &name, float def = 0.); + +} +#endif // _VARIABLESGLOBAL_H diff --git a/engine/ViewBase.cc b/engine/ViewBase.cc new file mode 100644 index 0000000..0f83f00 --- /dev/null +++ b/engine/ViewBase.cc @@ -0,0 +1,330 @@ +#include "ViewBase.h" + +#include "ModelBase.h" +#include "ControllerBase.h" +#include "CameraBase.h" +#include "OverlayBase.h" + +#include "SimpleConsoleOverlay.h" +#include "OGLFT.h" + +#include +#include + +#include "DrawingsGL.h" + +using namespace std; + +namespace Engine { + +static ViewBase* ViewInstance = NULL; + +void InitGL () { + glClearColor(0.3f, 0.3f, 0.3f, 1.0f); + glClearDepth(1.0); + glDepthFunc(GL_LESS); + glEnable(GL_DEPTH_TEST); + glShadeModel(GL_SMOOTH); + glEnable (GL_CULL_FACE); + glDisable (GL_FOG); + + glMatrixMode (GL_PROJECTION); + glLoadIdentity (); + glMatrixMode (GL_MODELVIEW); + glLoadIdentity (); +} + +/* + * Inherited Module functions + */ +int ViewBase::OnInit (int argc, char* argv[]) { + LogMessage ("View Init"); + + mWindowHeight = VIEW_DEFAULT_HEIGHT; + mWindowWidth = VIEW_DEFAULT_WIDTH; + + SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); + + if( SDL_SetVideoMode( mWindowWidth, mWindowHeight, 16, SDL_OPENGL | SDL_RESIZABLE ) == 0 ) { + LogError ("Video mode set failed: %s", SDL_GetError ()); + exit (-1); + } + + InitGL (); + Resize (mWindowWidth, mWindowHeight); + + mConsoleFont = new OGLFT::Monochrome ("./data/fonts/console.ttf", 12); + + if ( mConsoleFont == 0 || !mConsoleFont->isValid() ) { + LogError ("Could not load font %s!", "./data/fonts/console.ttf"); + exit (-1); + } + + SimpleConsoleOverlay* console_overlay = new SimpleConsoleOverlay; + AddOverlay (console_overlay); + + mConsoleFont->setForegroundColor (1., 1., 1.); + + mDrawAxis = false; + + mDrawGrid = false; + mGridSizeX = 8; + mGridSizeZ = 8; + + ViewInstance = this; + + return 0; +} + +void ViewBase::OnDestroy () { + if (mConsoleFont ) + delete mConsoleFont; + mConsoleFont = NULL; + + std::vector::iterator overlay_iter = mOverlays.begin(), overlay_temp; + while (overlay_iter != mOverlays.end()) { + overlay_temp = overlay_iter; + delete *overlay_temp; + + overlay_iter++; + } + + ViewInstance = NULL; + + LogDebug ("View Destroy"); +} + +void ViewBase::CalcWorldCoordinates (int screen_x, int screen_y, float world_y, float *pos_out) { + GLdouble modelMatrix[16], projMatrix[16]; + GLint viewport[4]; + GLdouble wx, wy, wz; + + glGetIntegerv (GL_VIEWPORT, viewport); + glGetDoublev(GL_MODELVIEW_MATRIX, modelMatrix); + glGetDoublev(GL_PROJECTION_MATRIX, projMatrix); + + int realy = viewport[3] - screen_y - 1; + + gluUnProject ((GLdouble) screen_x, (GLdouble) realy, 1., + modelMatrix, projMatrix, viewport, &wx, &wy, &wz); + + GLdouble t; + GLdouble d[3]; + float eye[3]; + + mCamera->GetEye (&eye[0]); + + d[0] = wx - eye[0]; + d[1] = wy - eye[1]; + d[2] = wz - eye[2]; + + assert (fabs (d[1]) >= 1.0e-3); + t = -eye[1]/d[1] + world_y; + + pos_out[0] = eye[0] + t * d[0]; + pos_out[1] = eye[1] + t * d[1]; + pos_out[2] = eye[2] + t * d[2]; +} + +/* + * Module specific functions + */ +void ViewBase::UpdateCamera () { + EntityPhysicState* player_ent = GetEntityPhysicState (GetPlayerEntityId()); + + if (!player_ent) { + LogError ("Could not call Model::PositionCamera(): player entity not found!"); + exit (-1); + } + vector3d entity_camera_distance (-2, 3, 0); + vector3d entity_position = player_ent->GetPosition(); + player_ent->Globalize (entity_camera_distance); + + mCamera->SetEye ( + entity_camera_distance[0], + entity_camera_distance[1], + entity_camera_distance[2] + ); + mCamera->SetPointOfIntrest ( + entity_position[0], + entity_position[1], + entity_position[2] + ); + + mCamera->Update (); +} + +void ViewBase::Draw () { + // Clear the screen + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // update the frame rate counter + static Uint32 this_frame_ticks; + static Uint32 last_frame_ticks = 0; + static Uint32 last_fps_update = 0; + static int frame_counter = 0; + + this_frame_ticks = SDL_GetTicks (); + last_fps_update += this_frame_ticks - last_frame_ticks; + last_frame_ticks = this_frame_ticks; + frame_counter++; + + if (last_fps_update > 1000) { + mFrameRate = frame_counter; + last_fps_update = 0; + frame_counter = 0; + } + + UpdateCamera (); + + if (mDrawGrid) + DrawGrid (); + + if (mDrawAxis) + DrawAxis (); + + DrawWorld (); + + std::vector::iterator overlay_iter; + for (overlay_iter = mOverlays.begin(); overlay_iter != mOverlays.end(); overlay_iter++) { + (*overlay_iter)->Draw(); + } + + // and update the screen + SDL_GL_SwapBuffers (); +} + +void ViewBase::DrawGLString (float x, float y, const char* str) { + glPixelStorei (GL_UNPACK_ALIGNMENT, 1); + mConsoleFont->draw (x, y, str); +} + +void ViewBase::GetCamereEye (float *eye_out) { + assert (mCamera); + mCamera->GetEye (eye_out); +} + +void ViewBase::DrawGrid () { + float xmin, xmax, xstep, zmin, zmax, zstep; + int i, count_x, count_z; + + xmin = -mGridSizeX; + xmax = mGridSizeX; + zmin = -mGridSizeZ; + zmax = mGridSizeZ; + + count_x = mGridSizeX * 2; + count_z = mGridSizeZ * 2; + + xstep = 1.; + zstep = 1.; + + glColor3f (1., 1., 1.); + glBegin (GL_LINES); + for (i = 0; i <= count_x; i++) { + glVertex3f (i * xstep + xmin, 0., zmin); + glVertex3f (i * xstep + xmin, 0., zmax); + } + for (i = 0; i <= count_z; i++) { + glVertex3f (xmin, 0, i * zstep + zmin); + glVertex3f (xmax, 0, i * zstep + zmin); + } + glEnd (); + +} + +void ViewBase::DrawWorld () { +} + +void ViewBase::Resize (int width, int height) { + if (height == 0) + height = 1; + + mWindowWidth = static_cast (width); + mWindowHeight = static_cast (height); + + glViewport(0, 0, width, height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(mCamera->GetFOVY (), float (width) / float (height), 0.1, 100); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity (); + + LogDebug ("Resize to: %d x %d", mWindowWidth,mWindowHeight); + + /** \warning + * This call has to be made for SDL 1.2 for 1.3 there seems to be a + * workaround, however since I do not yet run SDL 1.3 I hold on to this. + * See http://lists.libsdl.org/pipermail/sdl-libsdl.org/2008-November/067306.html + */ + if( SDL_SetVideoMode( mWindowWidth, mWindowHeight, 16, SDL_OPENGL | SDL_RESIZABLE ) == 0 ) { + LogError ("Video mode set failed: %s", SDL_GetError ()); + exit (-1); + } +} + +bool ViewBase::SendKeyDown (const SDL_keysym &keysym) { + std::vector::iterator overlay_iter; + for (overlay_iter = mOverlays.begin(); overlay_iter != mOverlays.end(); overlay_iter++) { + if ( (*overlay_iter)->OnKeyDown (keysym)) + return true; + } + + return false; +} + +bool ViewBase::SendKeyUp (const SDL_keysym &keysym) { + std::vector::iterator overlay_iter; + for (overlay_iter = mOverlays.begin(); overlay_iter != mOverlays.end(); overlay_iter++) { + if ( (*overlay_iter)->OnKeyUp (keysym)) + return true; + } + + return false; +} + +bool ViewBase::SendMouseButtonUp (Uint8 button, Uint16 xpos, Uint16 ypos) { + std::vector::iterator overlay_iter; + for (overlay_iter = mOverlays.begin(); overlay_iter != mOverlays.end(); overlay_iter++) { + if ( (*overlay_iter)->OnMouseButtonUp (button, xpos, ypos)) + return true; + } + + return false; +} + +bool ViewBase::SendMouseButtonDown (Uint8 button, Uint16 xpos, Uint16 ypos) { + std::vector::iterator overlay_iter; + for (overlay_iter = mOverlays.begin(); overlay_iter != mOverlays.end(); overlay_iter++) { + if ( (*overlay_iter)->OnMouseButtonDown (button, xpos, ypos)) + return true; + } + + return false; +} + +/* + * Global functions + */ +void DrawGLString (float x, float y, const char* str) { + if (!ViewInstance) { + LogError ("Cannot Draw GL String: View not yet initialized!"); + return; + } + ViewInstance->DrawGLString (x, y, str); +} + +unsigned int GetWindowWidth() { + return ViewInstance->GetWindowWidth (); +} + +unsigned int GetWindowHeight() { + return ViewInstance->GetWindowHeight (); +} + +int GetFrameRate () { + return ViewInstance->GetFrameRate (); +} + +} diff --git a/engine/ViewBase.h b/engine/ViewBase.h new file mode 100644 index 0000000..a6a2bf1 --- /dev/null +++ b/engine/ViewBase.h @@ -0,0 +1,98 @@ +#ifndef _VIEWBASE_H +#define _VIEWBASE_H + +#include "Engine.h" + +// forward declarations for the OGLFT fonts +namespace OGLFT { + class Monochrome; +} + +namespace Engine { + +class Module; +class ModelBase; +class CameraBase; +class OverlayBase; + +/** \brief Performs the actual drawing based on Camera and Model + */ + +class ViewBase : public Module{ + public: + /** \brief Resizes the View */ + void Resize (int width, int height); + + /** \brief Performs all drawing */ + virtual void Draw (); + + /** \brief Draws a string at the given position using current projection + * and modelview matrices */ + void DrawGLString (float x, float y, const char* str); + /** \brief Stores the eye poisition in eye_out */ + void GetCamereEye (float *eye_out); + + /** \brief Calculates the world coordinates to given screen coordinates */ + void CalcWorldCoordinates (int screen_x, int screen_y, float world_y, float *pos_out); + + unsigned int GetWindowWidth () { return mWindowWidth; }; + unsigned int GetWindowHeight () { return mWindowHeight; }; + int GetFrameRate() { return mFrameRate; }; + + void SetDrawAxis (bool draw_axis) { mDrawGrid = draw_axis; }; + bool GetDrawAxis () { return mDrawGrid; }; + void SetDrawGrid (bool draw_grid) { mDrawGrid = draw_grid; }; + bool GetDrawGrid () { return mDrawGrid; }; + void SetGridSize (int x, int z) { mGridSizeX = x; mGridSizeZ = z; } + + void AddOverlay (OverlayBase *overlay) { mOverlays.push_back (overlay); }; + + /* Input forwarding for the overlays */ + bool SendKeyDown (const SDL_keysym &keysym); + bool SendKeyUp (const SDL_keysym &keysym); + bool SendMouseButtonUp (Uint8 button, Uint16 xpos, Uint16 ypos); + bool SendMouseButtonDown (Uint8 button, Uint16 xpos, Uint16 ypos); + + protected: + /** \brief Initializes the system */ + int OnInit (int argc, char* argv[]); + /** \brief Destroys the system (must be called!) */ + void OnDestroy (); + + /** \brief Updates the camera for further drawing */ + virtual void UpdateCamera (); + /** \brief Draws a grid of 16 x 16 tiles */ + void DrawGrid (); + /** \brief Draws the level and all the visible Entities */ + virtual void DrawWorld (); + /** \brief Draws orthographic overlay*/ + void DrawOverlay2D (); + + ModelBase *mModel; + CameraBase *mCamera; + + std::vector mOverlays; + + /** \brief The height of the canvas we're drawing on */ + unsigned int mWindowHeight; + /** \brief The width of the canvas we're drawing on */ + unsigned int mWindowWidth; + /** \brief Stores the current frame rate */ + int mFrameRate; + + bool mDrawAxis; + bool mDrawGrid; + int mGridSizeX; + int mGridSizeZ; + + /** \brief Font that is used in the console */ + OGLFT::Monochrome* mConsoleFont; + + friend class Engine; +}; + +} + +#include "ViewBaseGlobal.h" + +#endif // _VIEWBase_H diff --git a/engine/ViewBaseGlobal.h b/engine/ViewBaseGlobal.h new file mode 100644 index 0000000..6bc5295 --- /dev/null +++ b/engine/ViewBaseGlobal.h @@ -0,0 +1,17 @@ +#ifndef _VIEWGLOBAL_H +#define _VIEWGLOBAL_H + +namespace Engine { + +/** \brief Draws the given string at the given position using the current + * OpenGL transformations */ +void DrawGLString (float x, float y, const char* str); + +unsigned int GetWindowWidth(); +unsigned int GetWindowHeight(); + +int GetFrameRate (); + +} + +#endif /* _VIEWGLOBAL_H */ diff --git a/engine/VisualEntityBase.cc b/engine/VisualEntityBase.cc new file mode 100644 index 0000000..3d344e7 --- /dev/null +++ b/engine/VisualEntityBase.cc @@ -0,0 +1,72 @@ +#include "EntityBase.h" + +#include + +namespace Engine { + +void EntityVisualState::Draw () { + if (mBaseType == EntityBaseTypeActor) { + int i, segments; + segments = 20; + double x, z, rad, drad; + + drad = (M_PI * 2) / segments; + + glBegin (GL_TRIANGLE_FAN); + glVertex3f (0., 0., 0.); + for (i = 0; i <= segments; i++) { + rad = drad * i; + sincos (rad, &z, &x); + glVertex3f (x * mRadius, 0., -z * mRadius); + } + glEnd (); + + glDisable (GL_DEPTH_TEST); + glColor3f (0.8, 0., 0.2); + glBegin (GL_TRIANGLES); + glVertex3f (mRadius, 0., 0.); + glVertex3f (0., 0., -mRadius * 0.3); + glVertex3f (0., 0., mRadius * 0.3); + glEnd (); + glEnable (GL_DEPTH_TEST); + + return; + } else if (mBaseType == EntityBaseTypeBlock) { + glBegin (GL_QUADS); + glVertex3f (-0.5, 0., 0.5); + glVertex3f (0.5, 0., 0.5); + glVertex3f (0.5, 0., -0.5); + glVertex3f (-0.5, 0., -0.5); + glEnd (); + } else if (mBaseType == EntityBaseTypeParticle) { + int i, segments; + segments = 20; + double x, z, rad, drad; + + drad = (M_PI * 2) / segments; + + glDisable (GL_DEPTH_TEST); + glColor3f (0., 0.8, 0.1); + glBegin (GL_TRIANGLE_FAN); + glVertex3f (0., 0., 0.); + for (i = 0; i <= segments; i++) { + rad = drad * i; + sincos (rad, &z, &x); + glVertex3f (x * mRadius, 0., -z * mRadius); + } + glEnd (); + + glColor3f (0.8, 0., 0.2); + glBegin (GL_TRIANGLES); + glVertex3f (mRadius, 0., 0.); + glVertex3f (0., 0., -mRadius * 0.3); + glVertex3f (0., 0., mRadius * 0.3); + glEnd (); + glEnable (GL_DEPTH_TEST); + + return; + } +} + +} + diff --git a/engine/doc/Mainpage.h b/engine/doc/Mainpage.h new file mode 100644 index 0000000..4520861 --- /dev/null +++ b/engine/doc/Mainpage.h @@ -0,0 +1,96 @@ +/** \file documentation.h \mainpage Main Page + * + * This is the documentation of Engine -- a game engine without a name. + * + * \section General Information + * + * To get an overview over the most important functions and classes have a + * look at Engine. For the structure of the Engine look at Engine::Engine. In + * \ref usecases you find documentation on how things are supposed to work and + * what the ideas behind certain designs is. + * + * \section Physics + * + * So far we only use a very simple physics system which is mostly defined by + * the Engine::Physics class. It uses the coll2d collision library. Collision + * response so far only tries to prevent interpenetration. + * + * \section Networking + * + * For easier networking it is assumed that everything runs over a network. + * The game itself is rather a smart client to a simple synchronization + * protocol. What is being synchronized is the game state and the client + * simply displays the current state it knows. The client is "smart" as it + * tries to guess what the next state will be. + * + * For networking we want to use Enet http://enet.bespin.org/. + * + * Notes for networking: At + * http://www.gamedev.net/community/forums/topic.asp?topic_id=550962 is an + * interesting thread about modeling the updates. Especially the answer of + * Antheus at http://www.gamedev.net/community/forums/viewreply.asp?ID=3546077 + * describes two fundamental models: + * + * Level Triggering (GoF Observer Pattern): Entity A sends its changes to the + * Observer pattern and all objects that are interested in the state of Entity + * A get notfied by the observer. + * + * Edge Triggering: For each Entity A the Observer B is interested, it stores + * a flag for a value it is interested in (e.g. one for position, one for + * velocity, etc.). When Entity A modifies one of the values it notifies the + * observer that its current state has changed and the Observer sets the flag + * to "dirty". Anyone interested in events polls at the Observer and decides + * what to do with it. When the value is queried, the Observer reads the value + * from the Entity and clears the flag. + * + * For networking a combination can be used: During the simulation loop only + * Edge Triggering is performed and at the end of it, all dirty States get + * sent over the network. + * + * When notifying events such as "Add new Entity X" the user tomv describes at + * http://www.gamedev.net/community/forums/viewreply.asp?ID=3545586 a method + * to circumvent these messages. Instead, the server simply sends out updates + * about the Entities around a player where close Entities are updated more + * frequently than ones that are further away. It simply sends "Entity X + * changed by D". If a client does not know what the Entity X is, it queries + * the type and current state of the Entity X and therefore reconstructs the + * surroundings step-by-step. It is also robust against dropped messages of + * the form "Add new Entity X". + * + * At tomvas model there are some problems: How are events "Delete Entity X" + * handled? How to handle malicious clients that request the state of all + * Entities? (Solution for the last question: Request Queues). + * + * Kylotan cals tomvas model as a unreliable system for generic state + * [synchronization?] (http://www.gamedev.net/community/forums/viewreply.asp?ID=3548662). + * Additionally he mentions that it can increase occurences of short term + * unsynchronous states and reccomends using some reliable message passing. + * + * \section ToDos + * + * This is a loose list of items that are to be implemented. For a list of all + * todos within the code have a look at the \ref todo. + * + * \todo [high] Create a simple racing or asteroids game + * \todo [med] Clear all references of EntityVisualState and EntityGameState + * \todo [med] Clarify functionalities of CreateEntity, KillEntity, RegisterEntity, UnregisterEntity (which frees memory, which does only change the state of the Model?) + * \todo [med] Add basic networking + * \todo [med] Add serialization + * \todo [med] Add cal3d support + * \todo [med] Smooth Physics at convex collisions (somehow there is jitter) + * \todo [med] Create better collsion response + * \todo [med] In rare cases two Actors can penetrate each other (probably at slow velocities) + * \todo [low] Add a timer object to keep track of what is sucking performance. + * \todo [low] Add a inspector widget that shows information about a selected Entity + * \todo [low] Use a std::map as initialization parameters for + * Engine::Module::Init() + * + * Done: + * - [high] In Physics remove dependancy on the Model to pass on collision + * events + * - [high] Fix Physics bug when two actors collide actively (i.e. velocity + * towards each other) + * - [med] Better Game Input so that each Entity has its own ControllerState + * that can be modified + * - [med] (31-01-2009) Add support for loading levels (and saving!) + */ diff --git a/engine/doc/Usecases.h b/engine/doc/Usecases.h new file mode 100644 index 0000000..e0482fb --- /dev/null +++ b/engine/doc/Usecases.h @@ -0,0 +1,114 @@ +/** \page usecases Usecases + * + * This page contains some information on how various tasks are supposed to be + * performed with this engine. It should help understand how the internals + * work and how to avoid certain pitfalls. + * + * \section entity_management Entity Management + * + * Entities have to be created with Engine::CreateEntity() Factory Method. + * This will also cause the correct registration in all modules (especially in + * the Engine::Physics module). And create the registrations for it. + * + * Once an Entity is no more used, one has to call Engine::DestroyEntity(). + * + * \section entitiy_drawing Drawing of an Entity + * + * Aim: The visual state must always represent the visual state of the game + * state. + * + * To do this we always have to update the visual entity from the game state + * entity. + * + * \code + * View::DrawEntity (Entity* entity) { + * // if this entity has no visual part we don't need to draw + * if (! entity->mPhysicState) return; + * + * // update entity->mVisualState based on entity->mGameState + * + * // perform positioning based on entity->mPhysicState + * + * // perform drawing based on entity->mVisualState + * } + * \endcode + * + * \section game_input Game Input + * + * Each Entity has a ControllerState which keeps track on how the Entity is + * currently steered. This is then processed by the model each frame for each + * entity and updates the velocities / orientations etc. before the physical + * simulation is started. The player input simply forwards its input to the + * Entity with the Player Id and updates the ControllerState of the Entity + * with the Id. + * + * To add a new key state one has to follow these steps: + * - add a new EntityControllerKeyState e.g. EntityKeyStateCrouch (must be added + * above EntityKeyStateLast) + * - define the behaviour of the control in Entity::ProcessController() which + * updates the EntityPhysicalState of the Entity + * - add a command to be able to bind a key to the EntityControllerKeyState + * + * \section console_input Console Input + * + * Since it could be that the key being pressed has to be forwarded to another + * system such as the Menu or the Engine::Console system, we have to query the + * Engine::Model whether it is active and forward the input if it is the case. + * Otherwise we just execute the binding for the key (if it exists). + * + * \code + * Controller::OnKeyDown (SDLKey key) { + * if (mConsole->GetActive ()) + * mConsole->OnKeyDown (key); + * + * if (mBinding[key].size()) + * mCommands->QueueCommand (mBinding[key]); + * } + * \endcode + * + * \section addcommand Adding a Command to the Command System + * + * For the various Modules such as Engine::Controller, Engine::View, etc. + * separate files exist in which commands are defined. The filename pattern is + * usually [ModuleName]Commands.cc and contains the function + * \code void ModuleName::OnRegisterCommands () \endcode which is run during the + * initialization phase of the Engine in Engine::OnInit. + * + * Commands themselves have the signature: + * \code bool Cmd_CrazyCommand (std::vector args) \endcode + * and return true on success and error if some error has happened. The + * prefix \e Cmd_ is not mandatory but keeps things clear. + * + * Please make sure to call Engine::CommandSetErrorString in such a case. The + * message itself will be automatically reported to the Engine::Logging system + * as a warning since Command errors are hopefully not that important that + * they can crash the whole Engine. + * + * To register a Command to the Engine::Commands system you have to call \code + * AddCommand ("crazycommand", Cmd_CrazyCommand); \endcode in + * ModuleName::OnRegisterCommands. With this the Command is accessible through + * the Command system. + * + * \section addvariable Adding a Variable to the Engine::Variable System + * + * To register a variable to the Engine::Variables Module one \e must use a + * static variable of type Engine::Variable and use a special constructor: + * \code + * static Var_Variable PlayerSpeed ("playerspeed", "1.25"); + * \endcode + * This constructor takes care of registering the Variable PlayerSpeedVariable + * and its value to the Engine::Variables Module. The first argument is the + * name of the variable which can be used to retrieve a pointer to the + * Variable with Engine::GetVariable, Engine::GetVariableString, etc. The + * second argument is the value which the system will automatically try to + * convert to a float. This float is then returned if Engine::GetVariableFloat + * is called. + * + * The prefix \e Var_ is for readability in the code. + * + * The keyword \e static ensures that the lifespan of the variable is not only + * in a local function environment and thus mandatory. However it is + * registered at that time the program executes the first time the line in + * which the definition was made. To be safe define all your variables in the + * global scope of te source file of your Engine::Module. + */ diff --git a/engine/globals.h b/engine/globals.h new file mode 100644 index 0000000..3a14fd6 --- /dev/null +++ b/engine/globals.h @@ -0,0 +1,16 @@ +#ifndef _GLOBALS_H +#define _GLOBALS_H + +#define LOG_DEFAULT_LEVEL LogLevelMessage +#define LOG_MAX_MESSAGE_LENGTH 1024 + +#define VIEW_DEFAULT_HEIGHT 600 +#define VIEW_DEFAULT_WIDTH 800 + +#ifdef WIN32 + #include +#endif + +#include + +#endif // _GLOBALS_H diff --git a/engine/keytable.h b/engine/keytable.h new file mode 100644 index 0000000..142223f --- /dev/null +++ b/engine/keytable.h @@ -0,0 +1,166 @@ +#ifndef KEYTABLE_H +#define KEYTABLE_H + +#include + +/** \brief Defines the key codes for mouse buttons + * + * With this enum we can treat mouse button events the same way as we treat + * keyboard events. To do so we define the left button as the last keyboard + * button. + */ +enum MouseButton { + MouseButtonUnknown = SDLK_LAST, + MouseButtonLeft, + MouseButtonMiddle, + MouseButtonRight, + MouseButtonWheelUp, + MouseButtonWheelDown, + MouseButtonLast +}; + +struct key_definition { + int keynum; + const char *keystr; +}; + +const int keytable_last = MouseButtonLast; + +static const key_definition key_table[] = { + {SDLK_BACKSPACE, "backspace"}, + {SDLK_TAB, "tab"}, +// {SDLK_CLEAR, "12"}, + {SDLK_RETURN, "return"}, + {SDLK_PAUSE, "pause"}, + {SDLK_ESCAPE, "escape"}, + {SDLK_SPACE, "space"}, + {SDLK_EXCLAIM, "exclaim"}, +// {SDLK_QUOTEDBL, ""}, + {SDLK_HASH, "#"}, + {SDLK_DOLLAR, "$"}, + {SDLK_AMPERSAND, "&"}, +// {SDLK_QUOTE, "39"}, +// {SDLK_LEFTPAREN, "("}, +// {SDLK_RIGHTPAREN, ")"}, +// {SDLK_ASTERISK, "*"}, + {SDLK_PLUS, "+"}, + {SDLK_COMMA, ","}, + {SDLK_MINUS, "-"}, + {SDLK_PERIOD, "."}, + {SDLK_SLASH, "/"}, + {SDLK_0, "0"}, + {SDLK_1, "1"}, + {SDLK_2, "2"}, + {SDLK_3, "3"}, + {SDLK_4, "4"}, + {SDLK_5, "5"}, + {SDLK_6, "6"}, + {SDLK_7, "7"}, + {SDLK_8, "8"}, + {SDLK_9, "9"}, + {SDLK_COLON, ":"}, + {SDLK_SEMICOLON, ";"}, + {SDLK_LESS, "<"}, + {SDLK_EQUALS, "="}, + {SDLK_GREATER, ">"}, + {SDLK_QUESTION, "?"}, + {SDLK_AT, "@"}, + {SDLK_LEFTBRACKET, "["}, + {SDLK_BACKSLASH, "\\"}, + {SDLK_RIGHTBRACKET, "]"}, + {SDLK_CARET, "^"}, + {SDLK_UNDERSCORE, "_"}, + {SDLK_BACKQUOTE, "`"}, + {SDLK_a, "a"}, + {SDLK_b, "b"}, + {SDLK_c, "c"}, + {SDLK_d, "d"}, + {SDLK_e, "e"}, + {SDLK_f, "f"}, + {SDLK_g, "g"}, + {SDLK_h, "h"}, + {SDLK_i, "i"}, + {SDLK_j, "j"}, + {SDLK_k, "k"}, + {SDLK_l, "l"}, + {SDLK_m, "m"}, + {SDLK_n, "n"}, + {SDLK_o, "o"}, + {SDLK_p, "p"}, + {SDLK_q, "q"}, + {SDLK_r, "r"}, + {SDLK_s, "s"}, + {SDLK_t, "t"}, + {SDLK_u, "u"}, + {SDLK_v, "v"}, + {SDLK_w, "w"}, + {SDLK_x, "x"}, + {SDLK_y, "y"}, + {SDLK_z, "z"}, + {SDLK_KP0, "keypad_0"}, + {SDLK_KP1, "keypad_1"}, + {SDLK_KP2, "keypad_2"}, + {SDLK_KP3, "keypad_3"}, + {SDLK_KP4, "keypad_4"}, + {SDLK_KP5, "keypad_5"}, + {SDLK_KP6, "keypad_6"}, + {SDLK_KP7, "keypad_7"}, + {SDLK_KP8, "keypad_8"}, + {SDLK_KP9, "keypad_9"}, + {SDLK_KP_PERIOD, "keypad_period"}, + {SDLK_KP_DIVIDE, "keypad_devide"}, + {SDLK_KP_MULTIPLY, "keypad_multiply"}, + {SDLK_KP_MINUS, "keypad_minus"}, + {SDLK_KP_PLUS, "keypad_plus"}, + {SDLK_KP_ENTER, "keypad_enter"}, + {SDLK_KP_EQUALS, "keypad_equals"}, + {SDLK_UP, "up"}, + {SDLK_DOWN, "down"}, + {SDLK_RIGHT, "right"}, + {SDLK_LEFT, "left"}, + {SDLK_INSERT, "insert"}, + {SDLK_HOME, "home"}, + {SDLK_END, "end"}, + {SDLK_PAGEUP, "pageup"}, + {SDLK_PAGEDOWN, "pagedown"}, + {SDLK_F1, "f1"}, + {SDLK_F2, "f2"}, + {SDLK_F3, "f3"}, + {SDLK_F4, "f4"}, + {SDLK_F5, "f5"}, + {SDLK_F6, "f6"}, + {SDLK_F7, "f7"}, + {SDLK_F8, "f8"}, + {SDLK_F9, "f9"}, + {SDLK_F10, "f10"}, + {SDLK_F11, "f11"}, + {SDLK_F12, "f12"}, + {SDLK_F13, "f13"}, + {SDLK_F14, "f14"}, + {SDLK_F15, "f15"}, + {SDLK_NUMLOCK, "numlock"}, + {SDLK_CAPSLOCK, "capslock"}, + {SDLK_SCROLLOCK, "scrollock"}, + {SDLK_RSHIFT, "right_shift"}, + {SDLK_LSHIFT, "left_shift"}, + {SDLK_RCTRL, "right_ctrl"}, + {SDLK_LCTRL, "left_ctrl"}, + {SDLK_RALT, "right_alt"}, + {SDLK_LALT, "left_alt"}, + {SDLK_RMETA, "right_meta"}, + {SDLK_LMETA, "left_meta"}, + {SDLK_LSUPER, "left_super"}, /* Left "Windows" key */ + {SDLK_RSUPER, "right_super"}, /* Right "Windows" key */ + {SDLK_MODE, "altgr"}, /* "Alt Gr" key */ + {SDLK_COMPOSE, "compose"}, /* Multi-key compose key */ + {SDLK_DELETE, "delete"}, + {MouseButtonLeft, "mouse_left"}, + {MouseButtonMiddle, "mouse_middle"}, + {MouseButtonRight, "mouse_right"}, + {MouseButtonWheelUp, "mouse_wheelup"}, + {MouseButtonWheelDown, "mouse_wheeldown"}, + {keytable_last, NULL} +}; + +#endif /* KEYTABLE_H */ + diff --git a/engine/libraries/CMakeLists.txt b/engine/libraries/CMakeLists.txt new file mode 100644 index 0000000..d613c45 --- /dev/null +++ b/engine/libraries/CMakeLists.txt @@ -0,0 +1,3 @@ +ADD_SUBDIRECTORY ( mathlib ) +ADD_SUBDIRECTORY ( coll2d ) +ADD_SUBDIRECTORY ( oglft ) diff --git a/engine/libraries/coll2d/CMake/FindUnitTest++.cmake b/engine/libraries/coll2d/CMake/FindUnitTest++.cmake new file mode 100644 index 0000000..261e7a8 --- /dev/null +++ b/engine/libraries/coll2d/CMake/FindUnitTest++.cmake @@ -0,0 +1,28 @@ +# - Try to find UnitTest++ +# +# + +SET (UNITTEST++_FOUND FALSE) + +FIND_PATH (UNITTEST++_INCLUDE_DIR UnitTest++.h /usr/include/unittest++ /usr/local/include/unittest++ $ENV{UNITTESTXX_PATH}/src $ENV{UNITTESTXX_INCLUDE_PATH}) + +FIND_LIBRARY (UNITTEST++_LIBRARY NAMES UnitTest++ PATHS /usr/lib /usr/local/lib $ENV{UNITTESTXX_PATH} ENV{UNITTESTXX_LIBRARY_PATH}) + +IF (UNITTEST++_INCLUDE_DIR AND UNITTEST++_LIBRARY) + SET (UNITTEST++_FOUND TRUE) +ENDIF (UNITTEST++_INCLUDE_DIR AND UNITTEST++_LIBRARY) + +IF (UNITTEST++_FOUND) + IF (NOT UnitTest++_FIND_QUIETLY) + MESSAGE(STATUS "Found UnitTest++: ${UNITTEST++_LIBRARY}") + ENDIF (NOT UnitTest++_FIND_QUIETLY) +ELSE (UNITTEST++_FOUND) + IF (UnitTest++_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find UnitTest++") + ENDIF (UnitTest++_FIND_REQUIRED) +ENDIF (UNITTEST++_FOUND) + +MARK_AS_ADVANCED ( + UNITTEST++_INCLUDE_DIR + UNITTEST++_LIBRARY + ) diff --git a/engine/libraries/coll2d/CMakeLists.txt b/engine/libraries/coll2d/CMakeLists.txt new file mode 100644 index 0000000..f4d35d7 --- /dev/null +++ b/engine/libraries/coll2d/CMakeLists.txt @@ -0,0 +1,21 @@ +PROJECT (COLL2D) + +CMAKE_MINIMUM_REQUIRED (VERSION 2.6) + +# Needed for UnitTest++ +LIST( APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMake ) + +SET ( COLL2D_SRCS + src/coll2d.cc + ) + +INCLUDE_DIRECTORIES ( include ../mathlib/ ) + +SET_TARGET_PROPERTIES ( ${PROJECT_EXECUTABLES} PROPERTIES + LINKER_LANGUAGE CXX +) + +SUBDIRS (tests) + +ADD_LIBRARY ( coll2d ${COLL2D_SRCS} ) + diff --git a/engine/libraries/coll2d/include/coll2d.h b/engine/libraries/coll2d/include/coll2d.h new file mode 100644 index 0000000..2693a40 --- /dev/null +++ b/engine/libraries/coll2d/include/coll2d.h @@ -0,0 +1,346 @@ +#ifndef _COLL2D_H +#define _COLL2D_H + +/** \brief Coll2d - A 2d collision detection library + * \author Martin Felis + * + * This library provides functions to detect collisions between polygons and + * spheres. + * + * Notes: + * - vertices of polygons are described in local coordinates + * - return values of check_collision are in global coordinates + * - all Shapes get copied to a temporary instance which will then get + * transferred into global coordinates + * - Polygons are assumed to be convex and the vertices are stored + * counter clockwise. + * - The transformation of the global position and velocities towards + * relative position and velocities happens in the + * int check_collision__ functions! + */ + +#include +#include +#include + +#include + +namespace coll2d { + +/** \brief Contains the information of a collision + * + * \param normal The normal of the reference plane + * \param point The actual point where the collision happens in global + * cooldinates + * \param time If both objects move for this amount of time the contact + * will occur. + * \param reference_shape + * If 0, the first shape passed to he check_collision function + * is the reference shape, otherwise the second. +*/ +struct CollisionInfo { + vector3d normal; + vector3d point; + float time; + int reference_shape; + + CollisionInfo () : normal (0., 0., 0.), point (0., 0., 0.), time (-1.), reference_shape (-1) { + } + + CollisionInfo& operator= (const CollisionInfo& info) { + if (this != &info) { + normal = info.normal; + point = info.point; + time = info.time; + reference_shape = info.reference_shape; + } + + return *this; + } + + void doPrint (const char* msg) { + std::cout << msg; + std::cout << "Time = " << time << std::endl; + normal.print ("Normal = "); + point.print ( "Point = "); + std::cout << "Reference = " << reference_shape << std::endl; + } +}; + +/** \brief Base class for all shapes + * + */ +class Shape { +protected: + vector3d mPosition; + vector3d mVelocity; + float mAngle; + float mAngleVelocity; + + virtual void dummy() { + } +public: + Shape(): + mPosition (0., 0., 0.), + mVelocity (0., 0., 0.), + mAngle (0.), + mAngleVelocity (0.) { + } + Shape (const Shape &shape): + mPosition (shape.mPosition), + mVelocity (shape.mVelocity), + mAngle (shape.mAngle), + mAngleVelocity (shape.mAngleVelocity) + { } + + virtual ~Shape () {}; + + /** \brief Creates and returns a copy of itself */ + virtual Shape* getCopy () = 0; + + void setPosition(vector3d position) { + mPosition = position; + } + vector3d getPosition() { + return mPosition; + } + void setVelocity(vector3d velocity) { + mVelocity = velocity; + } + vector3d getVelocity() { + return mVelocity; + } + + void setAngle (const float &angle) { + mAngle = angle; + } + float getAngle () { + return mAngle; + } + void setAngleVelocity (float angle_velocity) { + mAngleVelocity = angle_velocity; + } + float getAngleVelocity () { + return mAngleVelocity; + } + + virtual void doPrintType() { + std::cout << "Shape" << std::endl; + } + virtual void doPrint (const char* name) { + std::cout << name << "" << std::endl; + } + friend int check_collision_rel(Shape *shape_a, Shape *shape_b, + vector3d *velocity_b, CollisionInfo* info); +}; + +class Polygon: public Shape { +private: + unsigned int mVerticeCount; + vector3d *mVertices; + bool mFreeVertices; +public: + Polygon() { + mVerticeCount = 0; + mVertices = NULL; + mFreeVertices = false; + } + Polygon(unsigned int n) { + mVerticeCount = n; + mVertices = new vector3d[n]; + mFreeVertices = true; + } + Polygon(unsigned int n, vector3d *vertices) { + mVerticeCount = n; + mFreeVertices = false; + + if (vertices == NULL && n > 0) { + mVertices = new vector3d [n]; + mFreeVertices = true; + } + + mVertices = vertices; + } + Polygon (const Polygon &polygon) : Shape (polygon) { + mVerticeCount = polygon.mVerticeCount; + mFreeVertices = true; + + mVertices = new vector3d[mVerticeCount]; + memcpy (mVertices, polygon.mVertices, sizeof (vector3d) * mVerticeCount); + } + virtual ~Polygon () { + if (mFreeVertices == true && mVertices) + delete[] mVertices; + } + + virtual Polygon* getCopy () { + Polygon *copy = new Polygon (*this); + + assert (copy); + + return copy; + } + + virtual void doPrintType() { + std::cout << "Polygon" << std::endl; + } + + virtual void doPrint (const char* name) { + std::cout << name << " (Polygon)" << std::endl; + + std::cout << "mVerticeCount = " << mVerticeCount << std::endl; + unsigned int i; + for (i = 0; i < mVerticeCount; i ++) { + std::cout << i << " = " << mVertices[i][0] << ", " << + mVertices[i][1] << ", " << + mVertices[i][2] << std::endl; + } + std::cout << "mPosition = " << mPosition[0] <<", " << + mPosition[1] << ", " << + mPosition[2] << std::endl; + + std::cout << "mVelocity = " << mVelocity[0] <<", " << + mVelocity[1] << ", " << + mVelocity[2] << std::endl; + + std::cout << "mAngle = " << mAngle << std::endl; + std::cout << "mAngleVelocity = " << mAngleVelocity << std::endl; + } + + unsigned int getVerticeCount () { + return mVerticeCount; + } + + vector3d& getVertice (unsigned int i) { + assert (i >= 0 && i < mVerticeCount); + return mVertices [i]; + } + + void setVertice (unsigned int i, const vector3d &vertice) { + assert (i >= 0 && i < mVerticeCount); + mVertices[i] = vertice; + } + + friend int check_collision_polygon_sphere(Shape *polygon_a, + Shape *sphere_b, CollisionInfo* info); +}; + +class Sphere: public Shape { +private: + float mRadius; +public: + Sphere (float radius) { + mRadius = radius; + Shape::setPosition (vector3d (0., 0., 0.)); + } + + Sphere(float radius, const vector3d &position) { + mRadius = radius; + Shape::setPosition(position); + } + + Sphere (const Sphere &sphere): + Shape (sphere), + mRadius (sphere.mRadius) { } + + virtual Sphere* getCopy() { + Sphere* copy = new Sphere (*this); + + return copy; + } + + virtual void doPrintType() { + std::cout << "Sphere" << std::endl; + } + + virtual void doPrint (const char* name) { + std::cout << name << " (Sphere)" << std::endl; + + std::cout << "mRadius = " << mRadius << std::endl; + std::cout << "mPosition = " << mPosition[0] <<", " << + mPosition[1] << ", " << + mPosition[2] << std::endl; + + std::cout << "mVelocity = " << mVelocity[0] <<", " << + mVelocity[1] << ", " << + mVelocity[2] << std::endl; + + std::cout << "mAngle = " << mAngle << std::endl; + std::cout << "mAngleVelocity = " << mAngleVelocity << std::endl; + } + + + void setRadius (float radius) { + mRadius = radius; + } + + float getRadius () { + return mRadius; + } + + friend int check_collision_polygon_sphere(Shape *polygon_a, + Shape *sphere_b, CollisionInfo* info); +}; + +/** \brief The higher level function to call for collision detection + * + * \param stepsize the timestep within we want to check for the collision + * \param shape_a first shape + * \param shape_b second shape + * \param info information about the collision will be to info + * + * \returns 0 - no collision, negative on error, positive on collision + */ +int check_collision(float timestep, Shape *shape_a, Shape *shape_b, CollisionInfo* info); + +/** \brief The higher level function to call for collision detection + * + * \param shape_a first shape + * \param shape_b second shape + * \param velocity_b relative velocity of b to a + * \param info information about the collision will be to info + * + * \returns 0 - no collision, negative on error, positive on collision + */ +int check_collision_rel(float timestep, Shape *shape_a, Shape *shape_b, vector3d *velocity_b, + CollisionInfo* info); + +/** \brief Callback signature for the check functions */ +typedef int (*check_cb)(float timestep, Shape *shape_a, Shape *shape_b, CollisionInfo* info); + +/** \brief Returns the check functions which performs the checks depending + * on their types. */ +check_cb get_check(Shape *shape_a, Shape *shape_b); + +/** \brief Performs a check between a polygon and a sphere + */ +int check_collision_polygon_sphere(float timestep, Shape *shape_a, Shape *shape_b, + CollisionInfo* info); + +/** \brief Performs a check between a sphere and a sphere + */ +int check_collision_sphere_sphere(float timestep, Shape *shape_a, Shape *shape_b, + CollisionInfo* info); + + +/** \brief Calculates the time it takes for a point to touch a plane + * + * \param normal normal vector of the plane + * \param plane_point point on the vector + * \param point point that is moving + * \param velocity velocity of the point + * + * \returns -1 if point is moving away from the plane (or is below and + * moves even further below + * >= 0 if point is moving along the plane or towards the plane + * + * This function depends on the scale of velocity. It is assumed + * that velocity represents the displacement in one frame. In this + * case a return value > 1 means there will not be a contact + * within this frame. + */ +float calculate_contact_plane_point(vector3d &normal, vector3d &plane_point, vector3d &point, vector3d &velocity); + +} + +#endif /* _COLL2D_H */ diff --git a/engine/libraries/coll2d/include/coll2d_errors.h b/engine/libraries/coll2d/include/coll2d_errors.h new file mode 100644 index 0000000..5ac1a25 --- /dev/null +++ b/engine/libraries/coll2d/include/coll2d_errors.h @@ -0,0 +1,9 @@ +#ifndef _COLL2D_ERRORS +#define _COLL2D_ERRORS + +#define CHECK_ERROR_UNKNOWN -9999 +#define CHECK_ERROR_NOT_IMPLEMENTED -1 +#define CHECK_ERROR_INVALID_TYPES -2 +#define CHECK_ERROR_OVERLAP -3 + +#endif /* _COLL2D_ERRORS */ diff --git a/engine/libraries/coll2d/src/coll2d.cc b/engine/libraries/coll2d/src/coll2d.cc new file mode 100644 index 0000000..aeaabcd --- /dev/null +++ b/engine/libraries/coll2d/src/coll2d.cc @@ -0,0 +1,572 @@ +#include +#include + +#include + +using namespace std; + +namespace coll2d { + +int check_collision (float timestep, Shape *shape_a, Shape *shape_b, CollisionInfo *info) { + check_cb check = NULL; + + check = get_check (shape_a, shape_b); + + if ( check == NULL ) { + return CHECK_ERROR_NOT_IMPLEMENTED; + } + + return check (timestep, shape_a, shape_b, info); +}; + +check_cb get_check (Shape *shape_a, Shape *shape_b) { + if ( (dynamic_cast (shape_a) != NULL) + && (dynamic_cast (shape_b) != NULL) ) { + return check_collision_polygon_sphere; + } + else if ( (dynamic_cast (shape_b) != NULL) + && (dynamic_cast (shape_a) != NULL) ) { + return check_collision_polygon_sphere; + } else if ( (dynamic_cast (shape_b) != NULL) + && (dynamic_cast (shape_a) != NULL) ) { + return check_collision_sphere_sphere; + } + + return NULL; +} + +int check_collision_rel (float timestep, Shape *shape_a, Shape *shape_b, vector3d *velocity_b, CollisionInfo *info) { + check_cb check = NULL; + + shape_b->setVelocity (*velocity_b); + check = get_check (shape_a, shape_b); + + if ( check == NULL ) { + return CHECK_ERROR_NOT_IMPLEMENTED; + } + + return check (timestep, shape_a, shape_b, info); +}; + +/** \brief calculates the time for a moving point to touch a plane + * + * \param normal The normal of the plane + * \param plane_point a point on the plane + * \param point the moving point + * \param velocity the velocity of the point + * + * \returns If the return value is negative, the point is moving away from the + * plane or is below the plane and thus does not touch it. Otherwise + * point + (return value) * velocity + * is on the plane. + */ +float calculate_contact_plane_point (vector3d &normal, vector3d &plane_point, vector3d &point, vector3d &velocity) { + vector3d temp_vector (point); + temp_vector -= plane_point; + + // If the following is < 0 then the point is "below" the plane + if (normal * temp_vector < 0) { + // If the following is < 0 then we move even deeper + if ( normal * velocity < 0 ) + return -999; + // Or towards the upper side of the plane. + else + return -111; + } + + int i,imax = -1; + float vmax = 0.; + for (i = 0; i < 3; i++) { + if ( normal[i] * velocity[i] < vmax) { + vmax = normal[i] * velocity[i]; + imax = i; + } + } + + if (imax == -1) { + return -1.; + } + + return (normal[imax] * plane_point[imax] - normal[imax] * point[imax]) / vmax; +} + +// #define VERBOSE_PHASE + +/** \brief Checks whether a sphere penetrates one of the polygones edges + * + * This check only finds intersections of the sphere with one of the sides in + * other words it will not find intersections with the vertices. As for convex + * polygons a sphere will either touch one of the edges or one of the + * vertices. + * + * First thing we do is calculate the normals pointing out of the polygon. + * Then for each edge e of the polygon (is being done by the function + * calculate_contatc_plane_point (...): + * - Calculate the point that would be the first to touch edge e if a contact + * occurs + * - Calculate the time it would take for this point to touch the edge with + * the relative velocity of the sphere + * + * Next we calculatefor each edge the point on the sphere which would be the first to touch + * the polygon, if it was moving towards the current edge. + */ +int check_collision_polygon_sphere_sides (Shape *shape_a, Shape *shape_b, CollisionInfo *info) { + Polygon* polygon = dynamic_cast (shape_a); + Sphere* sphere = dynamic_cast (shape_b); + + if ( polygon == NULL && sphere == NULL) { + polygon = dynamic_cast (shape_b); + sphere = dynamic_cast (shape_a); + } + + if (!polygon || !sphere) { + return CHECK_ERROR_INVALID_TYPES; + } + + vector3d velocity_sphere = sphere->getVelocity(); + + vector3d temp_vector (velocity_sphere); + if (temp_vector.length2() == 0.) + return 0; + + // Calculate the normals pointing OUT of the polygon + float *t = NULL, t_min = 10000.; + unsigned int i,j,vertices,count = 0; + vertices = polygon->getVerticeCount(); + vector3d *normals = NULL; + vector3d side (0., 0., 0.); + vector3d up (0., 1., 0.); +/* + if (polygon->getPosition().length() > 0.) { + for (i = 0; i < vertices; i++) { + polygon->getVertice(i) += polygon->getPosition(); + } + } +*/ + // normals contains all the normal vectors out of + // the polygon + normals = new vector3d[vertices]; + // The array t contains the time it takes until the sphere + // would touch the plane with the given velocity + t = new float[vertices]; + + for (i = 0; i < vertices; i++) { + j = (i + 1) % vertices; + side = polygon->getVertice(j); + side -= polygon->getVertice(i); + normals[i] = cross_product (side, up); + normals[i] /= normals[i].length (); + + // temp_vector is the point on the sphere that would touch the plane first + // if it was directly moving towards it + temp_vector = normals[i]; + temp_vector *= - sphere->getRadius(); + temp_vector += sphere->getPosition(); + + t[i] = calculate_contact_plane_point (normals[i], polygon->getVertice(i), temp_vector, velocity_sphere); + +#ifdef VERBOSE_PHASE + cout << "= next =" << endl; + cout << "t[" << i << "] = " << t[i] << " normal = "; normals[i].print (); + cout << "point = "; temp_vector.print(); + cout << "plane point = "; polygon->getVertice(i).print(); +#endif + if (t[i] < 0) + normals[i].setValues (0., 0., 0.); + else { + if (t[i] < t_min) + t_min = t[i]; + count ++; + } + } + + if (count == 0) { + delete[] t; + delete[] normals; + return 0; + } + + vector3d contact_point, cp_near; + + /// \ToDo: Instead of checking all planes, remember which ones to check + for (i = 0; i < vertices; i++) { + if ( t[i] >= 0) { + // So there seems to occur an collision. Now we have to check, + // whether this is actually between the points that define the + // plane. + unsigned int min_distance_vertex_index = i, other_vertice = (i + 1) % vertices; + float min_distance; + j = (i + 1) % vertices; + // First we calculate the actual contact point: + temp_vector = normals[i]; + temp_vector *= - sphere->getRadius(); + temp_vector *= t[i]; + temp_vector += sphere->getPosition(); + + contact_point = normals[i]; + contact_point *= -sphere->getRadius(); + contact_point += temp_vector; + + // Now we calculate to which vertice this point is closer: + temp_vector = contact_point; + temp_vector -= polygon->getVertice(i); + min_distance = temp_vector.length2 (); + + temp_vector = contact_point; + temp_vector -= polygon->getVertice(j); + + if (min_distance > temp_vector.length2 ()) { + min_distance_vertex_index = j; + other_vertice = i; + } + + // Then we want to see, whether the contact point actually lies + // on the vector that goes from one vertice to the other. For + // this we calculate (cp - near) * (far - near). The value + // cannot be greater than 1. (otherwise we would have picked + // the other vector as near. If it is < 0 then it is outside + // of the polygon. + temp_vector = polygon->getVertice(other_vertice); + temp_vector -= polygon->getVertice(min_distance_vertex_index); + + temp_vector /= temp_vector.length(); + + cp_near = contact_point; + cp_near -= polygon->getVertice(min_distance_vertex_index); + + float val = cp_near * temp_vector; + if ( (val >= 0 && val <= 1) && (t[i] <= 1.)) { + info->time = t[i]; + info->normal = normals[i]; + + info->point = sphere->getPosition(); + info->point += sphere->getVelocity() * t[i]; + info->point -= normals[i] * sphere->getRadius (); + + delete[] t; + delete[] normals; + return 1; + } + } + } + + /* + if ( (t_min <= 1.) && (t_min >= 0.)) { + return 1; + } + */ + + delete[] t; + delete[] normals; + + if (t_min > 1.) + return 0; + + return 0; +// return CHECK_ERROR_UNKNOWN; +}; + +int check_collision_polygon_sphere_vertices (Shape *shape_a, Shape *shape_b, CollisionInfo *info) { + Polygon* polygon = dynamic_cast (shape_a); + Sphere* sphere = dynamic_cast (shape_b); + + if ( polygon == NULL && sphere == NULL) { + polygon = dynamic_cast (shape_b); + sphere = dynamic_cast (shape_a); + } + + if (!polygon || !sphere) { + return CHECK_ERROR_INVALID_TYPES; + } + + // Transform global velocities to relative velocities: + /* + sphere->setVelocity (sphere->getVelocity() - polygon->getVelocity()); +*/ + vector3d velocity_sphere = sphere->getVelocity(); + + vector3d temp_vector (velocity_sphere); + if (temp_vector.length2() == 0.) { +#ifdef VERBOSE_PHASE + cout << "sphere has no velocity!" << endl; +#endif + return 0; + } + + vector3d contact_point, cp_near; + + // Okay if we happen to be here, there still might be one of + // the corners colliding with the sphere. + float a, b, c, t1, t2, t_min = 10000; + unsigned int i, j, vertices; + vector3d position (sphere->getPosition()); + vector3d velocity (sphere->getVelocity()); + float radius = sphere->getRadius(); + vertices = polygon->getVerticeCount(); + + for (i = 0; i < vertices; i++) { + vector3d vertice (polygon->getVertice(i)); + a = b = c = 0; + + // We must ensure that all happens in the x-z-plane (so y == 0.) + vertice[1] = 0.; + position[1] = 0.; + + vector3d distance_sphere_vertice; + for (j = 0; j < 3; j++) { + distance_sphere_vertice[j] = position[j] - vertice[j]; + } +#ifdef VERBOSE_PHASE + cout << " ===== " << endl; + cout << "vertice = "; + vertice.print (); + velocity.print (); + position.print (); + cout << "radius = " << radius << endl; + cout << "distance = " << distance_sphere_vertice.length () << endl;; +#endif + for (j = 0; j < 3; j++) { + a += velocity[j]*velocity[j]; + b += 2 * velocity[j] * (position[j] - vertice[j]); + c += position[j] * position[j] - 2 * position[j] * vertice[j] + vertice[j] * vertice[j]; + } + c -= radius * radius; + + if (solve_quadratic (a, b, c, &t1, &t2)) { +// cout << "solve_quadratic = " << t1 << "\t" << t2 << endl; + float t_temp_min = t2; + if (t1 < t2) + t_temp_min = t1; + + if (t_temp_min < t_min) { + if ((t_temp_min <= 1.) && (t_temp_min >= 0.)) { + t_min = t_temp_min; + temp_vector = position; + temp_vector -= vertice; + temp_vector /= temp_vector.length (); +#ifdef VERBOSE_PHASE + cout << "new closest point t = " << t_min << endl; +#endif + info->time = t_min; + info->normal = temp_vector; + info->point = vertice; + } + } + } + } + + if ( (t_min <= 1.) && (t_min >= 0.)) { + return 1; + } + + if (t_min > 1.) + return 0; + + return CHECK_ERROR_UNKNOWN; +}; + +int check_collision_polygon_sphere (float timestep, Shape *shape_a, Shape *shape_b, CollisionInfo *info) { + /* If the first shape given is a sphere and the second one a shape, we have + * to remember it to set the right value to *info. + */ + bool swapped = false; + Polygon* polygon_cast_test = dynamic_cast (shape_a); + Sphere* sphere_cast_test = dynamic_cast (shape_b); + + if ( polygon_cast_test == NULL && sphere_cast_test == NULL) { + polygon_cast_test = dynamic_cast (shape_b); + sphere_cast_test = dynamic_cast (shape_a); + + swapped = true; + } + + if (!polygon_cast_test || !sphere_cast_test) { + return CHECK_ERROR_INVALID_TYPES; + } + + Polygon polygon_copy (*polygon_cast_test); + Sphere sphere_copy (*sphere_cast_test); + + unsigned int vertices; + vertices = polygon_copy.getVerticeCount(); + +#ifdef VERBOSE_PHASE + cout << "======== New Polygon Sphere Test: Phase 1" << endl; + if (swapped) + cout << "Swapped!" << endl; + cout << "Polygon position: "; + polygon_copy.getPosition().print (); + cout << "Polygon velocity: "; + polygon_copy.getVelocity().print (); + cout << "Sphere position: "; + sphere_copy.getPosition().print (); + cout << "Sphere velocity: "; + sphere_copy.getVelocity().print (); + cout << "Timestep : " << timestep << endl; + + cout << "Vertices before transformation = " << endl; + int i; + for (i = 0; i < vertices; i++) { + polygon_copy.getVertice(i).print(); + } +#endif + + // Here we translate the polygon and its velocity into the reference frame + // of the polygon. + sphere_copy.setPosition (sphere_copy.getPosition() - polygon_copy.getPosition()); + sphere_copy.setPosition (sphere_copy.getPosition().rotate_y (-polygon_copy.getAngle())); + // Here scale the velocity so that our time horizon lies in [0., 1.] + sphere_copy.setVelocity ((sphere_copy.getVelocity() - polygon_copy.getVelocity() ) * timestep ); + sphere_copy.setVelocity (sphere_copy.getVelocity().rotate_y (-polygon_copy.getAngle())); + +#ifdef VERBOSE_PHASE + cout << "Vertices after transformation = " << endl; + for (i = 0; i < vertices; i++) { + polygon_copy.getVertice(i).print(); + } +#endif + +#ifdef VERBOSE_PHASE + cout << "After transformation:" << endl; + cout << "Polygon position: "; + polygon_copy.getPosition().print (); + cout << "Polygon velocity: "; + polygon_copy.getVelocity().print (); + cout << "Sphere position: "; + sphere_copy.getPosition().print (); + cout << "Sphere velocity: "; + sphere_copy.getVelocity().print (); +#endif + + CollisionInfo sides_info; + CollisionInfo vertices_info; + int sides_result = 0, vertices_result = 0; + + /* Tricky part: Since polygons are assumed to be convex and we calculated + * with both methods a collision, we take the sides result. Since they are + * convex the first event to happen is the collision with the side. + * Otherwise it would first touch the vertice and then the side, which is + * not possible. (\Todo true?) + */ + + sides_result = check_collision_polygon_sphere_sides (&polygon_copy, &sphere_copy, &sides_info); + // We have to transform the time back to [0., timestep] + sides_info.time *= timestep; +#ifdef VERBOSE_PHASE + cout << "sides_result = " << sides_result << " t = " << sides_info.time << endl; +#endif + if (sides_result > 0) { + sides_info.point.rotate_y (polygon_copy.getAngle()); + sides_info.normal.rotate_y (polygon_copy.getAngle()); + sides_info.point += polygon_copy.getPosition(); + memcpy (info, &sides_info, sizeof (CollisionInfo)); + sides_info.time *= timestep; + if (swapped == true) { + info->reference_shape = 1; + } else { + info->reference_shape = 0; + } + return sides_result; + } + + vertices_result = check_collision_polygon_sphere_vertices (&polygon_copy, &sphere_copy, &vertices_info); + // We have to transform the time back to [0., timestep] + vertices_info.time *= timestep; +#ifdef VERBOSE_PHASE + cout << "vertices_res = " << vertices_result << " t = " << vertices_info.time << endl; + cout << "vertices_point = "; + vertices_info.point.print(); +#endif + if (vertices_result > 0) { + vertices_info.point.rotate_y (polygon_copy.getAngle()); + vertices_info.normal.rotate_y (polygon_copy.getAngle()); + vertices_info.point += polygon_copy.getPosition(); + memcpy (info, &vertices_info, sizeof (CollisionInfo)); + if (swapped == true) { + info->reference_shape = 1; + } else { + info->reference_shape = 0; + } + return vertices_result; + } + + if ((sides_result == 0) && (vertices_result == 0)) + return 0; + + return CHECK_ERROR_UNKNOWN; +}; + +int check_collision_sphere_sphere (float timestep, Shape *shape_a, Shape *shape_b, CollisionInfo *info) { + /* If the first shape given is a sphere and the second one a shape, we have + * to remember it to set the right value to *info. + */ + Sphere* sphere_test_a = dynamic_cast (shape_a); + Sphere* sphere_test_b = dynamic_cast (shape_b); + + if (!sphere_test_a || !sphere_test_b) { + return CHECK_ERROR_INVALID_TYPES; + } + + Sphere sphere_a (*sphere_test_a); + Sphere sphere_b (*sphere_test_b); + + // First we check whether there is actually a relative velocity towards each + // other: + vector3d rel_velocity = sphere_b.getVelocity (); + rel_velocity -= sphere_a.getVelocity (); + rel_velocity *= timestep; + if (rel_velocity.length2() == 0.) + return 0; + + vector3d rel_position = sphere_b.getPosition (); + rel_position -= sphere_a.getPosition (); + + // We need to ignore height differences + rel_position[1] = 0.; + rel_velocity[1] = 0.; + + vector3d rel_position_norm = rel_position; + rel_position_norm.normalize (); + + float velocity_projection = rel_position_norm * rel_velocity; + float distance = rel_position.length(); + + if (velocity_projection >= 0.) + return 0; + + float t = (- distance + sphere_a.getRadius () + sphere_b.getRadius ()) / velocity_projection; + +#ifdef VERBOSE_PHASE + cout << "==== New Sphere Sphere Test ====" << endl; + cout << "Relative Position = "; + rel_position.print (); + cout << "Relative Velocity = "; + rel_velocity.print (); + cout << "velocity_projection = " << velocity_projection << endl; + cout << "distance = " << distance << endl; + cout << "- distance + Ra + Rb = " << - distance + sphere_a.getRadius () + sphere_b.getRadius () << endl; + cout << "t = " << t << endl; +#endif + + // if t < 0 this means we would have to move back in time + // to get to the point where the two spheres touched. In other words: they + // are overlapping, hence it is an invalid state! + if (t < 0) + return CHECK_ERROR_OVERLAP; + if (t > 1) + return 0; + + info->point = sphere_a.getPosition() + rel_position_norm * t; + info->time = t * timestep; + + if (sphere_a.getVelocity().length2() == 0.) { + info->normal = rel_position_norm; + info->reference_shape = 0; + } else { + info->normal = rel_position_norm * -1.; + info->reference_shape = 1; + } + return 1; +}; + +} diff --git a/engine/libraries/coll2d/tests/CMakeLists.txt b/engine/libraries/coll2d/tests/CMakeLists.txt new file mode 100644 index 0000000..da7a20b --- /dev/null +++ b/engine/libraries/coll2d/tests/CMakeLists.txt @@ -0,0 +1,51 @@ +PROJECT (ENGINETESTS) + +CMAKE_MINIMUM_REQUIRED (VERSION 2.6) + +# Needed for UnitTest++ +LIST( APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/../CMake ) + +SET ( TESTS_SRCS + main.cc + general.cc + polygon_sphere.cc + sphere_sphere.cc + ) + +FIND_PACKAGE (UnitTest++) + +INCLUDE_DIRECTORIES ( ../mathlib/ ) + +SET_TARGET_PROPERTIES ( ${PROJECT_EXECUTABLES} PROPERTIES + LINKER_LANGUAGE CXX +) + +IF ( UNITTEST++_FOUND ) + + ADD_EXECUTABLE ( coll2dtests ${TESTS_SRCS} ) + + INCLUDE_DIRECTORIES ( ${UNITTEST++_INCLUDE_DIR} ) + + SET_TARGET_PROPERTIES ( coll2dtests PROPERTIES + LINKER_LANGUAGE CXX + OUTPUT_NAME runtests + ) + + TARGET_LINK_LIBRARIES ( coll2dtests + ${UNITTEST++_LIBRARY} + mathlib + coll2d + ) + + OPTION (RUN_AUTOMATIC_TESTS "Perform automatic tests after compilation?" OFF) + + IF (RUN_AUTOMATIC_TESTS) + ADD_CUSTOM_COMMAND (TARGET coll2dtests + POST_BUILD + COMMAND coll2dtests + COMMENT "Running automated tests..." + ) + ENDIF (RUN_AUTOMATIC_TESTS) + +ENDIF ( UNITTEST++_FOUND ) + diff --git a/engine/libraries/coll2d/tests/general.cc b/engine/libraries/coll2d/tests/general.cc new file mode 100644 index 0000000..f68289a --- /dev/null +++ b/engine/libraries/coll2d/tests/general.cc @@ -0,0 +1,284 @@ +#include + +#include + +using namespace coll2d; +using namespace std; + +TEST ( SphereCopyConstructer ) { + Sphere sphere_a (123.); + sphere_a.setVelocity (vector3d(1., 2., 3.)); + + Sphere sphere_b (sphere_a); + CHECK_EQUAL (sphere_a.getRadius (), sphere_b.getRadius ()); + + vector3d velocity_a = sphere_a.getVelocity (); + vector3d velocity_b = sphere_b.getVelocity (); + + CHECK (velocity_a == velocity_b ); + + sphere_b.setVelocity (vector3d (0., 0., 0.)); + + velocity_a = sphere_a.getVelocity (); + velocity_b = sphere_b.getVelocity (); + + CHECK (velocity_a != velocity_b ); +} + +TEST ( PolygonCopyConstructer ) { + Polygon polygon_a (3); + + vector3d vertice0 (-1., 0., -1.); + vector3d vertice1 (1., 0., -1.); + vector3d vertice2 (0., 0., 1.); + + polygon_a.setVertice (0, vertice0); + polygon_a.setVertice (1, vertice1); + polygon_a.setVertice (2, vertice2); + + polygon_a.setVelocity (vector3d(1., 2., 3.)); + + Polygon polygon_b (polygon_a); + + vector3d velocity_a = polygon_a.getVelocity (); + vector3d velocity_b = polygon_b.getVelocity (); + + CHECK (vertice0 == polygon_b.getVertice (0)); + CHECK (vertice1 == polygon_b.getVertice (1)); + CHECK (vertice2 == polygon_b.getVertice (2)); + + polygon_b.setVelocity (vector3d (0., 0., 0.)); + velocity_a = polygon_a.getVelocity (); + velocity_b = polygon_b.getVelocity (); + + CHECK (velocity_a != velocity_b ); +} + +/** Checks whether we identify the different shapes + * correctly. + */ +TEST ( CheckShapeType ) { + vector3d sphere_position (0., 0., 0.); + + Shape* polygon = new Polygon (0, NULL); + Shape* sphere = new Sphere (0, sphere_position); + + Shape* cast_test = NULL; + + cast_test = dynamic_cast ( sphere ); + CHECK_EQUAL (cast_test, sphere); + cast_test = dynamic_cast ( polygon ); + CHECK (cast_test == NULL ); + cast_test = dynamic_cast (polygon); + CHECK_EQUAL (cast_test, polygon); + cast_test = dynamic_cast (sphere); + CHECK (cast_test == NULL ); + + delete polygon; + delete sphere; +} + +/** Checks whether the right check function is chosen + */ +TEST ( CheckGetCheck ) { + vector3d sphere_position (0., 0., 0.); + + Shape* polygon = new Polygon (0, NULL); + Shape* sphere = new Sphere (0, sphere_position); + + check_cb check = NULL; + + // polygon - sphere -> polygon_sphere + check = get_check (polygon, sphere); + CHECK (static_cast (check) == static_cast (check_collision_polygon_sphere)); + + // sphere - polygon -> polygon_sphere + check = get_check (sphere, polygon); + CHECK (static_cast (check) == static_cast (check_collision_polygon_sphere)); + + // and we do not want that the pointers have changed! + // (as it used to be in previous versions) + CHECK (dynamic_cast (sphere) == NULL); + CHECK (dynamic_cast (polygon) == NULL); + + check = get_check (sphere, sphere); + CHECK (check == check_collision_sphere_sphere); + + delete polygon; + delete sphere; +} + +TEST ( CheckErrorNotImplemented ) { + Shape* polygon = new Polygon (0, NULL); + vector3d velocity (0., 0., 0.); + + CollisionInfo info; + int result = check_collision_rel (1., polygon, polygon, &velocity, &info); + + CHECK (result == CHECK_ERROR_NOT_IMPLEMENTED); + + delete polygon; +} + +TEST ( CheckCalculateContactPlanePoint ) { + vector3d normal (1., 0., 0.); + vector3d plane_point (0., 0., 0.); + vector3d point (1., 0., 0.); + vector3d velocity (-1., 0., 0); + + float result; + + // Directly moving towards the plane + result = calculate_contact_plane_point (normal, plane_point, point, velocity); + CHECK_EQUAL (1, result); + + // Moving with a slight angle onto the plane + velocity.setValues (-1., 0., 0.2); + result = calculate_contact_plane_point (normal, plane_point, point, velocity); + CHECK_EQUAL (1, result); + + velocity.setValues (-1., 0., 0.); + + // Point is "below" the plane and moves even deeper + plane_point.setValues (2., 0., 0); + result = calculate_contact_plane_point (normal, plane_point, point, velocity); + CHECK_EQUAL (-999, result); + + // Point is "below" but moves towards the plane + velocity.setValues (1., 0., 0); + result = calculate_contact_plane_point (normal, plane_point, point, velocity); + CHECK_EQUAL (-111, result); +} + +TEST ( CheckCollisionStepsize ) { + vector3d normal (1., 0., 0.); + vector3d plane_point (0., 0., 0.); + vector3d point (1., 0., 0.); + vector3d velocity (-1., 0., 0); + + float result; + + // Directly moving towards the plane + result = calculate_contact_plane_point (normal, plane_point, point, velocity); + CHECK_EQUAL (1, result); + + // Moving with a slight angle onto the plane + velocity.setValues (-1., 0., 0.2); + result = calculate_contact_plane_point (normal, plane_point, point, velocity); + CHECK_EQUAL (1, result); + + velocity.setValues (-1., 0., 0.); + + // Point is "below" the plane and moves even deeper + plane_point.setValues (2., 0., 0); + result = calculate_contact_plane_point (normal, plane_point, point, velocity); + CHECK_EQUAL (-999, result); + + // Point is "below" but moves towards the plane + velocity.setValues (1., 0., 0); + result = calculate_contact_plane_point (normal, plane_point, point, velocity); + CHECK_EQUAL (-111, result); +} + +/* Here we test, whether the value of info.reference_shape is correct. + * If we swap the shapes passed to check_collision, then the value must swap, + * too. + */ +TEST ( CheckReferenceShapeValue ) { + vector3d vertices[4]; + + vertices[0].setValues (-1., 0., 1.); + vertices[1].setValues (1., 0., 1.); + vertices[2].setValues (1., 0., -1.); + vertices[3].setValues (-1., 0., -1.); + + Shape* polygon = new Polygon (4, vertices); + + // first part of the test, the collision occurs + // on an edge + vector3d sphere_position (2.3, 0., 0.); + Shape* sphere = new Sphere (1, sphere_position); + sphere->setVelocity (vector3d (-1., 0., 0.)); + + CollisionInfo info; + int result = check_collision (1., polygon, sphere, &info); + + CHECK_EQUAL (1, result); + CHECK_EQUAL (0, info.reference_shape); + + result = check_collision (1., sphere, polygon, &info); + + CHECK_EQUAL (1, result); + CHECK_EQUAL (1, info.reference_shape); + + // here the collision occurs on a vertice + sphere->setPosition (vector3d (1.5, 0., -2.5)); + sphere->setVelocity (vector3d (0., 0., 1.)); + + result = check_collision (1., sphere, polygon, &info); + + CHECK_EQUAL (1, result); + CHECK_EQUAL (1, info.reference_shape); + + result = check_collision (1., polygon, sphere, &info); + + CHECK_EQUAL (1, result); + CHECK_EQUAL (0, info.reference_shape); + + delete sphere; + delete polygon; +} + +/* Test whether Polygon::getCopy() does what it should */ +TEST ( CheckPolygonGetCopy ) { + vector3d vertices[4]; + + vertices[0].setValues (-1., 0., 1.); + vertices[1].setValues (1., 0., 1.); + vertices[2].setValues (1., 0., -1.); + vertices[3].setValues (-1., 0., -1.); + + Polygon* polygon = new Polygon (4, vertices); + Polygon* polygon_copy = polygon->getCopy(); + + CHECK (polygon != polygon_copy); + CHECK_EQUAL (polygon->getVerticeCount(), polygon_copy->getVerticeCount()); + + unsigned int i, count; + count = polygon->getVerticeCount (); + + for (i = 0; i < count; i ++) { + vector3d vertice = polygon->getVertice (i); + vector3d vertice_copy = polygon_copy->getVertice (i); + + int j; + for (j = 0; j < 3; j++) { + CHECK_EQUAL (vertice[j], vertice_copy[j]); + } + } + + delete polygon; + delete polygon_copy; +} + +/* Test whether Sphere::getCopy() does what it should */ +TEST ( CheckSphereGetCopy ) { + Sphere* sphere = new Sphere (1.23); + Sphere* sphere_copy = sphere->getCopy(); + + CHECK_EQUAL (sphere->getRadius(), sphere_copy->getRadius()); + CHECK (sphere != sphere_copy); + + delete sphere; + delete sphere_copy; +} + +TEST ( SphereSetGetRadius ) { + Sphere sphere (123.); + CHECK_EQUAL (123, sphere.getRadius()); + + sphere.setRadius (456.); + CHECK_EQUAL (456, sphere.getRadius()); +} + + diff --git a/engine/libraries/coll2d/tests/main.cc b/engine/libraries/coll2d/tests/main.cc new file mode 100644 index 0000000..d37abc3 --- /dev/null +++ b/engine/libraries/coll2d/tests/main.cc @@ -0,0 +1,6 @@ +#include + +int main (int argc, char *argv[]) +{ + return UnitTest::RunAllTests (); +} diff --git a/engine/libraries/coll2d/tests/polygon_sphere.cc b/engine/libraries/coll2d/tests/polygon_sphere.cc new file mode 100644 index 0000000..cb79a5c --- /dev/null +++ b/engine/libraries/coll2d/tests/polygon_sphere.cc @@ -0,0 +1,544 @@ +#include + +#include + +using namespace coll2d; +using namespace std; + +TEST ( CheckErrorInvalidTypes ) { + Shape* polygon = new Polygon (0, NULL); + vector3d velocity (0., 0., 0.); + vector3d sphere_position (2.1, 0., 0.); + Shape* sphere = new Sphere (1, sphere_position); + sphere->setVelocity (vector3d(0., 0., 0.)); + + CollisionInfo info; + int result = check_collision_polygon_sphere (1., sphere, sphere, &info); + CHECK (result == CHECK_ERROR_INVALID_TYPES); + + result = check_collision_polygon_sphere (1., polygon, polygon, &info); + CHECK (result == CHECK_ERROR_INVALID_TYPES); + + delete polygon; + delete sphere; +} + +TEST ( CheckCollisionPolygonSphereNoVelocity ) { + vector3d vertices[4]; + + vertices[0].setValues (-1., 0., -1.); + vertices[1].setValues (1., 0., -1.); + vertices[2].setValues (1., 0., 1.); + vertices[3].setValues (-1., 0., 1.); + + Shape* polygon = new Polygon (4, vertices); + + vector3d sphere_position (2.1, 0., 0.); + Shape* sphere = new Sphere (1, sphere_position); + + vector3d velocity (0., 0., 0.); + + CollisionInfo info; + int result = check_collision_rel (1., polygon, sphere, &velocity, &info); + + CHECK (result >= 0); + + delete polygon; + delete sphere; +} + +TEST ( CheckCollisionPolygonSphereDiverging ) { + vector3d vertices[4]; + + vertices[0].setValues (-1., 0., 1.); + vertices[1].setValues (1., 0., 1.); + vertices[2].setValues (1., 0., -1.); + vertices[3].setValues (-1., 0., -1.); + + Shape* polygon = new Polygon (4, vertices); + + vector3d sphere_position (2.1, 0., 0.); + Shape* sphere = new Sphere (1, sphere_position); + + vector3d velocity (1., 0., 0.); + + CollisionInfo info; + int result = check_collision_rel (1., polygon, sphere, &velocity, &info); + + CHECK (result >= 0); + + delete polygon; + delete sphere; +} + +TEST ( CheckCollisionPolygonSphereContact ) { + vector3d vertices[4]; + + vertices[0].setValues (-1., 0., 1.); + vertices[1].setValues (1., 0., 1.); + vertices[2].setValues (1., 0., -1.); + vertices[3].setValues (-1., 0., -1.); + + Shape* polygon = new Polygon (4, vertices); + + vector3d sphere_position (2., 0., 0.); + Shape* sphere = new Sphere (1, sphere_position); + sphere->setVelocity (vector3d (-1., 0., 0.)); + + CollisionInfo info; + int result = check_collision (1., polygon, sphere, &info); + + CHECK (result > 0); + CHECK_EQUAL (0.0, info.time); + CHECK_EQUAL (1., info.normal[0]); + CHECK_EQUAL (0., info.normal[1]); + CHECK_EQUAL (0., info.normal[2]); + + delete polygon; + delete sphere; +} + +TEST ( CheckCollisionPolygonSphereColliding ) { + vector3d vertices[4]; + + vertices[0].setValues (-1., 0., 1.); + vertices[1].setValues (1., 0., 1.); + vertices[2].setValues (1., 0., -1.); + vertices[3].setValues (-1., 0., -1.); + + Shape* polygon = new Polygon (4, vertices); + + vector3d sphere_position (2.5, 0., 0.1); + Shape* sphere = new Sphere (1, sphere_position); + + vector3d velocity (-1., 0., 0.); + + CollisionInfo info; + int result = check_collision_rel (1., polygon, sphere, &velocity, &info); + + CHECK_EQUAL (1, result); + CHECK_EQUAL (0.5, info.time); + + delete polygon; + delete sphere; +} + +TEST ( CheckCollisionPolygonSphereCollidingTop ) { + vector3d vertices[4]; + + vertices[0].setValues (-1., 0., 1.); + vertices[1].setValues (1., 0., 1.); + vertices[2].setValues (1., 0., -1.); + vertices[3].setValues (-1., 0., -1.); + + Shape* polygon = new Polygon (4, vertices); + + vector3d sphere_position (0., 0., 2.5); + Shape* sphere = new Sphere (1, sphere_position); + + vector3d velocity (0., 0., -1.); + + CollisionInfo info; + int result = check_collision_rel (1., polygon, sphere, &velocity, &info); + + CHECK_EQUAL (1, result); + CHECK_EQUAL (0.5, info.time); + + delete polygon; + delete sphere; +} + +TEST ( CheckCollisionPolygonSphereCollidingNonPerpendicular ) { + vector3d vertices[4]; + + vertices[0].setValues (-1., 0., 1.); + vertices[1].setValues (1., 0., 1.); + vertices[2].setValues (1., 0., -1.); + vertices[3].setValues (-1., 0., -1.); + + Shape* polygon = new Polygon (4, vertices); + + vector3d sphere_position (0., 0., 2.5); + Shape* sphere = new Sphere (1, sphere_position); + + vector3d velocity (0.01, 0., -1.); + + CollisionInfo info; + int result = check_collision_rel (1., polygon, sphere, &velocity, &info); + + CHECK_EQUAL (1, result); + CHECK_EQUAL (0.5, info.time); + + delete polygon; + delete sphere; +} + +TEST ( CheckCollisionPolygonSphereCollidingCorner) { + vector3d vertices[4]; + + vertices[0].setValues (-1., 0., 1.); + vertices[1].setValues (1., 0., 1.); + vertices[2].setValues (1., 0., -1.); + vertices[3].setValues (-1., 0., -1.); + + Shape* polygon = new Polygon (4, vertices); + + vector3d sphere_position ( 1.5, 0., 2.5); + Shape* sphere = new Sphere (1, sphere_position); + + vector3d velocity (0.0, 0., -1.); + + CollisionInfo info; + int result = check_collision_rel (1., polygon, sphere, &velocity, &info); + + CHECK_EQUAL (1, result); + + delete polygon; + delete sphere; +} + +// In this test we make sure, that the z value of both the polygon and the +// sphere get ignored: +TEST ( CheckCollisionPolygonSphereCollidingCornerIgnoreHeight) { + vector3d vertices[4]; + + vertices[0].setValues (-1., 0., 1.); + vertices[1].setValues (1., 0., 1.); + vertices[2].setValues (1., 0., -1.); + vertices[3].setValues (-1., 0., -1.); + + Shape* polygon = new Polygon (4, vertices); + + vector3d sphere_position ( 1.5, 10., 2.5); + Shape* sphere = new Sphere (1, sphere_position); + sphere->setVelocity (vector3d (0., 0., -1.)); + + CollisionInfo info; + int result = check_collision (1., polygon, sphere, &info); + + CHECK_EQUAL (1, result); + + vertices[0].setValues (-1., -10., 1.); + vertices[1].setValues (1., -10., 1.); + vertices[2].setValues (1., -10., -1.); + vertices[3].setValues (-1., -10., -1.); + + sphere->setPosition (vector3d (1.5, 0., 2.5)); + result = check_collision (1., polygon, sphere, &info); + + CHECK_EQUAL (1, result); + + delete polygon; + delete sphere; +} + +TEST ( CheckCollisionPolygonSphereCollidingCornerTop) { + vector3d vertices[4]; + + vertices[0].setValues (-1., 0., 1.); + vertices[1].setValues (1., 0., 1.); + vertices[2].setValues (1., 0., -1.); + vertices[3].setValues (-1., 0., -1.); + + Shape* polygon = new Polygon (4, vertices); + + vector3d sphere_position (1.5, 0., - 2.5); + Shape* sphere = new Sphere (1, sphere_position); + + vector3d velocity (0., 0., 1.); + + CollisionInfo info; + int result = check_collision_rel (1., polygon, sphere, &velocity, &info); + + CHECK_EQUAL (1, result); + + delete polygon; + delete sphere; +} + +TEST ( CheckCollisionPolygonSphereCollidingCornerNeighbour) { + vector3d vertices[4]; + + vertices[0].setValues (-1., 0., 1.); + vertices[1].setValues (1., 0., -0.99); + vertices[2].setValues (1., 0., -1.); + vertices[3].setValues (-1., 0., -1.); + + Shape* polygon = new Polygon (4, vertices); + + vector3d sphere_position (1.5 , 0., - 2.5); + Shape* sphere = new Sphere (1, sphere_position); + + vector3d velocity (0., 0., 1.); + + CollisionInfo info; + check_collision_rel (1., polygon, sphere, &velocity, &info); + + CHECK (info.point == vertices[2]); + + delete polygon; + delete sphere; +} + +TEST ( CheckCollisionPolygonSphereNonCollidingSetPosition ) { + vector3d vertices[4]; + + vertices[0].setValues (-1., 0., 1.); + vertices[1].setValues (1., 0., 1.); + vertices[2].setValues (1., 0., -1.); + vertices[3].setValues (-1., 0., -1.); + + Shape* polygon = new Polygon (4, vertices); + + vector3d sphere_position (2.5, 0., 0.1); + Shape* sphere = new Sphere (1, sphere_position); + sphere->setPosition (vector3d (35, 0, 0)); + vector3d velocity (-1., 0., 0.); + + CollisionInfo info; + int result = check_collision_rel (1., polygon, sphere, &velocity, &info); + + CHECK_EQUAL (0, result); + + delete polygon; + delete sphere; +} + +// In this test we set the velocity of the polygon instead of the +// sphere. +TEST ( CheckCollisionPolygonSphereCollidingPolygonSetVelocity ) { + vector3d vertices[4]; + + vertices[0].setValues (-1., 0., 1.); + vertices[1].setValues (1., 0., 1.); + vertices[2].setValues (1., 0., -1.); + vertices[3].setValues (-1., 0., -1.); + + Shape* polygon = new Polygon (4, vertices); + + vector3d sphere_position (2.5, 0., 0.); + Shape* sphere = new Sphere (1, sphere_position); + + vector3d velocity (1., 0., 0.); + polygon->setVelocity (velocity); + + CollisionInfo info; + int result = check_collision (1., polygon, sphere, &info); + + CHECK_EQUAL (1, result); + CHECK_EQUAL (0.5, info.time); + + delete polygon; + delete sphere; +} + +TEST ( CheckSetGetAngle ) { + Shape* polygon = new Polygon (0, NULL); + + polygon->setAngle (0.1234567); + + CHECK (fabs (0.1234567 - polygon->getAngle ()) < 1.0e-7); + + delete polygon; +} + +TEST ( CheckCollisionPolygonSphereDiamond ) { + vector3d vertices[4]; + float sqrt2 = sqrt (2); + + vertices[0].setValues (-sqrt2, 0., 0.); + vertices[1].setValues (0., 0., sqrt2); + vertices[2].setValues (sqrt2, 0., 0.); + vertices[3].setValues (0., 0., -sqrt2); + + Shape* polygon = new Polygon (4, vertices); + + Shape* sphere = new Sphere (1., vector3d (sqrt2 + 1.5, 0., 0.)); + sphere->setVelocity (vector3d (-1., 0., 0.)); + + CollisionInfo info; + int result = check_collision (1., polygon, sphere, &info); + + CHECK_EQUAL (1, result); + CHECK (fabs (0.5 - info.time) <= 1.0e-6); + CHECK_EQUAL (0, info.reference_shape); + + delete polygon; + delete sphere; +} + +TEST ( CheckPolygonSpherePassingBy ) { + vector3d vertices[4]; + + vertices[0].setValues (-1., 0., 1.); + vertices[1].setValues (1., 0., 1.); + vertices[2].setValues (1., 0., -1.); + vertices[3].setValues (-1., 0., -1.); + + Shape* polygon = new Polygon (4, vertices); + + vector3d sphere_position (1., 0., 2.5); + Shape* sphere = new Sphere (1, sphere_position); + sphere->setVelocity (vector3d (-2., 0., 0.)); + + CollisionInfo info; + int result = check_collision (1., polygon, sphere, &info); + + CHECK_EQUAL (0, result); + + delete sphere; + delete polygon; +} + +TEST ( CheckCollisionPolygonSphereReturnValuePoint ) { + vector3d vertices[4]; + + vertices[0].setValues (-1., 0., 1.); + vertices[1].setValues (1., 0., 1.); + vertices[2].setValues (1., 0., -1.); + vertices[3].setValues (-1., 0., -1.); + + Shape* polygon = new Polygon (4, vertices); + polygon->setPosition (vector3d (sqrt (2), 0., - sqrt (2))); + polygon->setAngle (M_PI * 0.25); + + Shape* sphere = new Sphere (1, vector3d (-sqrt(2) * 0.5, 0., sqrt (2) * 0.5)); + sphere->setVelocity (vector3d (sqrt (2), 0., -sqrt(2))); + + CollisionInfo info; + int result = check_collision (1., polygon, sphere, &info); + + CHECK_EQUAL (1, result); + CHECK ( fabs (0.5 - info.time) < 1.0e-6); + CHECK ( (info.point - vector3d (sqrt (2) * 0.5, 0., -sqrt(2) * 0.5)).length() < 1.0e-6 ); + + delete polygon; + delete sphere; +} + +TEST ( CheckCollisionPolygonSphereReturnValuePointTranslated ) { + vector3d vertices[4]; + + vertices[0].setValues (-1., 0., 1.); + vertices[1].setValues (1., 0., 1.); + vertices[2].setValues (1., 0., -1.); + vertices[3].setValues (-1., 0., -1.); + + Shape* polygon = new Polygon (4, vertices); + polygon->setPosition (vector3d (sqrt (2) + 1., 0., - sqrt (2) - 1.)); + polygon->setAngle (M_PI * 0.25); + + Shape* sphere = new Sphere (1, vector3d (-sqrt(2) * 0.5 + 1., 0., sqrt (2) * 0.5 -1.)); + sphere->setVelocity (vector3d (sqrt (2), 0., -sqrt(2))); + + CollisionInfo info; + int result = check_collision (1., polygon, sphere, &info); + + CHECK_EQUAL (1, result); + CHECK ( fabs (0.5 - info.time) < 1.0e-6); + CHECK ( (info.point - vector3d (sqrt (2) * 0.5 + 1, 0., -sqrt(2) * 0.5 - 1.)).length() < 1.0e-6 ); + + delete polygon; + delete sphere; +} + +TEST ( CheckCollisionPolygonSphereReturnValuePointVerticeTranslated ) { + vector3d vertices[4]; + + vertices[0].setValues (-1., 0., 1.); + vertices[1].setValues (1., 0., 1.); + vertices[2].setValues (1., 0., -1.); + vertices[3].setValues (-1., 0., -1.); + + Shape* polygon = new Polygon (4, vertices); + polygon->setPosition (vector3d (0. + 2., 0., - sqrt (2) - 1.5)); + polygon->setAngle (M_PI * 0.25); + + Shape* sphere = new Sphere (1, vector3d (0. + 2., 0., 0.)); + sphere->setVelocity (vector3d (0. , 0., -1.)); + + CollisionInfo info; + int result = check_collision (1., polygon, sphere, &info); + + CHECK_EQUAL (1, result); + CHECK ( fabs (0.5 - info.time) < 1.0e-6); + CHECK ( (info.point - vector3d (0. + 2., 0., -1.5)).length() < 1.0e-6 ); + + delete polygon; + delete sphere; +} + +TEST ( CheckCollisionPolygonSphereReferenceShapePolygon ) { + vector3d vertices[4]; + + vertices[0].setValues (-1., 0., 1.); + vertices[1].setValues (1., 0., 1.); + vertices[2].setValues (1., 0., -1.); + vertices[3].setValues (-1., 0., -1.); + + Shape* polygon = new Polygon (4, vertices); + polygon->setPosition (vector3d (0., 0., 0.)); + polygon->setVelocity (vector3d (1., 0., 0.)); + + Shape* sphere = new Sphere (1, vector3d (2.5, 0., 0.)); + sphere->setVelocity (vector3d (0., 0., 0.)); + + CollisionInfo info; + int result = check_collision (1., polygon, sphere, &info); + + CHECK_EQUAL (1, result); + CHECK ( fabs (0.5 - info.time) < 1.0e-6); + CHECK_EQUAL ( 0, info.reference_shape ); + + polygon->setPosition (vector3d (0., 0., 0.)); + polygon->setVelocity (vector3d (1., 0., 0.)); + sphere->setPosition (vector3d (2.5, 0., 0.)); + sphere->setVelocity (vector3d (0., 0., 0.)); + + result = check_collision (1., sphere, polygon, &info); + + CHECK_EQUAL (1, result); + CHECK ( fabs (0.5 - info.time) < 1.0e-6); + CHECK_EQUAL ( 1, info.reference_shape ); + + + delete polygon; + delete sphere; +} + +TEST ( CheckCollisionPolygonSphereTimestep ) { + vector3d vertices[4]; + + vertices[0].setValues (-1., 0., 1.); + vertices[1].setValues (1., 0., 1.); + vertices[2].setValues (1., 0., -1.); + vertices[3].setValues (-1., 0., -1.); + + Shape* polygon = new Polygon (4, vertices); + + vector3d sphere_position (2.5, 0., 0.1); + Shape* sphere = new Sphere (1, sphere_position); + + vector3d velocity (-1., 0., 0.); + sphere->setVelocity (velocity); + + CollisionInfo info; + int result; + result = check_collision (1., polygon, sphere, &info); + + CHECK_EQUAL (1, result); + CHECK_EQUAL (0.5, info.time); + + result = check_collision (2., polygon, sphere, &info); + + CHECK_EQUAL (1, result); + CHECK_EQUAL (0.5, info.time); + + result = check_collision (0.499, polygon, sphere, &info); + CHECK_EQUAL (0, result); + + delete polygon; + delete sphere; +} + + diff --git a/engine/libraries/coll2d/tests/sphere_sphere.cc b/engine/libraries/coll2d/tests/sphere_sphere.cc new file mode 100644 index 0000000..2698661 --- /dev/null +++ b/engine/libraries/coll2d/tests/sphere_sphere.cc @@ -0,0 +1,245 @@ +#include + +#include + +using namespace coll2d; +using namespace std; + +TEST ( CheckSphereSphereInvalidTypes ) { + Shape* polygon = new Polygon (0, NULL); + vector3d velocity (0., 0., 0.); + vector3d sphere_position (2.1, 0., 0.); + Shape* sphere = new Sphere (1, sphere_position); + sphere->setVelocity (vector3d(0., 0., 0.)); + + CollisionInfo info; + int result = check_collision_sphere_sphere (1., polygon, sphere, &info); + CHECK (result == CHECK_ERROR_INVALID_TYPES); + + delete polygon; + delete sphere; +} + +TEST ( CheckSphereSphereNoVelocity ) { + Shape* sphere_a = new Sphere (1., vector3d (0., 0., 0.)); + Shape* sphere_b = new Sphere (1, vector3d (4., 0., 0.)); + + CollisionInfo info; + int result = check_collision_sphere_sphere (1., sphere_a, sphere_b, &info); + CHECK_EQUAL (0, result); + + delete sphere_a; + delete sphere_b; +} + +/* This tests whether we report a collision time of 0.0 when two spheres are + * in contact with each other + */ +TEST ( CheckCollisionSphereSphereContact ) { + Shape* sphere_a = new Sphere (1., vector3d (0., 0., 0.)); + Shape* sphere_b = new Sphere (1, vector3d (2., 0., 0.)); + + sphere_b->setVelocity (vector3d(-1., 0., 0.)); + + CollisionInfo info; + int result = check_collision_sphere_sphere (1., sphere_a, sphere_b, &info); + + CHECK (result >= 0); + CHECK_EQUAL (0.0, info.time); + + delete sphere_a; + delete sphere_b; +} + +TEST ( CheckSphereSphereDiverging ) { + Shape* sphere_a = new Sphere (1., vector3d (0., 0., 0.)); + Shape* sphere_b = new Sphere (1, vector3d (4., 0., 0.)); + + sphere_b->setVelocity (vector3d (1., 0., 0.)); + + CollisionInfo info; + int result = check_collision_sphere_sphere (1., sphere_a, sphere_b, &info); + CHECK_EQUAL (0, result); + + delete sphere_a; + delete sphere_b; +} + +TEST ( CheckSphereSphereCollision ) { + Shape* sphere_a = new Sphere (1., vector3d (0., 0., 0.)); + Shape* sphere_b = new Sphere (1, vector3d (3., 0., 0.)); + + sphere_b->setVelocity (vector3d (-1., 0., 0.)); + + CollisionInfo info; + int result = check_collision_sphere_sphere (1., sphere_a, sphere_b, &info); + CHECK_EQUAL (1, result); + + delete sphere_a; + delete sphere_b; +} + +TEST ( CheckSphereSphereCollisionCheckOrder ) { + Shape* sphere_a = new Sphere (1., vector3d (0., 0., 0.)); + Shape* sphere_b = new Sphere (1, vector3d (3., 0., 0.)); + + sphere_a->setVelocity (vector3d (1., 0., 0.)); + sphere_b->setVelocity (vector3d (-1.5, 0., 0.)); + + CollisionInfo info; + int result = check_collision_sphere_sphere (1., sphere_a, sphere_b, &info); + CHECK_EQUAL (1, result); + + vector3d normal = info.normal; + result = check_collision_sphere_sphere (1., sphere_b, sphere_a, &info); + CHECK_EQUAL (1, result); + + CHECK ( normal[0] == -info.normal[0]); + CHECK ( normal[1] == -info.normal[1]); + CHECK ( normal[2] == -info.normal[2]); + + delete sphere_a; + delete sphere_b; +} + +TEST ( CheckSphereSphereNoCollision ) { + Shape* sphere_a = new Sphere (1., vector3d (0., 0., 0.)); + Shape* sphere_b = new Sphere (1, vector3d (3., 0., 0.)); + + sphere_b->setVelocity (vector3d (-0.9, 0., 0.)); + + CollisionInfo info; + int result = check_collision_sphere_sphere (1., sphere_a, sphere_b, &info); + CHECK_EQUAL (0, result); + + delete sphere_a; + delete sphere_b; +} + +TEST ( CheckSphereSphereCollisionResult ) { + Shape* sphere_a = new Sphere (1., vector3d (0., 0., 0.)); + Shape* sphere_b = new Sphere (1, vector3d (2.5, 0., 0.)); + + sphere_b->setVelocity (vector3d (-1, 0., 0.)); + + CollisionInfo info; + int result = check_collision_sphere_sphere (1., sphere_a, sphere_b, &info); + CHECK_EQUAL (1, result); + CHECK_EQUAL (0.5, info.time); + + delete sphere_a; + delete sphere_b; +} + +TEST ( CheckSphereSphereMovingNonHorizontal ) { + Shape* sphere_a = new Sphere (3., vector3d (0., 0., 0.)); + Shape* sphere_b = new Sphere (1.5, vector3d (3., 0., 4.)); + + sphere_a->setVelocity (vector3d (3./5., 0., 4./5.)); + + CollisionInfo info; + int result = check_collision_sphere_sphere (1., sphere_a, sphere_b, &info); + + vector3d result_normal = vector3d (-3., 0., -4.).normalize(); + vector3d collision_point = result_normal * -0.5; + CHECK_EQUAL (1, result); + CHECK_EQUAL (0.5, info.time); + CHECK_EQUAL (1, info.reference_shape); + CHECK (result_normal == info.normal); + CHECK (collision_point == info.point); + + sphere_a->setVelocity (vector3d (0., 0., 0.)); + + sphere_b->setVelocity (vector3d (-3./5., 0., -4./5.)); + result = check_collision_sphere_sphere (1., sphere_a, sphere_b, &info); + CHECK_EQUAL (1, result); + CHECK_EQUAL (0.5, info.time); + + delete sphere_a; + delete sphere_b; +} + +/* If there is one of the sphere non moving we must ensure that we + * set the moving one as the incidence shape. + */ +TEST ( CheckSphereSphereReferenceShape ) { + Shape* sphere_a = new Sphere (3., vector3d (0., 0., 0.)); + Shape* sphere_b = new Sphere (1.5, vector3d (3., 0., 4.)); + + sphere_a->setVelocity (vector3d (3./5., 0., 4./5.)); + + CollisionInfo info; + int result = check_collision_sphere_sphere (1., sphere_a, sphere_b, &info); + + vector3d result_normal = vector3d (-3., 0., -4.).normalize(); + vector3d collision_point = result_normal * 0.5; + CHECK_EQUAL (1, result); + CHECK_EQUAL (0.5, info.time); + CHECK (result_normal == info.normal); + CHECK_EQUAL (1, info.reference_shape); + + sphere_a->setVelocity (vector3d (0., 0., 0.)); + + sphere_b->setVelocity (vector3d (-3./5., 0., -4./5.)); + result = check_collision_sphere_sphere (1., sphere_a, sphere_b, &info); + CHECK_EQUAL (1, result); + CHECK (result_normal * -1. == info.normal); + CHECK_EQUAL (0.5, info.time); + CHECK_EQUAL (0, info.reference_shape); + + delete sphere_a; + delete sphere_b; +} + +TEST ( CheckSphereSphereIgnoreHeight ) { + Shape* sphere_a = new Sphere (1., vector3d (0., 0., 0.)); + Shape* sphere_b = new Sphere (1, vector3d (3., 0., 0.)); + + sphere_b->setVelocity (vector3d (-2., 0., 0.)); + + sphere_a->setPosition (vector3d (0., 10., 0.)); + + CollisionInfo info; + int result = check_collision_sphere_sphere (1., sphere_a, sphere_b, &info); + CHECK_EQUAL (1, result); + + delete sphere_a; + delete sphere_b; +} + +TEST ( CheckSphereSphereCollisionTimestep ) { + Shape* sphere_a = new Sphere (1., vector3d (0., 0., 0.)); + Shape* sphere_b = new Sphere (1, vector3d (3., 0., 0.)); + + sphere_b->setVelocity (vector3d (-1., 0., 0.)); + + CollisionInfo info; + int result = check_collision_sphere_sphere (1., sphere_a, sphere_b, &info); + CHECK_EQUAL (1, result); + CHECK_EQUAL (1, info.time); + + result = check_collision_sphere_sphere (2., sphere_a, sphere_b, &info); + CHECK_EQUAL (1., result); + CHECK_EQUAL (1., info.time); + + result = check_collision_sphere_sphere (0.5, sphere_a, sphere_b, &info); + CHECK_EQUAL (0, result); + + delete sphere_a; + delete sphere_b; +} + +TEST ( CheckPerpendicularMovement ) { + Shape* sphere_a = new Sphere (0.4, vector3d (0., 0., 0.)); + Shape* sphere_b = new Sphere (0.4, vector3d (0., 0., -1.)); + + sphere_a->setVelocity (vector3d (0., 0., 0.)); + sphere_b->setVelocity (vector3d (5., 0., 0.)); + + CollisionInfo info; + int result = check_collision_sphere_sphere (1., sphere_a, sphere_b, &info); + CHECK_EQUAL (0, result); + + delete sphere_a; + delete sphere_b; +} diff --git a/engine/libraries/mathlib/CMakeLists.txt b/engine/libraries/mathlib/CMakeLists.txt new file mode 100644 index 0000000..96ae294 --- /dev/null +++ b/engine/libraries/mathlib/CMakeLists.txt @@ -0,0 +1,10 @@ +PROJECT (MATHLIB CXX) + +LIST ( APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/ ) + +SET ( SRCS + mathlib.cc + main.cc + ) + +ADD_LIBRARY ( mathlib mathlib.cc mathlib.h) diff --git a/engine/libraries/mathlib/mathlib.cc b/engine/libraries/mathlib/mathlib.cc new file mode 100644 index 0000000..0365fd6 --- /dev/null +++ b/engine/libraries/mathlib/mathlib.cc @@ -0,0 +1,224 @@ +#include +#include +#include +#include + +#include "mathlib.h" + +using namespace std; + +#ifdef WIN32 + +void sincos (float rad, double *s, double *c){ + *s = sin (rad); + *c = cos (rad); +} + +#endif + +vector3d::vector3d () +{ + values[0] = 0.; + values[1] = 0.; + values[2] = 0.; +} + +vector3d::vector3d (float u, float v, float w) +{ + values[0] = u; + values[1] = v; + values[2] = w; +} + +vector3d::vector3d (const vector3d &vec) +{ + values[0] = vec.values[0]; + values[1] = vec.values[1]; + values[2] = vec.values[2]; +} + +vector3d vector3d::operator=(const vector3d &vec) { + if (this != &vec) { + values[0] = vec.values[0]; + values[1] = vec.values[1]; + values[2] = vec.values[2]; + } + return *this; +} + +vector3d::~vector3d () +{ +} + +#ifndef MATHLIB_INLINE +vector3d vector3d::operator* (const float &f) +{ + return vector3d (values[0] * f, values[1] * f, values[2] * f); +} + +vector3d vector3d::operator/ (const float &f) +{ + return vector3d (values[0] / f, values[1] / f, values[2] / f); +} + +void vector3d::operator*= (const float &f) +{ + values[0] *= f; + values[1] *= f; + values[2] *= f; +} + +void vector3d::operator/= (const float &f) +{ + values[0] /= f; + values[1] /= f; + values[2] /= f; +} + +float vector3d::operator* (const vector3d &vec) +{ + return values[0] * vec.values[0] + values[1] * vec.values[1] + values[2] * vec.values[2]; +} + +vector3d vector3d::operator+ (const vector3d &vec) +{ + return vector3d (values[0] + vec.values[0], values[1] + vec.values[1], values[2] + vec.values[2]); +} + +vector3d vector3d::operator- (const vector3d &vec) +{ + return vector3d (values[0] - vec.values[0], values[1] - vec.values[1], values[2] - vec.values[2]); +} + +void vector3d::operator+= (const vector3d &vec) +{ + values[0] += vec.values[0]; + values[1] += vec.values[1]; + values[2] += vec.values[2]; +} + +void vector3d::operator-= (const vector3d &vec) +{ + values[0] -= vec.values[0]; + values[1] -= vec.values[1]; + values[2] -= vec.values[2]; +} + +bool vector3d::operator== (const vector3d &vec) +{ + if ( (values[0]==vec.values[0]) && (values[1]==vec.values[1]) && (values[2]==vec.values[2]) ) + return true; + return false; +} + +bool vector3d::operator!= (const vector3d &vec) +{ + return ! (operator==(vec)); +} + +vector3d vector3d::cross (const vector3d &vec) +{ + return vector3d (values[1] * vec.values[2] - values[2] * vec.values[1], + values[2] * vec.values[0] - values[0] * vec.values[2], + values[0] * vec.values[1] - values[1] * vec.values[0]); +} + +float vector3d::length2 () +{ + return values[0]*values[0] + values[1]*values[1] + values[2]*values[2]; +} + +float vector3d::length () +{ + return sqrt (values[0]*values[0] + values[1]*values[1] + values[2]*values[2]); +} +#endif + +vector3d& vector3d::rotate_x (float angle) +{ + vector3d old (*this); + double s,c; + sincos (angle, &s, &c); + + values[0] = old[0]; + values[1] = c * old[1] - s * old[2]; + values[2] = s * old[1] + c * old[2]; + + return *this; +} + +vector3d& vector3d::rotate_y (float angle) +{ + vector3d old (*this); + double s,c; + sincos (angle, &s, &c); + + values[0] = c * old[0] + s * old[2]; + values[1] = old[1]; + values[2] = - s * old[0] + c * old[2]; + + return *this; +} + +vector3d& vector3d::rotate_z (float angle) +{ + vector3d old (*this); + double s,c; + sincos (angle, &s, &c); + + values[0] = c * old[0] + -s * old[1]; + values[1] = s * old[0] + c * old[1]; + values[2] = old[2]; + + return *this; +} + +vector3d& vector3d::normalize () { + float norm = length (); + values [0] /= norm; + values [1] /= norm; + values [2] /= norm; + + return *this; +} + +void vector3d::print () +{ + std::cout << values[0] << " " << values[1] << " " << values[2] << std::endl; +} + +void vector3d::print (const char *str) +{ + std::cout << str << values[0] << " " << values[1] << " " << values[2] << std::endl; +} + +vector3d cross_product (vector3d &a, vector3d &b) { + return a.cross (b); +} + +inline bool point_within (vector3d *a, vector3d *b, vector3d *point); + +vector3d integrate_rk45 (float t, vector3d& ydot, vector3d& y) +{ + vector3d k1 = ydot * t; + vector3d k2 = (ydot + k1 / 2) * t; + vector3d k3 = (ydot + k2 / 2 ) * t; + vector3d k4 = (ydot + k3) * t; + + return y + (k1 + k2 * 2 + k3 * 2 + k4) / 6; +} + +vector3d integrate (float t, vector3d& ydot, vector3d& y) +{ + return integrate_rk45 (t, ydot, y); +} + +int solve_quadratic (float a, float b, float c, float *x1, float *x2) { + if (b*b - 4 * a * c < 0) + return 0; + *x1 = static_cast ((-b + sqrt (b*b - 4 * a * c)) / (2 * a)); + *x2 = static_cast ((-b - sqrt (b*b - 4 * a * c)) / (2 * a)); + + return 1; +} + diff --git a/engine/libraries/mathlib/mathlib.h b/engine/libraries/mathlib/mathlib.h new file mode 100644 index 0000000..fd30375 --- /dev/null +++ b/engine/libraries/mathlib/mathlib.h @@ -0,0 +1,179 @@ +#ifndef __MATHLIB_H +#define __MATHLIB_H + +#include +#include +#include + +#ifdef WIN32 + +#define M_PI 3.14159265 +void sincos (float rad, double *s, double *c); + +#endif + +#define MATHLIB_INLINE + +struct vector3d +{ + float values[3]; + + vector3d (); + vector3d (float u, float v, float w); + vector3d (const vector3d &vec); + vector3d operator= (const vector3d &vec); + ~vector3d (); + + // Setter and getter + inline void setValues (float u, float v, float w) { + values[0] = u; + values[1] = v; + values[2] = w; + } + + inline float& operator[] (const int index) { + assert (index >= 0); + assert (index <= 3); + + return values[index]; + } + + vector3d operator* (const float &f); + vector3d operator/ (const float &f); + void operator*= (const float &f); + void operator/= (const float &f); + + float operator* (const vector3d &vec); + vector3d operator+ (const vector3d &vec); + vector3d operator- (const vector3d &vec); + void operator+= (const vector3d &vec); + void operator-= (const vector3d &vec); + bool operator== (const vector3d &vec); + bool operator!= (const vector3d &vec); + vector3d cross (const vector3d &vec); + + float length2 (); + float length (); + float get_angle () { + return atan2 (values[1], values[0]); + } + + vector3d &rotate_x (float angle); + vector3d &rotate_y (float angle); + vector3d &rotate_z (float angle); + vector3d &normalize (); + + void print (); + void print (const char* str); +}; + +#ifdef MATHLIB_INLINE + +inline +vector3d vector3d::operator* (const float &f) +{ + return vector3d (values[0] * f, values[1] * f, values[2] * f); +} + +inline +vector3d vector3d::operator/ (const float &f) +{ + return vector3d (values[0] / f, values[1] / f, values[2] / f); +} + +inline +void vector3d::operator*= (const float &f) +{ + values[0] *= f; + values[1] *= f; + values[2] *= f; +} + +inline +void vector3d::operator/= (const float &f) +{ + values[0] /= f; + values[1] /= f; + values[2] /= f; +} + +inline +float vector3d::operator* (const vector3d &vec) +{ + return values[0] * vec.values[0] + values[1] * vec.values[1] + values[2] * vec.values[2]; +} + +inline +vector3d vector3d::operator+ (const vector3d &vec) +{ + return vector3d (values[0] + vec.values[0], values[1] + vec.values[1], values[2] + vec.values[2]); +} + +inline +vector3d vector3d::operator- (const vector3d &vec) +{ + return vector3d (values[0] - vec.values[0], values[1] - vec.values[1], values[2] - vec.values[2]); +} + +inline +void vector3d::operator+= (const vector3d &vec) +{ + values[0] += vec.values[0]; + values[1] += vec.values[1]; + values[2] += vec.values[2]; +} + +inline +void vector3d::operator-= (const vector3d &vec) +{ + values[0] -= vec.values[0]; + values[1] -= vec.values[1]; + values[2] -= vec.values[2]; +} + +inline +bool vector3d::operator== (const vector3d &vec) +{ + if ( (values[0]==vec.values[0]) && (values[1]==vec.values[1]) && (values[2]==vec.values[2]) ) + return true; + return false; +} + +inline +bool vector3d::operator!= (const vector3d &vec) +{ + return ! (operator==(vec)); +} + +inline +vector3d vector3d::cross (const vector3d &vec) +{ + return vector3d (values[1] * vec.values[2] - values[2] * vec.values[1], + values[2] * vec.values[0] - values[0] * vec.values[2], + values[0] * vec.values[1] - values[1] * vec.values[0]); +} + +inline +float vector3d::length2 () +{ + return values[0]*values[0] + values[1]*values[1] + values[2]*values[2]; +} + +inline +float vector3d::length () +{ + return sqrt (values[0]*values[0] + values[1]*values[1] + values[2]*values[2]); +} +#endif + +vector3d cross_product (vector3d &a, vector3d &b); + +inline bool point_within (vector3d *a, vector3d *b, vector3d *point); + +vector3d integrate_rk45 (float t,vector3d& ydot, vector3d& y); + +vector3d integrate (float t, vector3d& ydot, vector3d& y); + +int solve_quadratic (float a, float b, float c, float *x1, float *x2); + +#endif /* __MATHLIB_H */ diff --git a/engine/libraries/oglft/AUTHORS b/engine/libraries/oglft/AUTHORS new file mode 100644 index 0000000..2a22791 --- /dev/null +++ b/engine/libraries/oglft/AUTHORS @@ -0,0 +1,5 @@ +Authors of OGLFT + +Allen Barnett +Oliver Bock +Nigel Stewart diff --git a/engine/libraries/oglft/CMake/FindFreeType2.cmake b/engine/libraries/oglft/CMake/FindFreeType2.cmake new file mode 100644 index 0000000..3abf606 --- /dev/null +++ b/engine/libraries/oglft/CMake/FindFreeType2.cmake @@ -0,0 +1,28 @@ +# Tries to find FREETYPE2 (http://freetype2.bespin.org) a simple and fast +# network library by Lee Salzman +# + +SET (FREETYPE2_FOUND FALSE) + +FIND_PATH (FREETYPE2_INCLUDE_DIR freetype/freetype.h /usr/include/ /usr/local/include/ /usr/include/freetype2 /usr/local/include/freetype2 $ENV{FREETYPE2_PATH}/include $ENV{FREETYPE2_INCLUDE_PATH}) + +FIND_LIBRARY (FREETYPE2_LIBRARIES NAMES freetype PATHS /usr/lib /usr/local/lib $ENV{FREETYPE2_PATH} $ENV{FREETYPE2_PATH}/lib ENV{FREETYPE2_LIBRARY_PATH}) + +IF (FREETYPE2_INCLUDE_DIR AND FREETYPE2_LIBRARIES) + SET (FREETYPE2_FOUND TRUE) +ENDIF (FREETYPE2_INCLUDE_DIR AND FREETYPE2_LIBRARIES) + +IF (FREETYPE2_FOUND) + IF (NOT FREETYPE2_FIND_QUIETLY) + MESSAGE(STATUS "Found FREETYPE2: ${FREETYPE2_LIBRARIES}") + ENDIF (NOT FREETYPE2_FIND_QUIETLY) +ELSE (FREETYPE2_FOUND) + IF (FREETYPE2_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find FREETYPE2") + ENDIF (FREETYPE2_FIND_REQUIRED) +ENDIF (FREETYPE2_FOUND) + +MARK_AS_ADVANCED ( + FREETYPE2_INCLUDE_DIR + FREETYPE2_LIBRARIES + ) diff --git a/engine/libraries/oglft/CMakeLists.txt b/engine/libraries/oglft/CMakeLists.txt new file mode 100644 index 0000000..70dc430 --- /dev/null +++ b/engine/libraries/oglft/CMakeLists.txt @@ -0,0 +1,63 @@ +project( OGLFT ) + +CMAKE_MINIMUM_REQUIRED (VERSION 2.6) + +Set( CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMake ) + +FIND_PACKAGE( OpenGL REQUIRED ) +FIND_PACKAGE( FreeType2 REQUIRED ) + +if( ENABLE_QT ) + find_package( Qt REQUIRED ) + if( DESIRED_QT_VERSION EQUAL 3 ) + find_package( KDE3 REQUIRED ) + endif( DESIRED_QT_VERSION EQUAL 3 ) +endif( ENABLE_QT ) + +if( ENABLE_GLE ) + find_package( GLE ) +endif( ENABLE_GLE ) + +# +# Test to determine if we want the "tripledot" (variable) form of the GLU tesselator callback. +# +if(NOT GLU_TESS_CALLBACK_TRIPLEDOT) + if(WIN32 OR CMAKE_SYSTEM_NAME MATCHES "Linux") + # Skip the compile check for platforms that never need the variable form + set(DEFAULT_GLU_TESS_CALLBACK_TRIPLEDOT false) + else(WIN32 OR CMAKE_SYSTEM_NAME MATCHES "Linux") + # For other platforms perform the check + include(CheckCXXSourceCompiles) + include(CheckIncludeFiles) + set(CMAKE_REQUIRED_INCLUDES ${OPENGL_INCLUDE_DIR}) + set(CMAKE_REQUIRED_LIBRARIES ${OPENGL_LIBRARY}) + check_include_files(OpenGL/gl.h HAVE_OPENGL_DIR) + if(HAVE_OPENGL_DIR) + set(OPENGL_DIR_PREFIX "OpenGL") + else(HAVE_OPENGL_DIR) + set(OPENGL_DIR_PREFIX "GL") + endif(HAVE_OPENGL_DIR) + set(GLUTESS_TEST_SOURCE " + #include <${OPENGL_DIR_PREFIX}/gl.h> + #include <${OPENGL_DIR_PREFIX}/glu.h> + typedef GLvoid (*GLUTessCallback)(...); + static void testcb(GLvoid *, void*) { } + int main() { + GLUtesselator *t = gluNewTess(); + gluTessCallback(t, GLU_TESS_VERTEX_DATA, GLUTessCallback(testcb)); + return 0; + } + ") + check_cxx_source_compiles("${GLUTESS_TEST_SOURCE}" GLU_Tesselator_Needs_Variable_Parameter_Callback_Convention_Failure_Means_No) + set(GLU_TESS_CALLBACK_TRIPLEDOT ${GLU_Tesselator_Needs_Variable_Parameter_Callback_Convention_Failure_Means_No}) + endif(WIN32 OR CMAKE_SYSTEM_NAME MATCHES "Linux") +endif(NOT GLU_TESS_CALLBACK_TRIPLEDOT) + +add_subdirectory( liboglft ) + +set( BUILD_OGLFT_TESTS OFF CACHE BOOL "Build oglft tests." ) + +if( BUILD_OGLFT_TESTS ) + enable_testing() + add_subdirectory( tests ) +endif( BUILD_OGLFT_TESTS ) diff --git a/engine/libraries/oglft/COPYING b/engine/libraries/oglft/COPYING new file mode 100644 index 0000000..d60c31a --- /dev/null +++ b/engine/libraries/oglft/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/engine/libraries/oglft/COPYING.LIB b/engine/libraries/oglft/COPYING.LIB new file mode 100644 index 0000000..b1e3f5a --- /dev/null +++ b/engine/libraries/oglft/COPYING.LIB @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/engine/libraries/oglft/ChangeLog b/engine/libraries/oglft/ChangeLog new file mode 100644 index 0000000..c193c52 --- /dev/null +++ b/engine/libraries/oglft/ChangeLog @@ -0,0 +1,432 @@ +2002-07-12 Allen Barnett + + * doc/Doxyfile, configure.ac: Updated to version 0.8. + + * liboglft/OGLFT.cpp: + Removed unnecessary default arguments from constructors. + + * tests/Makefile.am: Added extra demo3 header. + + * liboglft/OGLFT.h: Improved the Doxygen comments. + +2002-07-11 Allen Barnett + + * doc/documentation.dox: Updated to version 0.8 information. + + * NEWS: Added FreeType version warning. + + * liboglft/OGLFT.cpp: Updated to be compilable by G++ 3. + + Minor improvement of Doxygen comments. + + Added height() method. Returns the "line spacing" of the font. + + Added drawing of formatted numbers. + + Made a couple of changes to support newer versions of FreeType: + * A default charmap is not activated if there is no UNICODE map. + * Adjust creation of monochrome bitmap to account for even + number of bytes now in FreeType bitmaps. + + Correctly compute the vector size for each auxiliary face: + Type1 and TrueType fonts typically have different EM sizes. + + Reverted to tesselating straight lines with one step. + + * liboglft/OGLFT.h: Updated to be compilable by G++ 3. + + Minor improvement of Doxygen comments. + + Added height() method. Returns the "line spacing" of the font. + + Added drawing of formatted numbers. + + * tests/tutorial3.cpp: Updated to be compilable by G++ 3. + + * tests/tutorial2.cpp: Updated to be compilable by G++ 3. + + Also, minor modification to optionally (at compile time) use a + scalable representation. And, made it double buffered since that's the + only available visual with the Mach64 driver. + + * tests/tutorial1.cpp, tests/speedtest.cpp, tests/demo.cpp: + Updated to be compilable by G++ 3. + + * tests/demo3.cpp: Updated to be compilable by G++ 3. + + Added example of number formatting. + + * tests/demo2.cpp: Updated to be compilable by G++ 3. + + * tests/Demo3UnicodeExample.h: Added GPL header. + + * tests/Demo3UnicodeExample2.h: Initial checkin. + + * README: Improved Qt project instructions. + + * configure.ac: Updated version number. + + * tests/tests.pro, OGLFT.pro, liboglft/liboglft.pro: Initial checkin. + + * README, NEWS: Added new features for version 0.8. + +2002-07-11 Allen Barnett + + * doc/documentation.dox: Updated to version 0.8 information. + + * NEWS: Added FreeType version warning. + + * liboglft/OGLFT.cpp: Updated to be compilable by G++ 3. + + Minor improvement of Doxygen comments. + + Added height() method. Returns the "line spacing" of the font. + + Added drawing of formatted numbers. + + Made a couple of changes to support newer versions of FreeType: + * A default charmap is not activated if there is no UNICODE map. + * Adjust creation of monochrome bitmap to account for even + number of bytes now in FreeType bitmaps. + + Correctly compute the vector size for each auxiliary face: + Type1 and TrueType fonts typically have different EM sizes. + + Reverted to tesselating straight lines with one step. + + * liboglft/OGLFT.h: Updated to be compilable by G++ 3. + + Minor improvement of Doxygen comments. + + Added height() method. Returns the "line spacing" of the font. + + Added drawing of formatted numbers. + + * tests/tutorial3.cpp: Updated to be compilable by G++ 3. + + * tests/tutorial2.cpp: Updated to be compilable by G++ 3. + + Also, minor modification to optionally (at compile time) use a + scalable representation. And, made it double buffered since that's the + only available visual with the Mach64 driver. + + * tests/tutorial1.cpp, tests/speedtest.cpp, tests/demo.cpp: + Updated to be compilable by G++ 3. + + * tests/demo3.cpp: Updated to be compilable by G++ 3. + + Added example of number formatting. + + * tests/demo2.cpp: Updated to be compilable by G++ 3. + + * tests/Demo3UnicodeExample.h: Added GPL header. + + * tests/Demo3UnicodeExample2.h: Initial checkin. + + * README: Improved Qt project instructions. + + * configure.ac: Updated version number. + + * tests/tests.pro, OGLFT.pro, liboglft/liboglft.pro: Initial checkin. + + * README, NEWS: Added new features for version 0.8. + +2002-03-26 Allen Barnett + + * tests/tutorial2.cpp, tests/tutorial1.cpp: Added . + + * tests/demo3.cpp: + Without GL_RASTER_POSITION_UNCLIPPED_IBM, need to adjust some of the + text items so that they are not clipped. + + * tests/demo.cpp: + Reduced the size of the font since nVidia's OpenGL version does not + support the GL_RASTER_POSITION_UNCLIPPED_IBM. + + * configure.ac: + Note that disabling Qt support also removes UNICODE support. + +2002-03-26 allen@llama.lignumcomputing.org + + * /home/allen/cvsroot/lignumCAD/OGLFT/tests/tutorial2.cpp, /home/allen/cvsroot/lignumCAD/OGLFT/tests/tutorial1.cpp: + Added . + + * /home/allen/cvsroot/lignumCAD/OGLFT/tests/demo3.cpp: + Without GL_RASTER_POSITION_UNCLIPPED_IBM, need to adjust some of the + text items so that they are not clipped. + + * /home/allen/cvsroot/lignumCAD/OGLFT/tests/demo.cpp: + Reduced the size of the font since nVidia's OpenGL version does not + support the GL_RASTER_POSITION_UNCLIPPED_IBM. + + * /home/allen/cvsroot/lignumCAD/OGLFT/configure.ac: + Note that disabling Qt support also removes UNICODE support. + +2002-03-25 allen@llama.lignumcomputing.org + + * /home/allen/cvsroot/lignumCAD/OGLFT/configure.ac: + Update the version number to 0.7. + + * /home/allen/cvsroot/lignumCAD/OGLFT/tests/Makefile.am: + Use the correct name for the embedded font file. + + * /home/allen/cvsroot/lignumCAD/OGLFT/liboglft/OGLFT.cpp: + Updated Doxygen comments. + + * /home/allen/cvsroot/lignumCAD/OGLFT/tests/demo3.cpp: + Use .h for embedded font file to make automake happier. + + * /home/allen/cvsroot/lignumCAD/OGLFT/tests/demo3.cpp, /home/allen/cvsroot/lignumCAD/OGLFT/tests/Demo3UnicodeExample.c, /home/allen/cvsroot/lignumCAD/OGLFT/tests/Demo3UnicodeExample.sfd, /home/allen/cvsroot/lignumCAD/OGLFT/tests/Demo3UnicodeExample.ttf, /home/allen/cvsroot/lignumCAD/OGLFT/tests/tosrc.pl: + Add demo files for UNICODE and multiple fonts. + + * /home/allen/cvsroot/lignumCAD/OGLFT/doc/documentation.dox: + Updated to 0.7. + + * /home/allen/cvsroot/lignumCAD/OGLFT/liboglft/OGLFT.h: + Updated the Doxygen comments. + + * /home/allen/cvsroot/lignumCAD/OGLFT/NEWS: Addes the latest news. + + * /home/allen/cvsroot/lignumCAD/OGLFT/README: + Mention UNICODE rendering dependence on Qt and how to build the test programs. + + * /home/allen/cvsroot/lignumCAD/OGLFT/doc/Doxyfile: + Updated version number. + + * /home/allen/cvsroot/lignumCAD/OGLFT/liboglft/OGLFT.h: + Fix typos in description of vertical justification enum documentation. + + * /home/allen/cvsroot/lignumCAD/OGLFT/configure.ac: + Now, if you have Qt and don't disable it, the library will compile + with UNICODE support, in the form of Qt's QString. Thus, the FLAGS + necessary to compile with Qt are propagated through to all the + Makefiles. + + * /home/allen/cvsroot/lignumCAD/OGLFT/liboglft/OGLFT.h: + * Added the ability to draw glyphs based on UNICODE characters. Here, + we use the Qt UNICODE representation: QString. Additionally, there + are methods to set the foreground and background colors using + QColor. + + * Made the definition of measure(const char*) explicit in each of + Face's immediate subclasses in order to avoid a complaint from + gcc 2.95 about "using measure" from Face. + + * /home/allen/cvsroot/lignumCAD/OGLFT/liboglft/OGLFT.cpp: + * Added the ability to draw glyphs based on UNICODE characters. Here, + we use the Qt UNICODE representation: QString. Additionally, there + are methods to set the foreground and background colors using + QColor. + + * Removed redundant insertion of display list index in the glyph display + list cache. + + * Removed memory leak on deletion of face. Was not freeing the glyph + display lists. + + * Added check for validity of FT_Face in OGLFT::Face subclasses before + proceeding with subclass initiliation. + + * For now at least, tessellation of straight lines is also broken into + segments so that the triangles produced by gluTess* are (slightly) + more regular. + + * /home/allen/cvsroot/lignumCAD/OGLFT/liboglft/OGLFT.h: + Fix issue with compiling under older version (2.95) of GCC g++. + + * /home/allen/cvsroot/lignumCAD/OGLFT/liboglft/OGLFT.cpp: + Add extra case statements to silence compiler warning. + + * /home/allen/cvsroot/lignumCAD/OGLFT/liboglft/OGLFT.h: + Fix bug in BoundingBox auto-increment operator. + +2002-02-05 allen@llama.lignumcomputing.org + + * /home/allen/cvsroot/lignumCAD/OGLFT/doc/documentation.dox: + Update copyright date and SourceForge logo reference. + +2002-02-04 allen@llama.lignumcomputing.org + + * /home/allen/cvsroot/lignumCAD/OGLFT/NEWS, /home/allen/cvsroot/lignumCAD/OGLFT/configure.ac, /home/allen/cvsroot/lignumCAD/OGLFT/doc/Doxyfile: + Update to 0.6. + + * /home/allen/cvsroot/lignumCAD/OGLFT/NEWS: + Comment on "Changes to improve compilation on SGI." + + * /home/allen/cvsroot/lignumCAD/OGLFT/liboglft/OGLFT.h, /home/allen/cvsroot/lignumCAD/OGLFT/liboglft/OGLFT.cpp: + Changes to improve compilation on SGI. + + * /home/allen/cvsroot/lignumCAD/OGLFT/liboglft/OGLFT.h: + Don't try to include mpatrol header if built without mpatrol. + + * /home/allen/cvsroot/lignumCAD/OGLFT/configure.ac: + * Make sure to check for libm first. + + * Do not continue build if proper freetype headers are not found. + + * /home/allen/cvsroot/lignumCAD/OGLFT/tests/demo2.cpp: + stdio.h not included if mpatrol is undefined. + + * /home/allen/cvsroot/lignumCAD/OGLFT/tests/Makefile.am: + Don't include moc_*.cpp files in distribution. + + * /home/allen/cvsroot/lignumCAD/OGLFT/tests/vignette.h, /home/allen/cvsroot/lignumCAD/OGLFT/tests/speedtest.h: + Missing definition of exit() when built in "dist" mode (?) + + * /home/allen/cvsroot/lignumCAD/OGLFT/ChangeLog: + Reran EMACS regeneration command. + + * /home/allen/cvsroot/lignumCAD/OGLFT/NEWS: + Add note about already open FT_Face. + + * /home/allen/cvsroot/lignumCAD/OGLFT/doc/html/classOGLFT_1_1ColorTess.html, /home/allen/cvsroot/lignumCAD/OGLFT/doc/html/classOGLFT_1_1ColorTess-members.html, /home/allen/cvsroot/lignumCAD/OGLFT/doc/html/classOGLFT_1_1TextureTess.html, /home/allen/cvsroot/lignumCAD/OGLFT/doc/html/classOGLFT_1_1TextureTess-members.html: + New structures. + + * /home/allen/cvsroot/lignumCAD/OGLFT/ChangeLog, /home/allen/cvsroot/lignumCAD/OGLFT/Makefile.in, /home/allen/cvsroot/lignumCAD/OGLFT/NEWS: + Updates for version 0.5. + + * /home/allen/cvsroot/lignumCAD/OGLFT/doc/Doxyfile: + Updated version number. + + * /home/allen/cvsroot/lignumCAD/OGLFT/doc/documentation.dox: + Updates for version 0.5. + + * /home/allen/cvsroot/lignumCAD/OGLFT/tests/demo2.cpp: + Demo of using an already open FT_Face. + + * /home/allen/cvsroot/lignumCAD/OGLFT/tests/demo3.cpp: The spiffy demo. + + * /home/allen/cvsroot/lignumCAD/OGLFT/tests/Makefile.am: + Renamed demo2 and demo3. + + * /home/allen/cvsroot/lignumCAD/OGLFT/liboglft/OGLFT.cpp, /home/allen/cvsroot/lignumCAD/OGLFT/liboglft/OGLFT.h: + Add a constructor where the FT_Face is already open. + + * /home/allen/cvsroot/lignumCAD/OGLFT/README: + Updated compilation instructions to better describe configure options. + + * /home/allen/cvsroot/lignumCAD/OGLFT/Makefile.am: + Add target for the bzip distribution. + + * /home/allen/cvsroot/lignumCAD/OGLFT/tests/Makefile.am: + Added demo2 texture.png to distribution. + + * /home/allen/cvsroot/lignumCAD/OGLFT/tests/demo2.cpp: + Run the per glyph display text in the other direction. + +2002-02-04 Allen Barnett + + * NEWS: Comment on "Changes to improve compilation on SGI." + + * liboglft/OGLFT.h, liboglft/OGLFT.cpp: + Changes to improve compilation on SGI. + + * liboglft/OGLFT.h: + Don't try to include mpatrol header if built without mpatrol. + + * configure.ac: * Make sure to check for libm first. + + * Do not continue build if proper freetype headers are not found. + + * tests/demo2.cpp: stdio.h not included if mpatrol is undefined. + + * tests/Makefile.am: Don't include moc_*.cpp files in distribution. + + * tests/vignette.h, tests/speedtest.h: + Missing definition of exit() when built in "dist" mode (?) + + * ChangeLog: Reran EMACS regeneration command. + + * NEWS: Add note about already open FT_Face. + + * doc/Doxyfile: Updated version number. + + * doc/documentation.dox: Updates for version 0.5. + + * tests/demo2.cpp: Demo of using an already open FT_Face. + + * tests/demo3.cpp: The spiffy demo. + + * tests/Makefile.am: Renamed demo2 and demo3. + + * liboglft/OGLFT.cpp, liboglft/OGLFT.h: + Add a constructor where the FT_Face is already open. + + * README: + Updated compilation instructions to better describe configure options. + + * Makefile.am: Add target for the bzip distribution. + + * tests/Makefile.am: Added demo2 texture.png to distribution. + + * tests/demo2.cpp: + Run the per glyph display text in the other direction. + +2002-02-01 Allen Barnett + + * tests/background00a.png, tests/background00b.png, tests/background00.png, tests/background01.png, tests/background10a.png, tests/background10b.png, tests/background10.png, tests/background11.png, tests/background.png: + Background images for demo2. + + * tests/tutorial3.cpp, tests/speedtest.cpp: + Accounted for absence of Solid face. + + * configure.ac: Improved GLE checking. + + * tests/demo2.cpp, tests/demo.cpp: Accounted for absence of Solid face. + + * configure.ac: Added an mpatrol configuration option. + + * liboglft/OGLFT.h: + Added definitions for Color and Texture callbacks during tessellation. + + * liboglft/OGLFT.cpp: * Added color callback for Face::Outline. + + * Added color and texture callbacks for Face::Filled. + + * Properly set the character rotation reference for all face types. + + * Cleaned up memory leak during GLU tesselation. + + * tests/vignette.h, tests/demo2.cpp: Initial checkin + +2002-02-01 Allen Barnett + + * tests/background00a.png, tests/background00b.png, tests/background00.png, tests/background01.png, tests/background10a.png, tests/background10b.png, tests/background10.png, tests/background11.png, tests/background.png: + Background images for demo2. + + * tests/tutorial3.cpp, tests/speedtest.cpp: + Accounted for absence of Solid face. + + * configure.ac: Improved GLE checking. + + * tests/demo2.cpp, tests/demo.cpp: Accounted for absence of Solid face. + + * configure.ac: Added an mpatrol configuration option. + + * liboglft/OGLFT.h: + Added definitions for Color and Texture callbacks during tessellation. + + * liboglft/OGLFT.cpp: * Added color callback for Face::Outline. + + * Added color and texture callbacks for Face::Filled. + + * Properly set the character rotation reference for all face types. + + * Cleaned up memory leak during GLU tesselation. + + * tests/vignette.h, tests/demo2.cpp: Initial checkin + +2002-01-29 Allen Barnett + + * liboglft/OGLFT.cpp: Moved config.h to the main library source . + + * liboglft/Makefile.am: Be sure to install the library header file. + + * liboglft/OGLFT.h: Moved config.h to the main library source. + + * tests/tutorial3.cpp, tests/tutorial2.cpp, tests/tutorial1.cpp, tests/speedtest.cpp: + Added revsion control keyword (Id). + + * tests/speedtest.cpp, tests/speedtest.h: + Reorganized a little to help automake deal with .moc files. + diff --git a/engine/libraries/oglft/INSTALL b/engine/libraries/oglft/INSTALL new file mode 100644 index 0000000..62ea076 --- /dev/null +++ b/engine/libraries/oglft/INSTALL @@ -0,0 +1,231 @@ +Copyright 1994, 1995, 1996, 1999, 2000, 2001 Free Software Foundation, +Inc. + + This file is free documentation; the Free Software Foundation gives +unlimited permission to copy, distribute and modify it. + +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. (Caching is +disabled by default to prevent problems with accidental use of stale +cache files.) + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You only need +`configure.ac' if you want to change it or regenerate `configure' using +a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. Run `./configure --help' +for details on some of the pertinent environment variables. + + You can give `configure' initial values for variables by setting +them in the environment. You can do that on the command line like this: + + ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not support the `VPATH' +variable, you have to compile the package for one architecture at a +time in the source code directory. After you have installed the +package for one architecture, use `make distclean' before reconfiguring +for another architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' cannot figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it cannot guess the host type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are _building_ compiler tools for cross-compiling, you should +use the `--target=TYPE' option to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the host +platform (i.e., that on which the generated programs will eventually be +run) with `--host=TYPE'. In this case, you should also specify the +build platform with `--build=TYPE', because, in this case, it may not +be possible to guess the build platform (it sometimes involves +compiling and running simple test programs, and this can't be done if +the compiler is a cross compiler). + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + + Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +will cause the specified gcc to be used as the C compiler (unless it is +overridden in the site shell script). + +`configure' Invocation +====================== + + `configure' recognizes the following options to control how it +operates. + +`--help' +`-h' + Print a summary of the options to `configure', and exit. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. + diff --git a/engine/libraries/oglft/NEWS b/engine/libraries/oglft/NEWS new file mode 100644 index 0000000..bc5efa7 --- /dev/null +++ b/engine/libraries/oglft/NEWS @@ -0,0 +1,46 @@ +New in OGLFT version 0.8 + + * Added the ability to draw numbers with a format à la printf. + There is also a format, %p, which will draw the number as + a fraction. + + * Updated the source to be compilable with G++ 3. + + * Added an (untested) Qt project for OSs which don't support + autoconf. + + * Note: FreeType versions 2.0.9 through 2.1.2 contain a bug which + prevents rotated text from being rendered correctly. + +New in OGLFT version 0.7 + + * Fixed another memory leak when deleting a Face which had compiled + its glyphs into display lists (the default behavior). + + * Added the ability to render UNICODE glyphs using Qt QStrings. + + * Added the ability to use mulitple FT_Faces in a single OGLFT::Face + so that you can cover more UNICODE points by combining fonts. + +New in OGLFT version 0.6 + + Updates include: + + * Fixed a pervasive memory leak (must call FT_Done_Glyph after each + call to FT_Get_Glyph). + + * measure() now queries the OpenGL view and calls gluUnProject to + compute accurate glyph sizes. + + * Autoconf-enabled. + + * There is a user color callback for drawing the Outline face and + color and texture coordinate callbacks for the Filled face. + + * Can pass an already open FreeType FT_Face to a rendering style. + + * Changes to improve compilation on the SGI. + +;;;Local variables: *** +;;;mode: text *** +;;;End: *** diff --git a/engine/libraries/oglft/OGLFT.h b/engine/libraries/oglft/OGLFT.h new file mode 100644 index 0000000..a8bf6ed --- /dev/null +++ b/engine/libraries/oglft/OGLFT.h @@ -0,0 +1,2321 @@ +// -*- c++ -*- +/* + * OGLFT: A library for drawing text with OpenGL using the FreeType library + * Copyright (C) 2002 lignum Computing, Inc. + * $Id: OGLFT.h.cmake 107 2008-04-25 09:29:24Z brevilo $ + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef OGLFT_H +#define OGLFT_H + +// CMake activates these definitions. +/* #undef ENABLE_QT */ +/* #undef ENABLE_GLE */ +/* #undef GLU_TESS_CALLBACK_TRIPLEDOT */ +/* #undef HAVE_OPENGL_DIR */ +// Convert to our old options. +#if !defined(ENABLE_QT) +#define OGLFT_NO_QT +#endif +#if !defined(ENABLE_GLE) +#define OGLFT_NO_SOLID +#endif + +#include +#include +#include +#include + +#ifdef WIN32 +#include +#endif + +#ifdef HAVE_OPENGL_DIR +#include +#include +#else +#include +#include +#endif + +#ifndef OGLFT_NO_SOLID +#ifdef HAVE_OPENGL_DIR +#include +#else +#include +#endif +#endif + +#ifndef OGLFT_NO_QT +#define OGLFT_QT_VERSION +#if OGLFT_QT_VERSION == 3 +#include +#include +#elif OGLFT_QT_VERSION == 4 +#include +#include +#endif +#endif + +#include +#include FT_FREETYPE_H +#include FT_GLYPH_H +#include FT_OUTLINE_H +#include FT_TRIGONOMETRY_H + +#ifdef _MSC_VER +#ifdef OGLFT_BUILD +#define OGLFT_API __declspec(dllexport) +#else +#define OGLFT_API __declspec(dllimport) +#endif +#else +#define OGLFT_API +#endif + +//! All of OGLFT C++ objects are in this namespace. + +namespace OGLFT { + + //! Thanks to DesCartes, I'd consider these manifest constants. + enum Coordinates { + X, //!< The X component of space + Y, //!< The Y component of space + Z, //!< The Z component of space + W //!< The projection component of space + }; + + //! Who to credit? Newton? I'd consider these manifest constants. + enum ColorSpace { + R, //!< The Red component of a color + G, //!< The Green component of a color + B, //!< The Blue component of a color + A, //!< The Alpha (or transparency) of a color + }; + + //! Callback from GLU tessellation routines. +#ifdef GLU_TESS_CALLBACK_TRIPLEDOT + typedef GLvoid (*GLUTessCallback)(...); +#else + typedef GLvoid (*GLUTessCallback)(); +#endif + + //! The FreeType library instance. + /*! + * The FreeType library has a single, global instance of a library + * handle. This reference is used to load font faces. This detail + * is generally hidden from the user of OGLFT, however, it + * can be useful to get the FT_Library instance if you want to open + * a font file yourself, either from disk or embedded in the program. + */ + class Library { + public: + /*! + * The FreeType library's library handle is only available through this + * accessor method. + * \return the global OGLFT FreeType library handle. + */ + static OGLFT_API FT_Library& instance ( void ); + + protected: + /*! + * The constructor for this class is automatically called when + * this library is loaded. Access the instance through the instance() + * method. + */ + OGLFT_API Library ( void ); + /*! + * This destructor is automatically called when the program exits. + */ + OGLFT_API ~Library( void ); + + private: + static Library library; + static FT_Library library_; + }; + + //! Advance describes the "advance" of a glyph, namely the distance in + //! model space at which the NEXT glyph should be drawn. This class exists + //! to assist the computation of string metrics. + struct OGLFT_API Advance { + float dx_; //!< Advance increment in the X direction. + float dy_; //!< Advance increment in the Y direction. + + //! Default constructor. An otherwise uninitialized Advance contains zeros. + Advance ( float dx = 0, float dy = 0 ) : dx_( dx ), dy_( dy ) + {} + + //! Initialize an advance from a FreeType advance member. + Advance ( FT_Vector v ) + { + dx_ = v.x / 64.f; + dy_ = v.y / 64.f; + } + + //! Increment Advance with a FreeType advance member. + //! \return a reference to oneself. + Advance& operator+= ( const FT_Vector v ) + { + dx_ += v.x / 64.f; + dy_ += v.y / 64.f; + return *this; + } + }; + + //! Describe the metrics of a glyph or string relative to the origin + //! of the first character + struct OGLFT_API BBox { + float x_min_; //!< The left-most position at which "ink" appears. + float y_min_; //!< the bottom-most position at which "ink" appears. + float x_max_; //!< The right-most position at which "ink" appears. + float y_max_; //!< The top-most position at which "ink" appears. + Advance advance_; //!< The (total) advancement + + //! Default constructor is all zeros. + BBox () : x_min_( 0 ), y_min_( 0 ), x_max_( 0 ), y_max_( 0 ) + {} + + /*! + *(Partially) initialize a BBox from a FreeType bounding box member. + *(The advancement is initialized to zero by its default constructor). + * \param ft_bbox a FreeType bounding box as retrieved from + * \c FT_Glyph_Get_CBox. + */ + BBox ( FT_BBox ft_bbox ) + { + x_min_ = ft_bbox.xMin / 64.f; + y_min_ = ft_bbox.yMin / 64.f; + x_max_ = ft_bbox.xMax / 64.f; + y_max_ = ft_bbox.yMax / 64.f; + } + + //! Scale the bounding box by a constant. + //! \param k a constant to scale the bounding box by. + //! \return a reference to oneself. + BBox& operator*= ( float k ) + { + x_min_ *= k; + y_min_ *= k; + x_max_ *= k; + y_max_ *= k; + advance_.dx_ *= k; + advance_.dy_ *= k; + + return *this; + } + + /*! + * Merge a bounding box into the current one (not really addition). + * Each time a BBox is "added", the current BBox is expanded to include + * the metrics of the new BBox. May only work for horizontal fonts, though. + * \param b the bounding box to merge. + * \return a reference to oneself. + */ + BBox& operator+= ( const BBox& b ) + { + float new_value; + + new_value = b.x_min_ + advance_.dx_; + if ( new_value < x_min_ ) x_min_ = new_value; + + new_value = b.y_min_ + advance_.dy_; + if ( new_value < y_min_ ) y_min_ = new_value; + + new_value = b.x_max_ + advance_.dx_; + if ( new_value > x_max_ ) x_max_ = new_value; + + new_value = b.y_max_ + advance_.dy_; + if ( new_value > y_max_ ) y_max_ = new_value; + + advance_.dx_ += b.advance_.dx_; + advance_.dy_ += b.advance_.dy_; + + return *this; + } + }; + + //! During tesselation of a polygonal Face (outline, filled or solid), + //! an object which implements this interface can be used to compute a + //! different color for each vertex. + class OGLFT_API ColorTess { + public: + virtual ~ColorTess ( void ) {} + //! Compute a color for this position. Note that the position is + //! in the glyph's local coordinate system. + //! \param p vertex position in glyph's local coordinate system. Argument is + //! a GLdouble[3]. + //! \return GLfloat[4] (RGBA) color specification. + virtual GLfloat* color ( GLdouble* p ) = 0; + }; + + //! During tesselation of a polygonal Face (outline, filled or solid), + //! an object which implements this interface can be used to compute a + //! different texture coordinate for each vertex. + class OGLFT_API TextureTess { + public: + virtual ~TextureTess ( void ) {} + //! Compute a texture coordinate for this position. Note that the + //! position is in the glyph's local coordinate system. + //! \param p vertex position in glyph's local coordinate system. Argument is + //! a GLdouble[3]. + //! \return GLfloat[2] (s,t) texture coordinates. + virtual GLfloat* texCoord ( GLdouble* p ) = 0; + }; + + //! The argument to setCharacterDisplayLists is an STL vector of + //! OpenGL display list names (GLuints). + typedef std::vector DisplayLists; + + //! A convenience definition of an iterator for display list vectors. + typedef DisplayLists::const_iterator DLCI; + + //! A convenience definition of an iterator for display list vectors. + typedef DisplayLists::iterator DLI; + + //! A face (aka font) used to render text with OpenGL. + /*! + * This is an abstract class, but it does define most the functions that + * you are likely to call to manipulate the rendering of the text. + */ + class Face { + public: + //! Thanks to the standard formerly known as PHIGS. Horizontal text + //! justification constants. + enum OGLFT_API HorizontalJustification { + LEFT, //!< Left justified justification of text + ORIGIN, //!< Natural origin alignment of text (default) + CENTER, //!< Center justified alignment of text + RIGHT //!< Right justified alignment of text + }; + + //! Thanks to the standard formerly known as PHIGS. Vertical text + //! justification constants. + enum OGLFT_API VerticalJustification { + BOTTOM, //!< Descender alignment of text + BASELINE, //!< Baseline alignment of text (default) + MIDDLE, //!< Centered alignment of text + TOP //!< Ascender justification of text + }; + + //! Control how OpenGL display lists are created for individual glyphs. + //! The default mode is to create display lists for each glyph as it + //! is requested. Therefore, the Face drawing routines cannot themselves + //! be called from within an open display list. In IMMEDIATE mode, + //! cached glyphs will be drawn if available, otherwise the FreeType + //! data for a glyph is re-rendered each time. + enum OGLFT_API GlyphCompileMode { + COMPILE, //!< Compile new glyphs when seen for the first time. + IMMEDIATE //!< Do not \em create display lists for glyphs. + }; + + private: + //! We allow a Face to be constructed either from a file name + //! or passed in as an already opened FreeType FT_Face. In the case + //! of the later (already opened), we don't close the FT_Face on + //! destruction. This way you can share FT_Faces between related + //! OGLFT faces. Also, we're experimenting with being able to use + //! multiple FT_Faces in a single OGLFT Face, so this is represented + //! as a data structure. + struct FaceData { + FT_Face face_; + bool free_on_exit_; + FaceData ( FT_Face face, bool free_on_exit = true ) + : face_( face ), free_on_exit_( free_on_exit ) + {} + }; + protected: + //! The FreeType face - experimentally, this is now an array of + //! faces so that we can handle a wider range of UNICODE points + //! in case a face doesn't cover the points of interest. + std::vector< FaceData > faces_; + + //! Did a font load OK? + bool valid_; + + //! Glyph display list creation mode. + enum GlyphCompileMode compile_mode_; + + //! Nominal point size. + float point_size_; + + //! Display resolution in pixels per inch. + FT_UInt resolution_; + + //! Does rendering text affect the MODELVIEW matrix? + bool advance_; + + //! Foreground color (I really wanted to avoid this, but not really + //! possible without state queries, which you can't put into + //! display lists. Anyway, you'll be able to get even more fancy + //! by passing in a function to map the color with, so why balk at + //! this?) + GLfloat foreground_color_[4]; + + //! Background color (what modes would use this?) + GLfloat background_color_[4]; + + //! PHIGS-like horizontal positioning of text. + enum HorizontalJustification horizontal_justification_; + + //! PHIGS-like vertical positioning of text. + enum VerticalJustification vertical_justification_; + + //! Rotate an entire string in the Z plane + GLfloat string_rotation_; + + //! Let the user decide which character to use as the rotation reference. + //! Use "o" by default, I suppose. + FT_UInt rotation_reference_glyph_; + + //! The rotation reference character could be in any face. + FT_Face rotation_reference_face_; + + //! These are the translation offsets provided by the rotation reference + //! character; for whom, we've discovered, only the Y position is relevant. + GLfloat rotation_offset_y_; + + //! Type of the cache of defined glyph to display list mapping. + typedef std::map< FT_UInt, GLuint > GlyphDLists; + + //! A convenience definition of the iterator over the glyph to display + //! list map. + typedef GlyphDLists::const_iterator GDLCI; + + //! A convenience definition of the iterator over the glyph to display + //! list map. + typedef GlyphDLists::iterator GDLI; + + //! Cache of defined glyph display lists + GlyphDLists glyph_dlists_; + + //! The user can supply an array of display list which are invoked + //! before each glyph is rendered. + DisplayLists character_display_lists_; + + public: + /*! + * Construct a Face by loading a font from the given file. + * \param filename the filename which contains the font face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + Face ( const char* filename, float point_size = 12, FT_UInt resolution = 100 ); + + /*! + * Construct a Face by loading a font from the given memory location. + * \param data_base the memory location (base pointer) which contains the font face. + * \param data_size the size (in bytes) of the font data found at \ref data_base. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + Face ( const FT_Byte* data_base, const FT_Long data_size, + float point_size = 12, FT_UInt resolution = 100 ); + + /*! + * Alternatively, the user may have already opened a face and just + * wants to draw with it. This is useful for Multiple Master fonts or + * combining multiple files to increase UNICODE point coverage. + * \param face open Freetype FT_Face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + Face ( FT_Face face, float point_size = 12, FT_UInt resolution = 100 ); + + /*! + * Deleting a Face frees its FreeType face (and anything else it's + * styles have allocated). + */ + virtual ~Face ( void ); + + /*! + * Let the user test to see if the font was loaded OK. + * \return true if the FT_Face was successfully created. + */ + bool isValid ( void ) const { return valid_; } + + /*! + * Add another FT_Face to the OGLFT Face. Generally used to add more + * coverage of UNICODE points (at least that's the plan). This + * routine takes a filename and takes ownership of the FT_Face. + * \param filename name of file containing font face data. + * \return true if face was successfully added. + */ + bool addAuxiliaryFace ( const char* filename ); + + /*! + * Add another FT_Face to the OGLFT Face. Generally used to add more + * coverage of UNICODE points (at least that's the plan). This + * routine takes a memory location and takes ownership of the FT_Face. + * \param data_base the memory location (base pointer) which contains the font face. + * \param data_size the size (in bytes) of the font data found at \ref data_base. + * \return true if face was successfully added. + */ + bool addAuxiliaryFace ( const FT_Byte* data_base, const FT_Long data_size ); + + /*! + * Add another FT_Face to the OGLFT Face. Generally used to add more + * coverage of UNICODE points (at least that's the plan). This + * routine takes an already open FT_Face. The user is responsible + * for clean up. + * \param face open FreeType FT_Face + * \return true if face was successfully added. + */ + bool addAuxiliaryFace ( FT_Face face ); + + /*! + * By default, each time a new character is seen, its glyph is rendered + * into a display list. This means that a display list cannot already + * be open (since OpenGL doesn't allow nested display list creation). + * Rendering can be set into immediate mode in which case glyphs are + * rendered from display lists if available, but are otherwise generated + * anew each time. + * \param compile_mode the new compile mode. + */ + void setCompileMode ( enum GlyphCompileMode compile_mode ) + { + compile_mode_ = compile_mode; + } + + /*! + * \return the current glyph compile mode. + */ + enum GlyphCompileMode compileMode ( void ) const { return compile_mode_; } + + /*! + * For the rasterized styles (Monochrome, Grayscale, Translucent, Texture), + * glyphs are rendered at the pixel size given by: + * + * point_size [pts] * / 72 [pts/in] * resolution [dots/in] = [dots]. + * + * For the polygon styles (Outline, Filled, Solid), the "nominal" size of + * the glyphs is: + * + * point_size[pts] / 72 [pts/in] * resolution [dots/in] + * / units_per_EM [font unit/EM] = [dots * EM]. + * + * If the MODELVIEW and PROJECTION matrices are such that one screen pixel + * corresponds to one modeling unit, then polygonal Faces will + * be the same size as raster Faces. + * + * Note that changing the point size after Face creation will invalidate + * the cache of OpenGL display lists and any other information which + * the individual styles have cached. + * \param point_size the new point size in points (1/72-th inch). + */ + void setPointSize ( float point_size ); + + /*! + * \return the current point size. + */ + float pointSize ( void ) { return point_size_; } + + /*! + * For the rasterized styles (Monochrome, Grayscale, + * Translucent, Texture), the exact rendered size of the glyphs depends on + * the resolution of the display (as opposed to the polygon styles + * whose size is controlled by the viewing matrices). The Texture + * style is slightly different because the glyphs are texture-mapped + * onto an arbitrary rectangle; here, the resolution only controls + * how accurately the glyph is rendered. + * \param resolution the resolution in DPI (dots per inch). + */ + void setResolution ( FT_UInt resolution ); + + /*! + * \return the current raster resolution. + */ + FT_UInt resolution ( void ) { return resolution_; } + + /*! + * If advance is true, then the changes made to the MODELVIEW matrix + * to render a string are allowed to remain. Otherwise, the library + * pushes the current MODELVIEW matrix onto the matrix stack, renders + * the string and then pops it off again. Rendering a character always + * modifies the MODELVIEW matrix. + * \param advance whether or not the MODELVIEW matrix should be left + * translated by the advancement of a rendered string. + */ + void setAdvance ( bool advance ) { advance_ = advance; } + + /*! + * \return the advance value. + */ + bool advance ( void ) const { return advance_; } + + /*! + * This is the nominal color of the glyphs. A lot of other things + * can alter what you actually see! Note that changing the foreground + * color invalidates the glyph cache. + * \param red the red component of the foreground color. + * \param green the green component of the foreground color. + * \param blue the blue component of the foreground color. + * \param alpha the alpha component of the foreground color. + */ + void setForegroundColor ( GLfloat red = 0.0, + GLfloat green = 0.0, + GLfloat blue = 0.0, + GLfloat alpha = 1.0 ); + + /*! + * This is the nominal color of the glyphs. A lot of other things + * can alter what you actually see! Note that changing the foreground + * color invalidates the glyph cache. + * \param foreground_color an array of 4 values corresponding to the + * red, green, blue and alpha components of the foreground color. + */ + void setForegroundColor ( const GLfloat foreground_color[4] ); +#ifndef OGLFT_NO_QT + /*! + * This is the nominal color of the glyphs. A lot of other things + * can alter what you actually see! Note that changing the foreground + * color invalidates the glyph cache. + * \param foreground_color the foreground color as an unsigned int. + */ + void setForegroundColor ( const QRgb foreground_color ); +#endif /* OGLFT_NO_QT */ + /*! + * \return the red component of the foreground color + */ + GLfloat foregroundRed ( void ) const { return foreground_color_[R]; } + /*! + * \return the green component of the foreground color + */ + GLfloat foregroundGreen ( void ) const { return foreground_color_[G]; } + /*! + * \return the blue component of the foreground color + */ + GLfloat foregroundBlue ( void ) const { return foreground_color_[B]; } + /*! + * \return the alpha component of the foreground color + */ + GLfloat foregroundAlpha ( void ) const { return foreground_color_[A]; } + + /*! + * This is the nominal background color of the glyphs. A lot of other things + * can alter what you actually see! Note that changing the background + * color invalidates the glyph cache. + * \param red the red component of the background color. + * \param green the green component of the background color. + * \param blue the blue component of the background color. + * \param alpha the alpha component of the background color. + */ + void setBackgroundColor ( GLfloat red = 1.0, + GLfloat green = 1.0, + GLfloat blue = 1.0, + GLfloat alpha = 0.0 ); + + /*! + * This is the nominal background color of the glyphs. A lot of other things + * can alter what you actually see! Note that changing the background + * color invalidates the glyph cache. + * \param background_color an array of 4 values corresponding to the + * red, green, blue and alpha components of the background color. + */ + void setBackgroundColor ( const GLfloat background_color[4] ); +#ifndef OGLFT_NO_QT + /*! + * This is the nominal background color of the glyphs. A lot of other things + * can alter what you actually see! Note that changing the background + * color invalidates the glyph cache. + * \param background_color the background color as an unsigned int. + */ + void setBackgroundColor ( const QRgb background_color ); +#endif /* OGLFT_NO_QT */ + /*! + * \return the red component of the background color + */ + GLfloat backgroundRed ( void ) const { return background_color_[R]; } + /*! + * \return the green component of the background color + */ + GLfloat backgroundGreen ( void ) const { return background_color_[G]; } + /*! + * \return the blue component of the background color + */ + GLfloat backgroundBlue ( void ) const { return background_color_[B]; } + /*! + * \return the alpha component of the background color + */ + GLfloat backgroundAlpha ( void ) const { return background_color_[A]; } + + /*! + * Set the individual character rotation in the Z direction. + * \param character_rotation_z angle in degrees of z rotation. + */ + virtual void setCharacterRotationZ ( GLfloat character_rotation_z ) = 0; + + /*! + * \return the character rotation in the Z direction. + */ + virtual GLfloat characterRotationZ ( void ) const = 0; + + /*! + * The z rotation angle needs a center. Nominate a character whose + * center is to be the center of rotation. By default, use "o". + * \param c rotation reference character. + */ + void setCharacterRotationReference ( unsigned char c ); + + /*! + * Rotate an entire string through the given angle (in the Z plane only). + * (Somewhat pointless for the vector styles since you can do mostly + * the same thing with the MODELVIEW transform, however, for what its + * worth, this routine uses the FreeType rotation function to compute + * the "proper" metrics for glyph advance.) + * \param string_rotation angle in degrees of z rotation. + */ + void setStringRotation ( GLfloat string_rotation ); + + /*! + * \return the (Z plane) string rotation angle. + */ + GLfloat stringRotation ( void ) const { return string_rotation_; } + + /*! + * Set the horizontal justification. + * \param horizontal_justification the new horizontal justification. + */ + void setHorizontalJustification ( enum HorizontalJustification + horizontal_justification ) + { + horizontal_justification_ = horizontal_justification; + } + + /*! + * \return the horizontal justification. + */ + enum HorizontalJustification horizontalJustification ( void ) const + { return horizontal_justification_; } + + /*! + * Set the vertical justification. + * \param vertical_justification the new vertical justification + */ + void setVerticalJustification ( enum VerticalJustification + vertical_justification ) + { + vertical_justification_ = vertical_justification; + } + + /*! + * \return the vertical justification. + */ + enum VerticalJustification verticaljustification ( void ) + const { return vertical_justification_; } + + /*! + * Specify an OpenGL display list to be invoked before + * each character in a string. Face makes a copy of the argument. Pass + * an empty DisplayLists to disable this feature. + * \param character_display_lists STL vector containing a display + * list to invoke before each glyph in a string is drawn. + */ + void setCharacterDisplayLists ( const DisplayLists& character_display_lists ) + { + character_display_lists_ = character_display_lists; + } + + /*! + * \return a reference to the array of character display lists. This is + * the live list as stored in the Face. + */ + DisplayLists& characterDisplayLists ( void ) + { return character_display_lists_; } + + /*! + * \return the height (i.e., line spacing) at the current character size. + */ + virtual double height ( void ) const = 0; + + /*! + * Compute the bounding box info for a character. + * \param c the (latin1) character to measure. + * \return the bounding box of c. + */ + virtual BBox measure ( unsigned char c ) = 0; +#ifndef OGLFT_NO_QT + /*! + * Compute the bounding box info for a character. + * \param c the (UNICODE) character to measure. + * \return the bounding box of c. + */ + virtual BBox measure ( const QChar c ) = 0; +#endif /* OGLFT_NO_QT */ + /*! + * Compute the bounding box info for a string. + * \param s the (latin1) string to measure. + * \return the bounding box of s. + */ + virtual BBox measure ( const char* s ); + /*! + * Compute the bounding box info for a string without conversion + * to modeling coordinates. + * \param s the (latin1) string to measure. + * \return the bounding box of s. + */ + virtual BBox measureRaw ( const char* s ); +#ifndef OGLFT_NO_QT + /*! + * Compute the bounding box info for a string. + * \param s the (UNICODE) string to measure. + * \return the bounding box of s. + */ + virtual BBox measure ( const QString& s ); + /*! + * Compute the bounding box info for a real number formatted as specified. + * \param format (see draw for valid formats) + * \param number real number. + * \return the bounding box of the formatted number. + */ + virtual BBox measure ( const QString& format, double number ); + /*! + * Compute the bounding box info for a string without conversion + * to modeling coordinates. + * \param s the (UNICODE) string to measure. + * \return the bounding box of s. + */ + virtual BBox measureRaw ( const QString& s ); +#endif /* OGLFT_NO_QT */ + /*! + * Compile a string into an OpenGL display list for later + * rendering. Essentially, the string is rendered at the origin + * of the current MODELVIEW. Note: no other display lists should + * be open when this routine is called. Also, the Face does not + * keep track of these lists, so you must delete them in order + * to recover the memory. + * \param s the (latin1) string to compile. + * \return the display list name for the string. + */ + GLuint compile ( const char* s ); +#ifndef OGLFT_NO_QT + /*! + * Compile a string into an OpenGL display list for later + * rendering. Essentially, the string is rendered at the origin + * of the current MODELVIEW. Note: no other display lists should + * be open when this routine is called. Also, the Face does not + * keep track of these lists, so you must delete them in order + * to recover the memory. + * \param s the (UNICODE) string to compile. + * \return the display list name for the string. + */ + GLuint compile ( const QString& s ); +#endif /* OGLFT_NO_QT */ + /*! + * Compile a single character (glyph) into an OpenGL display list + * for later rendering. The Face \em does keep track of these + * display lists, so do not delete them. + * \param c the (latin1) character to compile. + * \return the display list name for the character. + */ + GLuint compile ( unsigned char c ); +#ifndef OGLFT_NO_QT + /*! + * Compile a single character (glyph) into an OpenGL display list + * for later rendering. The Face \em does keep track of these + * display lists, so do not delete them. + * \param c the (UNICODE) character to compile. + * \return the display list name for the character. + */ + GLuint compile ( const QChar c ); +#endif /* OGLFT_NO_QT */ + /*! + * Draw a (latin1) string using the current MODELVIEW matrix. If + * advance is true, then the final glyph advance changes to the + * MODELVIEW matrix are left in place. + * \param s the (latin1) string to draw. + */ + void draw ( const char* s ); +#ifndef OGLFT_NO_QT + /*! + * Draw a (UNICODE) string using the current MODELVIEW + * matrix. If advance is true, then the final glyph advance + * changes to the MODELVIEW matrix are left in place. + * \param s the (UNICODE) string to draw. + */ + void draw ( const QString& s ); +#endif /* OGLFT_NO_QT */ + /*! + * Draw the character using the current MODELVIEW matrix. Note that + * the MODELVIEW matrix is modified by the glyph advance. Draw a + * string if you don't want the MODELVIEW matrix changed. + * \param c the (latin1) character to draw. + */ + void draw ( unsigned char c ); + +#ifndef OGLFT_NO_QT + /*! + * Draw the character using the current MODELVIEW matrix. Note that + * the MODELVIEW matrix is modified by the glyph advance. Draw a + * string if you don't want the MODELVIEW matrix changed. + * \param c the (UNICODE) character to draw. + */ + void draw ( const QChar c ); +#endif /* OGLFT_NO_QT */ + /*! + * Draw the (latin1) character at the given 2D point. Note that + * the MODELVIEW matrix is modified by the glyph advance. Draw + * a string if you don't want the MODELVIEW matrix changed. + * \param x the X position. + * \param y the Y position. + * \param c the (latin1) character to draw. + */ + void draw ( GLfloat x, GLfloat y, unsigned char c ); + /*! + * Draw the (latin1) character at the given 3D point. Note that + * the MODELVIEW matrix is modified by the glyph advance. Draw + * a string if you don't want the MODELVIEW matrix changed. + * \param x the X position. + * \param y the Y position. + * \param z the Z position. + * \param c the (latin1) character to draw. + */ + void draw ( GLfloat x, GLfloat y, GLfloat z, unsigned char c ); +#ifndef OGLFT_NO_QT + /*! + * Draw the (UNICODE) character at the given 2D point. Note that + * the MODELVIEW matrix is modified by the glyph advance. Draw + * a string if you don't want the MODELVIEW matrix changed. + * \param x the X position. + * \param y the Y position. + * \param c the (UNICODE) character to draw. + */ + void draw ( GLfloat x, GLfloat y, QChar c ); + /*! + * Draw the (UNICODE) character at the given 3D point. Note that + * the MODELVIEW matrix is modified by the glyph advance. Draw + * a string if you don't want the MODELVIEW matrix changed. + * \param x the X position. + * \param y the Y position. + * \param z the Z position. + * \param c the (UNICODE) character to draw. + */ + void draw ( GLfloat x, GLfloat y, GLfloat z, QChar c ); +#endif /* OGLFT_NO_QT */ + /*! + * Draw a string at the given 2D point. + * \param x the X position. + * \param y the Y position. + * \param s the (latin1) string to draw. + */ + void draw ( GLfloat x, GLfloat y, const char* s ); + /*! + * Draw a string at the given 3D point. + * \param x the X position. + * \param y the Y position. + * \param z the Z position. + * \param s the (latin1) string to draw. + */ + void draw ( GLfloat x, GLfloat y, GLfloat z, const char* s ); +#ifndef OGLFT_NO_QT + /*! + * Draw a string at the given 2D point. + * \param x the X position. + * \param y the Y position. + * \param s the (UNICODE) string to draw. + */ + void draw ( GLfloat x, GLfloat y, const QString& s ); + /*! + * Draw a string at the given 3D point. + * \param x the X position. + * \param y the Y position. + * \param z the Z position. + * \param s the (UNICODE) string to draw. + */ + void draw ( GLfloat x, GLfloat y, GLfloat z, const QString& s ); + /*! + * Draw a real number per the given format at the given 2D point. + * \param x the X position. + * \param y the Y position. + * \param format Like a typical printf format. Regular text is printed + * while a '%' introduces the real number's format. Includes the + * following format flags: + * \li %%x.yf - floating point in field width x and precision y + * \li %%x.ye - scientific notation in field width x and precision y + * \li %%x.yg - pick best floating or scientific in field width x and + * precision y + * \li %%p - draw as a proper fraction, e.g. 1 1/2. Note: this currently + * requires a special font which encodes glyphs to be drawn for the + * numerator and demoninator in the UNICODE Private Area (0xE000). + * + * \param number the numeric value. + */ + void draw ( GLfloat x, GLfloat y, const QString& format, double number ); + /*! + * Draw a real number per the given format at the given 3D point. + * \param x the X position. + * \param y the Y position. + * \param z the Z position. + * \param format Like a typical printf format. Regular text is printed + * while a '%' introduces the real number's format. Includes the + * following format flags: + * \li %%x.yf - floating point in field width x and precision y + * \li %%x.ye - scientific notation in field width x and precision y + * \li %%x.yg - pick best floating or scientific in field width x and + * precision y + * \li %%p - draw as a proper fraction, e.g. 1 1/2. Note: this currently + * requires a special font which encodes glyphs to be drawn for the + * numerator and demoninator in the UNICODE Private Area (0xE000). + * + * \param number the numeric value. + */ + void draw ( GLfloat x, GLfloat y, GLfloat z, const QString& format, + double number ); +#endif /* OGLFT_NO_QT */ + /*! + * \return the face ascender, in point units. + */ + int ascender ( void ); + + /*! + * \return the face descender, in point units. + */ + int descender ( void ); + + protected: + // The various styles override these routines + + //! Some styles, in particular the Texture, need specialized steps + //! to compile a glyph into an OpenGL display list. + //! \param face the FT_Face containing the glyph. + //! \param glyph_index the index of the glyph in face. + //! \return the display list of the compiled glyph. + virtual GLuint compileGlyph ( FT_Face face, FT_UInt glyph_index ) = 0; + + //! Each style implements its own glyph rendering routine. + //! \param face the FT_Face containing the glyph. + //! \param glyph_index the index of the glyph in face. + virtual void renderGlyph ( FT_Face face, FT_UInt glyph_index ) = 0; + + //! There is a slight different between the way in which the polygonal + //! and raster styles select the character size for FreeType to generate. + virtual void setCharSize ( void ) = 0; + + //! The different styles have different caching needs (well, really only + //! the texture style currently has more than the display list cache). + virtual void clearCaches ( void ) = 0; + + //! The polygonal and raster styles compute different values for the + //! Z rotation offset. (It's in integer pixels for the raster styles and + //! in floating point pixels for the polygonal styles.) + virtual void setRotationOffset ( void ) = 0; + + private: + void init ( void ); + BBox measure_nominal ( const char* s ); +#ifndef OGLFT_NO_QT + BBox measure_nominal ( const QString& s ); + QString format_number ( const QString& format, double number ); +#endif /* OGLFT_NO_QT */ + }; + + //! This is the base class of the polygonal styles: outline, filled and solid. + /*! + * In the polygonal styles, the detailed geometric outlines of the glyphs + * are extracted from the font file and rendered as polygons. + */ + class Polygonal : public Face { + protected: + //! Angle of rotation of characters relative to text orientation. + struct { + bool active_; + GLfloat x_, y_, z_; + } character_rotation_; + + //! The tessellation of curves is pretty crude; regardless of length, + //! use the same number of increments (and as near as I can tell, this + //! is more than sufficient unless the glyph takes up the whole screen). + unsigned int tessellation_steps_; + + //! When curves are tessellated, we use the forward difference algorithm + //! from Foley and van Dam for parametric curves (pg. 511 of 2nd Ed. in C). + //! So, the step size, delta, is in the parametric variable which is always + //! on the interval [0,1]. Therefore, delta = 1/tessellation_steps + double delta_, delta2_, delta3_; + + //! For vector rendition modes, FreeType is allowed to generate the + //! lines and arcs at the original face definition resolution. To + //! get to the proper glyph size, the vertices are scaled before + //! they're passed to the GLU tessellation routines. + float vector_scale_; + + //! Callbacks for FreeType glyph decomposition into outlines + FT_Outline_Funcs interface_; + + //! Default number of steps to break TrueType and Type1 arcs into. + //! (Note: this looks good to me, anyway) + static const unsigned int DEFAULT_TESSELLATION_STEPS = 4; + + /*! + * VertexInfo is a private class which is used by the decomposition and + * tessellation routines to store the vertices and other data of the glyph's + * outline. Because of the "impedance mismatch" between the crazy + * 26.6 fixed point format of the FreeType library (well, don't + * blame them; look at what they have to work with) and OpenGL's preference + * for double precision, this simple vector has two constructors: one + * for 26.6 format and one for direct floating point. + * + * VertexInfo also contains (optional) pointers to objects which + * implement the ColorTess and TextureTess interfaces. + */ + struct VertexInfo { + double v_[3]; //!< Why is this double precision? Because the second + //!< argument to the routine gluTessVertex is a pointer + //!< to an array of doubles. Otherwise, we could use + //!< single precision everywhere. + + //! The user can provide a ColorTess object which computes a color + //! for each tesselated vertex. + ColorTess* color_tess_; + + //! The user can provide a TextureTess object which computes texture + //! coordinates for each tesselated vertex. + TextureTess* texture_tess_; + + //! Default constructor just initializes Vertex to zero. + //! \param color_tess optional color tesselation object. + //! \param texture_tess optional texture tesselation object. + VertexInfo ( ColorTess* color_tess = 0, TextureTess* texture_tess = 0 ) + : color_tess_( color_tess ), texture_tess_( texture_tess ) + { + v_[X] = v_[Y] = v_[Z] = 0.; + } + + /*! + * Construct a Vertex from a point in a FreeType contour. + * \param ft_v a FreeType FT_Vector, normally passed into the + * the decomposition callbacks. + * \param color_tess optional color tesselation object. + * \param texture_tess optional texture tesselation object. + */ + VertexInfo ( FT_Vector* ft_v, ColorTess* color_tess = 0, + TextureTess* texture_tess = 0 ) + : color_tess_( color_tess ), texture_tess_( texture_tess ) + { + v_[X] = (double)( ft_v->x / 64 ) + (double)( ft_v->x % 64 ) / 64.; + v_[Y] = (double)( ft_v->y / 64 ) + (double)( ft_v->y % 64 ) / 64.; + v_[Z] = 0.; + } + + /*! + * Construct a Vertex from a 2D point. + * \param p 2D array of doubles. + * \param color_tess optional color tesselation object. + * \param texture_tess optional texture tesselation object. + */ + VertexInfo ( double p[2], ColorTess* color_tess = 0, + TextureTess* texture_tess = 0 ) + : color_tess_( color_tess ), texture_tess_( texture_tess ) + { + v_[X] = p[X]; + v_[Y] = p[Y]; + v_[Z] = 0.; + } + + /*! + * Construct a Vertex from a 2D point. + * \param x the X coordinate. + * \param y the Y coordinate. + * \param color_tess optional color tesselation object. + * \param texture_tess optional texture tesselation object. + */ + VertexInfo ( double x, double y, ColorTess* color_tess = 0, + TextureTess* texture_tess = 0 ) + : color_tess_( color_tess ), texture_tess_( texture_tess ) + { + v_[X] = x; + v_[Y] = y; + v_[Z] = 0.; + } + + //! Treat the Vertex like a vector: Normalize its length in the + //! usual way. + void normalize ( void ) + { + double length = sqrt( v_[X] * v_[X] + v_[Y] * v_[Y] + v_[Z] * v_[Z] ); + v_[X] /= length; + v_[Y] /= length; + v_[Z] /= length; + } + }; + + /*! + * Buffers the last control point as the outline of a glyph is + * decomposed. + */ + VertexInfo last_vertex_; + + //! Normally, we will consider a list of vertices. + typedef std::list< VertexInfo* > VertexInfoList; + + //! A convenience definition of the iterator over the list of vertices. + typedef VertexInfoList::const_iterator VILCI; + + //! A convenience definition of the iterator over the list of vertices. + typedef VertexInfoList::iterator VILI; + + /*! + * As curves are decomposed out of the glyph, their vertices are passed + * along to the GLU tessellation functions. These vertices have to + * hang around until gluTessContourEnd is called. + */ + VertexInfoList vertices_; + + //! As GLU tessellation proceeds, new contours open with every call + //! to moveTo. + bool contour_open_; + + //! The user can provide a ColorTess object which computes a color + //! for each tesselated vertex. + ColorTess* color_tess_; + + //! The user can provide a TextureTess object which computes texture + //! coordinates for each tesselated vertex. + TextureTess* texture_tess_; + + public: + /*! + * \param filename the filename which contains the font face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Polygonal ( const char* filename, float point_size = 12, + FT_UInt resolution = 100 ); + + /*! + * \param data_base the memory location (base pointer) which contains the font face. + * \param data_size the size (in bytes) of the font data found at \ref data_base. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Polygonal ( const FT_Byte* data_base, const FT_Long data_size, + float point_size = 12, FT_UInt resolution = 100); + + /*! + * \param face open Freetype FT_Face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Polygonal ( FT_Face face, float point_size = 12, FT_UInt resolution = 100 ); + + /*! + * The Polygonal destructor doesn't do anything in particular. + */ + OGLFT_API virtual ~Polygonal ( void ); + + /*! + * TrueType and Type1 files describe the boundaries of glyphs with + * quadratic and cubic curves, respectively. Since OpenGL can only really + * draw straight lines, these curves have to be tessellated. The + * number of steps used is fixed for all glyphs in the face, + * but can be changed through this method. Other notes: This value is + * only applicable for Outline, Filled and Solid styles. Changing this value + * invalidates any cached display lists for glyphs in this face. + * + * \param tessellation_steps the number of steps to tessellate each curved + * segment of a glyph outline. + */ + OGLFT_API void setTessellationSteps ( unsigned int tessellation_steps ); + + /*! + * \return the number of steps used to tessellate curves in the + * polygonal font types. + */ + OGLFT_API unsigned int tessellationSteps ( void ) const { return tessellation_steps_; } + + /*! + * Set the individual character rotation in the X direction. + * \param character_rotation_x angle in degrees of the X rotation. + */ + OGLFT_API void setCharacterRotationX ( GLfloat character_rotation_x ); + + /*! + * Set the individual character rotation in the Y direction. + * \param character_rotation_y angle in degrees of the Y rotation. + */ + OGLFT_API void setCharacterRotationY ( GLfloat character_rotation_y ); + + /*! + * Set the individual character rotation in the Z direction. + * \param character_rotation_z angle in degrees of the Z rotation. + */ + OGLFT_API void setCharacterRotationZ ( GLfloat character_rotation_z ); + + /*! + * \return the character rotation in the X direction. + */ + OGLFT_API GLfloat characterRotationX ( void ) const { return character_rotation_.x_; } + + /*! + * \return the character rotation in the Y direction. + */ + OGLFT_API GLfloat characterRotationY ( void ) const { return character_rotation_.y_; } + + /*! + * \return the character rotation in the Z direction. + */ + OGLFT_API GLfloat characterRotationZ ( void ) const { return character_rotation_.z_; } + + /*! + * Set an optional color tesselation object. Each tesselated vertex + * is passed to this object, which returns a color for that position + * in space. + * \param color_tess the color tesselation object. + */ + OGLFT_API void setColorTess ( ColorTess* color_tess ); + /*! + * \return the color tesselation object. + */ + OGLFT_API ColorTess* colorTess ( void ) const { return color_tess_; } + /*! + * Set an optional texture coordinate tesselation object. Each + * tessellated vertex is passed to this object, which returns + * texture coordinates for that position in space. + * \param texture_tess the texture coordinate tesselation object. + */ + OGLFT_API void setTextureTess ( TextureTess* texture_tess ); + /*! + * \return the texture coordinate tesselation object. + */ + OGLFT_API TextureTess* textureTess ( void ) const { return texture_tess_; } + + /*! + * \return the height (i.e., line spacing) at the current character size. + */ + OGLFT_API double height ( void ) const; + + /*! + * Implement measuring a character in a polygonal face. + * \param c the (latin1) character to measure + * \return the bounding box of c. + */ + OGLFT_API BBox measure ( unsigned char c ); +#ifndef OGLFT_NO_QT + /*! + * Implement measuring a character in a polygonal face. + * \param c the (UNICODE) character to measure + * \return the bounding box of c. + */ + OGLFT_API BBox measure ( const QChar c ); +#endif /* OGLFT_NO_QT */ + /*! + * Measure a string of characters. Note: currently, this merely + * calls Face's measure routine. + * \param s string of (latin1) characters to measure + * \return the bounding box of s. + */ + OGLFT_API BBox measure ( const char* s ) { return Face::measure( s ); } +#ifndef OGLFT_NO_QT + /*! + * Implement measuring a formatted number + * \param format the format string + * \param number to value to format + * \return the bounding box of the formatted number + */ + OGLFT_API BBox measure ( const QString& format, double number ) + { return Face::measure( format, number ); } +#endif /* OGLFT_NO_QT */ + + private: + void init ( void ); + void setCharSize ( void ); + void setRotationOffset ( void ); + GLuint compileGlyph ( FT_Face face, FT_UInt glyph_index ); + protected: + void clearCaches ( void ); + }; + + //! Render text as a polygon outline. + /*! + * \image html outline_class.png + * Text is drawn as an outline of each glyph. The contours are extracted + * from the font file through FreeType. FreeType is used to scale the + * contours to a given size. Usually the outline is drawn in the foreground + * color, however, you can specify a ColorTess object to provide a color + * for each vertex individually. You can also use + * the per-glyph display list functionality to alter the attributes + * of each glyph. + * + * The only complexity to this style is selecting the point size. Since + * the outlines are drawn as a polygon, they are subject to the MODELVIEW + * transformation. The point size is nominally chosen to be the same as a + * raster image generated at the given resolution. Some experimentation + * with point size and resolution may be necessary to achieve the desired + * results. + */ + class Outline : public Polygonal { + public: + /*! + * \param filename the filename which contains the font face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Outline ( const char* filename, float point_size = 12, + FT_UInt resolution = 100 ); + + /*! + * \param data_base the memory location (base pointer) which contains the font face. + * \param data_size the size (in bytes) of the font data found at \ref data_base. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Outline ( const FT_Byte* data_base, const FT_Long data_size, + float point_size = 12, FT_UInt resolution = 100 ); + + /*! + * \param face open FreeType FT_Face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Outline ( FT_Face face, float point_size = 12, FT_UInt resolution = 100 ); + + /*! + * The destructor doesn't do anything in particular. + */ + ~Outline ( void ); + private: + void init ( void ); + void renderGlyph ( FT_Face face, FT_UInt glyph_index ); + static int moveToCallback ( FT_Vector* to, Outline* outline ); + static int lineToCallback ( FT_Vector* to, Outline* outline ); + static int conicToCallback ( FT_Vector* control, FT_Vector* to, Outline* outline ); + static int cubicToCallback ( FT_Vector* control1, FT_Vector* control2, + FT_Vector* to, Outline* outline ); + }; + + //! Render text as a filled polygons. + /*! + * \image html filled_class.png + * Each glyph is drawn as a filled polygon. The contours are extracted + * from the font file through FreeType. FreeType is used to scale the + * contours to the given size. Then the GLU tessellation routines are used + * to tessellate the contours into polygons (well, triangles). By default, + * these are drawn in GL_FILL polygon mode, but any other polygon mode + * can be specified. + * + * Usually, the polygons are drawn only in the + * foreground color, however, you may supply ColorTess and TextureTess + * objects which can alter the color or texture coordinates of each + * vertex individually. You can also use + * the per-glyph display list functionality to alter the attributes + * of each glyph. + * + * The only complexity to this style is selecting the point size. Since + * the glyphs are drawn as polygons, they are subject to the viewing and + * modeling transformations. The point size is nominally chosen to be the same + * as a raster image generated at the given resolution. Some experimentation + * with point size and resolution may be necessary to achieve the desired + * results. + */ + class Filled : public Polygonal { + //! 3D tessellation of glyphs is accomplished through the standard GLU + //! routines + GLUtesselator* tess_obj_; + + //! A place to store any extra vertices generated by the Combine callback + VertexInfoList extra_vertices_; + + protected: + //! Offset the glyph in the Z direction. Solely for the Solid subclass. + //! Until I can figure out how to shift the glyph outside the context + //! of this class, I guess this has got to stay (but it is redundant + //! to extrusion_.depth_) + GLfloat depth_offset_; + + public: + /*! + * \param filename the filename which contains the font face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Filled ( const char* filename, float point_size = 12, + FT_UInt resolution = 100 ); + + /*! + * \param data_base the memory location (base pointer) which contains the font face. + * \param data_size the size (in bytes) of the font data found at \ref data_base. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Filled ( const FT_Byte* data_base, const FT_Long data_size, + float point_size = 12, FT_UInt resolution = 100 ); + + /*! + * \param face open FreeType FT_Face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Filled ( FT_Face face, float point_size = 12, FT_UInt resolution = 100 ); + /*! + * The destructor deletes the GLU tessellation object allocated in + * in the constructor. + */ + OGLFT_API virtual ~Filled ( void ); + + /*! + * \return the list of extra vertices created by the GLU tessellation + * combine callback. + */ + OGLFT_API VertexInfoList& extraVertices ( void ) { return extra_vertices_; } + + protected: + void renderGlyph ( FT_Face face, FT_UInt glyph_index ); + private: + void init ( void ); + static int moveToCallback ( FT_Vector* to, Filled* filled ); + static int lineToCallback ( FT_Vector* to, Filled* filled ); + static int conicToCallback ( FT_Vector* control, FT_Vector* to, Filled* filled); + static int cubicToCallback ( FT_Vector* control1, FT_Vector* control2, + FT_Vector* to, Filled* filled ); + static void vertexCallback ( VertexInfo* vertex ); + static void beginCallback ( GLenum which ); + static void endCallback ( void ); + static void combineCallback ( GLdouble coords[3], void* vertex_data[4], + GLfloat weight[4], void** out_data, + Filled* filled ); + static void errorCallback ( GLenum error_code ); + }; + +#ifndef OGLFT_NO_SOLID + //! Render text as solid letters. + /*! + * \image html solid_class.png + * Each glyph is drawn as a closed solid. The contours are extracted + * from the font file through FreeType. FreeType is used to scale the + * contours to the given size. The contours are passed to the GLE + * tubing and extrusion library to create the sides of the solid. + * Then the GLU tessellation routines are used + * to tessellate the contours into polygons which are used to cap the sides. + * + * Currently, the solids are drawn only in the foreground color. However, + * proper surface normals are computed so that the solids may be lighted. + * Eventually, you'll be able to supply a color/texture + * coordinate function to make glyphs more interesting. Note that you can use + * the per-glyph display list functionality to alter each glyph individually. + * + * Another TODO item is to improve the interaction with GLE. Currently, + * you can only create block solids. Eventually, we'll have the capability + * add bevels and rounds to the edges of the solids and maybe even more + * general extrusions (like, for example, the swooshing letters in the title + * sequence of the Salkind's 1978 "Superman" movie). + * + * The only complexity to this style is selecting the point size. Since + * the glyphs are drawn as a collection of polygons, they are subject to the + * viewing and modeling transformations. The point size is nominally chosen + * to be the same as a raster image generated at the given resolution. + * Some experimentation with point size and resolution may be necessary to + * achieve the desired results. + */ + class Solid : public Filled { + private: + + //! Callbacks for FreeType glyph decomposition into outlines (note: this + //! has the same name as the variable in Polygonal, but it is distinct since + //! the routines for the GLE contouring are different from the Filled + //! GLU tessellation routines. This may be too confusing?) + FT_Outline_Funcs interface_; + + //! For now, you can only get block extruded solids + static const unsigned int N_POLYLINE_PTS = 4; + + //! Data for the gleExtrusion routine + struct glePoint2D { + double p_[2]; + glePoint2D ( double p[2] ) { p_[X] = p[X]; p_[Y] = p[Y]; } + glePoint2D ( double x, double y ) { p_[X] = x; p_[Y] = y; } + glePoint2D ( const VertexInfo& v ) { p_[X] = v.v_[X]; p_[Y] = v.v_[Y]; } + }; + + //! Collect all the output from GLE in one of these structures. + struct { + double depth_; + struct { + int x_, y_; + } normal_sign_; + std::vector< glePoint2D > contour_; + std::vector< glePoint2D > contour_normals_; + gleDouble up_[3]; + int n_polyline_pts_; + gleDouble point_array_[N_POLYLINE_PTS][3]; + } extrusion_; + + public: + /*! + * \param filename the filename which contains the font face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Solid ( const char* filename, float point_size = 12, FT_UInt resolution = 100 ); + + /*! + * \param data_base the memory location (base pointer) which contains the font face. + * \param data_size the size (in bytes) of the font data found at \ref data_base. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Solid ( const FT_Byte* data_base, const FT_Long data_size, + float point_size = 12, FT_UInt resolution = 100 ); + + /*! + * \param face open FreeType FT_Face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Solid ( FT_Face face, float point_size = 12, FT_UInt resolution = 100 ); + + /*! + * The destructor doesn't do anything in particular. + */ + OGLFT_API ~Solid ( void ); + /*! + * Set the thickness of the solid + * \param depth thickness of the solid in model units. + */ + OGLFT_API void setDepth ( double depth ); + + /*! + * \return the solid extrusion depth. + */ + OGLFT_API double depth ( void ) const { return extrusion_.depth_; } + + private: + // It would be nice if C/C++ had real matrix notation (like Perl!) + void assign ( gleDouble a[3], double x, double y, double z ) + { + a[X] = x; + a[Y] = y; + a[Z] = z; + } + + void init ( void ); + void renderGlyph ( FT_Face face, FT_UInt glyph_index ); + static int moveToCallback ( FT_Vector* to, Solid* solid ); + static int lineToCallback ( FT_Vector* to, Solid* solid ); + static int conicToCallback ( FT_Vector* control, FT_Vector* to, Solid* solid ); + static int cubicToCallback ( FT_Vector* control1, FT_Vector* control2, + FT_Vector* to, Solid* solid ); + }; +#endif /* OGLFT_NO_SOLID */ + //! This is the base class of the raster styles: bitmap, grayscale and + //! translucent. + /*! + * In the raster styles, FreeType's rasterizer is used to generate raster + * images of each glyph. + */ + class Raster : public Face { + protected: + //! Raster glyph can be rotated in the Z plane (in addition to the string + //! rotation). + GLfloat character_rotation_z_; + public: + /*! + * \param filename the filename which contains the font face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Raster ( const char* filename, float point_size = 12, FT_UInt resolution = 100 ); + + /*! + * \param data_base the memory location (base pointer) which contains the font face. + * \param data_size the size (in bytes) of the font data found at \ref data_base. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Raster ( const FT_Byte* data_base, const FT_Long data_size, + float point_size = 12, FT_UInt resolution = 100 ); + + /*! + * \param face open FreeType FT_Face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Raster ( FT_Face face, float point_size = 12, FT_UInt resolution = 100 ); + /*! + * The destructor doesn't do anything in particular. + */ + OGLFT_API virtual ~Raster ( void ); + /*! + * Set the individual character rotation in the Z direction. + * \param character_rotation_z angle in degrees of Z rotation. + */ + OGLFT_API void setCharacterRotationZ ( GLfloat character_rotation_z ); + /*! + * \return the character rotation in the Z direction. + */ + OGLFT_API GLfloat characterRotationZ ( void ) const { return character_rotation_z_; } + + /*! + * \return the height (i.e., line spacing) at the current character size. + */ + OGLFT_API double height ( void ) const; + + /*! + * Implement measuring a character in a raster face. + * \param c the (latin1) character to measure + * \return the bounding box of c. + */ + OGLFT_API BBox measure ( unsigned char c ); +#ifndef OGLFT_NO_QT + /*! + * Implement measuring a character in a raster face. + * \param c the (UNICODE) character to measure + * \return the bounding box of c. + */ + OGLFT_API BBox measure ( const QChar c ); +#endif /* OGLFT_NO_QT */ + /*! + * Measure a string of characters. Note: currently, this merely + * calls Face's measure routine. + * \param s string of (latin1) characters to measure + * \return the bounding box of s. + */ + OGLFT_API BBox measure ( const char* s ) { return Face::measure( s ); } +#ifndef OGLFT_NO_QT + /*! + * Implement measuring a formatted number + * \param format the format string + * \param number to value to format + * \return the bounding box of the formatted number + */ + OGLFT_API BBox measure ( const QString& format, double number ); +#endif /* OGLFT_NO_QT */ + + private: + void init ( void ); + GLuint compileGlyph ( FT_Face face, FT_UInt glyph_index ); + void setCharSize ( void ); + void setRotationOffset ( void ); + void clearCaches ( void ); + }; + + //! Render text as a monochrome raster image. + /*! + * \image html monochrome_class.png + * This is more or less the standard way in which text is intended to + * be rendered in OpenGL. It uses the \c glBitmap call to draw a sequence + * of monochrome bitmaps. Since FreeType is capable of rotating glyphs + * created from faces based on vector outlines, you can rotate (in the Z plane) + * both the text string as well as the individual characters in the string. + * + * Note: you \em must call + * \code + * glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + * \endcode + * before drawing in order for monochrome glyphs to be rendered properly. + * + * Another note: It is helpful to have the option + * \c GL_RASTER_POSITION_UNCLIPPED_IBM available if you intend to draw text + * at MODELVIEW based positions, otherwise if the initial text position is off + * the screen, the entire image is clipped. + */ + class Monochrome : public Raster { + public: + /*! + * \param filename the filename which contains the font face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Monochrome ( const char* filename, float point_size = 12, + FT_UInt resolution = 100 ); + + /*! + * \param data_base the memory location (base pointer) which contains the font face. + * \param data_size the size (in bytes) of the font data found at \ref data_base. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Monochrome ( const FT_Byte* data_base, const FT_Long data_size, + float point_size = 12, FT_UInt resolution = 100 ); + + /*! + * \param font open FreeType FT_Face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Monochrome ( FT_Face face, float point_size = 12, FT_UInt resolution = 100 ); + /*! + * The destructor doesn't do anything in particular. + */ + OGLFT_API ~Monochrome ( void ); + private: + GLubyte* invertBitmap ( const FT_Bitmap& bitmap ); + void renderGlyph ( FT_Face face, FT_UInt glyph_index ); + }; + + //! Render text as a grayscale raster image. + /*! + * \image html grayscale_class.png + * The Grayscale style is similar to the Monochrome style. FreeType is used + * to rasterize a glyph and this is then drawn on the screen using + * \c glDrawPixels. The FreeType rasterization is done in anti-aliased mode. + * When Grayscale draws the glyph image, the resulting text is blended + * smoothly from the foreground color to the background color. The background + * of the glyph is opaque, so this style works best over a solid background. + * + * Note: you \em must call + * \code + * glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + * \endcode + * before drawing in order for grayscale glyphs to be rendered properly. + * + * Another note: It is helpful to have the option + * \c GL_RASTER_POSITION_UNCLIPPED_IBM available if you intend to draw text + * at MODELVIEW based positions, otherwise if the initial text position is off + * the screen, the entire image is clipped. + */ + class Grayscale : public Raster { + public: + /*! + * \param filename the filename which contains the font face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Grayscale ( const char* filename, float point_size = 12, + FT_UInt resolution = 100 ); + + /*! + * \param data_base the memory location (base pointer) which contains the font face. + * \param data_size the size (in bytes) of the font data found at \ref data_base. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Grayscale ( const FT_Byte* data_base, const FT_Long data_size, + float point_size = 12, FT_UInt resolution = 100 ); + + /*! + * \param face open FreeType FT_Face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Grayscale ( FT_Face face, float point_size = 12, FT_UInt resolution = 100 ); + /*! + * The destructor doesn't do anything in particular. + */ + OGLFT_API ~Grayscale ( void ); + private: + GLubyte* invertPixmap ( const FT_Bitmap& bitmap ); + void renderGlyph ( FT_Face face, FT_UInt glyph_index ); + }; + + //! Render text as a translucent raster image. + /*! + * \image html translucent_class.png + * The Translucent style is similar to the Grayscale style. FreeType is used + * to rasterize a glyph and this is then drawn on the screen using + * \c glDrawPixels. The FreeType rasterization is done in anti-aliased mode. + * When Translucent draws the glyph image, the grayscale levels provided + * by FreeType are used as Alpha values in the raster image. This allows + * the glyphs to be smoothly blended into complicated backgrounds. + * + * Note: you \em must call + * \code + * glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + * \endcode + * before drawing in order for translucent glyphs to be rendered properly. + * Additionally, you need to activate blending in order to achieve the + * translucent effect: + * \code + * glEnable( GL_BLEND ); + * glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + * \endcode + * + * Another note: It is helpful to have the option + * \c GL_RASTER_POSITION_UNCLIPPED_IBM available if you intend to draw text + * at MODELVIEW based positions, otherwise if the initial text position is off + * the screen, the entire image is clipped. + */ + class Translucent : public Raster { + public: + /*! + * \param filename the filename which contains the font face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Translucent ( const char* filename, float point_size = 12, + FT_UInt resolution = 100 ); + + /*! + * \param data_base the memory location (base pointer) which contains the font face. + * \param data_size the size (in bytes) of the font data found at \ref data_base. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Translucent ( const FT_Byte* data_base, const FT_Long data_size, + float point_size = 12, FT_UInt resolution = 100 ); + + /*! + * \param face open FreeType FT_Face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Translucent ( FT_Face face, float point_size = 12, FT_UInt resolution = 100 ); + + /*! + * The destructor doesn't do anything in particular. + */ + OGLFT_API ~Translucent ( void ); + + private: + GLubyte* invertPixmapWithAlpha ( const FT_Bitmap& bitmap ); + void renderGlyph ( FT_Face face, FT_UInt glyph_index ); + }; + + //! This is the base class of the texture style. + class Texture : public Face { + protected: + //! Angle of rotation of characters relative to text orientation. + struct { + bool active_; //!< Is character rotation non-zero? (faster than checking all + //!< the other values.) + GLfloat x_, //!< Angle of rotation in the X direction. + y_, //!< Angle of rotation in the Y direction. + z_; //!< Angle of rotation in the Z direction. + } character_rotation_; + + /*! + * The textured glyphs need a little bit more infrastructure to draw + * since we have to remember the size of the texture object itself + * (at least implicitly). Also, we don't want to create any more + * texture objects than we have to, so they are always cached. + */ + struct TextureInfo { + GLuint texture_name_; //!< A bound texture name is an integer in OpenGL. + FT_Int left_bearing_, //!< The left bearing of the transformed glyph. + bottom_bearing_; //!< The bottom bearing of the transformed glyph. + int width_, //!< The 2**l width of the texture. + height_; //!< The 2**m height of the texture. + GLfloat texture_s_, //!< The fraction of the texture width occupied + //!< by the glyph. + texture_t_; //!< The fraction of the texture height occupied + //!< by the glyph. + FT_Vector advance_; //!< The advance vector of the transformed glyph. + }; + + //! Type of the cache of defined glyph to texture objects mapping. + typedef std::map< FT_UInt, TextureInfo > GlyphTexObjs; + + //! A convenience definition of the iterator over the glyph to texture + //! object map. + typedef GlyphTexObjs::const_iterator GTOCI; + + //! A convenience definition of the iterator over the glyph to texture + //! object map. + typedef GlyphTexObjs::iterator GTOI; + + //! Cache of defined glyph texture objects. + GlyphTexObjs glyph_texobjs_; + + public: + /*! + * \param filename the filename which contains the font face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Texture ( const char* filename, float point_size = 12, + FT_UInt resolution = 100 ); + + /*! + * \param data_base the memory location (base pointer) which contains the font face. + * \param data_size the size (in bytes) of the font data found at \ref data_base. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Texture ( const FT_Byte* data_base, const FT_Long data_size, + float point_size = 12, FT_UInt resolution = 100 ); + + /*! + * \param face open FreeType FT_Face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Texture ( FT_Face face, float point_size = 12, FT_UInt resolution = 100 ); + + /*! + * The texture destructor doesn't really do anything. + */ + OGLFT_API virtual ~Texture ( void ); + /*! + * Set the individual character rotation in the X direction. + * \param character_rotation_x angle in degrees of X rotation. + */ + OGLFT_API void setCharacterRotationX ( GLfloat character_rotation_x ); + + /*! + * Set the individual character rotation in the Y direction. + * \param character_rotation_y angle in degrees of Y rotation. + */ + OGLFT_API void setCharacterRotationY ( GLfloat character_rotation_y ); + + /*! + * Set the individual character rotation in the Z direction. + * \param character_rotation_z angle in degrees of Z rotation. + */ + OGLFT_API void setCharacterRotationZ ( GLfloat character_rotation_z ); + + /*! + * \return the character rotation in the X direction. + */ + OGLFT_API GLfloat characterRotationX ( void ) const { return character_rotation_.x_; } + + /*! + * \return the character rotation in the Y direction. + */ + OGLFT_API GLfloat characterRotationY ( void ) const { return character_rotation_.y_; } + + /*! + * \return the character rotation in the Z direction. + */ + OGLFT_API GLfloat characterRotationZ ( void ) const { return character_rotation_.z_; } + + /*! + * \return the height (i.e., line spacing) at the current character size. + */ + OGLFT_API double height ( void ) const; + + /*! + * Implement measuring a character in a texture face. + * \param c the (latin1) character to measure + * \return the bounding box of c. + */ + OGLFT_API BBox measure ( unsigned char c ); +#ifndef OGLFT_NO_QT + /*! + * Implement measuring a character in a texture face. + * \param c the (UNICODE) character to measure + * \return the bounding box of c. + */ + OGLFT_API BBox measure ( const QChar c ); +#endif /* OGLFT_NO_QT */ + /*! + * Measure a string of characters. Note: currently, this merely + * calls Face's measure routine. + * \param s string of (latin1) characters to measure + * \return the bounding box of s. + */ + OGLFT_API BBox measure ( const char* s ) { return Face::measure( s ); } +#ifndef OGLFT_NO_QT + OGLFT_API BBox measure ( const QString& s ) + { return Face::measure( s ); } + + /*! + * Implement measuring a formatted number + * \param format the format string + * \param number to value to format + * \return the bounding box of the formatted number + */ + OGLFT_API BBox measure ( const QString& format, double number ) + { return Face::measure( format, number ); } +#endif /* OGLFT_NO_QT */ + + protected: + /*! + * OpenGL texture maps have to be a power of 2 in width and height (including + * apparently 1 = 2**0 ). This function returns the next higher power of + * 2 of the argument. If the argument is already a power of 2, you just + * get that back. + * \param a width or height of an image. + * \return value of a rounded to nearest, higher power of 2. + */ + unsigned int nearestPowerCeil ( unsigned int a ); + /*! + * This is all that distinguishes the various texture styles. Each subclass + * defines this method as appropriate. Once the texture is bound, it + * is rendered the same in all cases. + * \param face FT_Face containing the glyph to render. + * \param glyph_index index of glyph in face. + */ + virtual void bindTexture ( FT_Face face, FT_UInt glyph_index ) = 0; + + private: + void init ( void ); + void setCharSize ( void ); + void setRotationOffset ( void ); + GLuint compileGlyph ( FT_Face face, FT_UInt glyph_index ); + void renderGlyph ( FT_Face face, FT_UInt glyph_index ); + void clearCaches ( void ); + }; + + //! Render text as texture mapped monochrome quads. + /*! + * \image html texture_monochrome_class.png + * This style is similar to the Monochrome raster style, except instead + * of using \c glBitmap to draw the raster image, the image is used + * as a texture map on a quad. If drawing is confined to the Z plane, + * then you will see no difference between this style and Monochrome. + * However, because the quad is a 3D object, it can be transformed + * by the usual modeling operations; so, texture mapped glyphs can be + * rotated in the X and Y directions as well as Z direction. Also, + * if the viewing (or modeling) transformation has a non-unity scale or + * shear, the glyphs will also be scaled or sheared (unlike the raster + * styles). Also, there is no problem with clipping glyphs which lie + * off the screen; texture mapped quads are properly clipped to the + * screen boundary. + * + * If this is not convincing enough, the performance of texture mapped + * glyphs is generally as good as or better than the equivalent + * raster style (especially with hardware texture acceleration). However, + * they do take up more memory space. + * + * Note: you \em must call + * \code + * glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + * \endcode + * before drawing in order for textured glyphs to be rendered properly. + */ + class MonochromeTexture : public Texture { + public: + /*! + * \param filename the filename which contains the font face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API MonochromeTexture ( const char* filename, float point_size = 12, + FT_UInt resolution = 100 ); + + /*! + * \param data_base the memory location (base pointer) which contains the font face. + * \param data_size the size (in bytes) of the font data found at \ref data_base. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API MonochromeTexture ( const FT_Byte* data_base, const FT_Long data_size, + float point_size = 12, FT_UInt resolution = 100 ); + + /*! + * \param face open FreeType FT_Face + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API MonochromeTexture ( FT_Face face, float point_size = 12, + FT_UInt resolution = 100 ); + /*! + * The monochrome texture destructor doesn't really do anything. + */ + OGLFT_API ~MonochromeTexture ( void ); + private: + GLubyte* invertBitmap ( const FT_Bitmap& bitmap, int* width, int* height ); + void bindTexture ( FT_Face face, FT_UInt glyph_index ); + }; + + //! Render text as texture mapped grayscale quads. + /*! + * \image html texture_grayscale_class.png + * This style is similar to the Grayscale raster style, except instead + * of using \c glDrawPixels to draw the raster image, the image is used + * as a texture map on a quad. If drawing is confined to the Z plane, + * then you will see no difference between this style and Grayscale. + * However, because the quad is a 3D object, it can be transformed + * by the usual modeling operations; so, texture mapped glyphs can be + * rotated in the X and Y directions as well as Z direction. Also, + * if the viewing (or modeling) transformation has a non-unity scale or + * shear, the glyphs will also be scaled or sheared (unlike the raster + * styles). Also, there is no problem with clipping glyphs which lie + * off the screen; texture mapped quads are properly clipped to the + * screen boundary. + * + * If this is not convincing enough, the performance of texture mapped + * glyphs is generally as good as or better than the equivalent + * raster style (especially with hardware texture acceleration). However, + * they do consume more memory space. + * + * Note: you \em must call + * \code + * glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + * \endcode + * before drawing in order for textured glyphs to be rendered properly. + */ + class GrayscaleTexture : public Texture { + public: + /*! + * \param filename the filename which contains the font face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API GrayscaleTexture ( const char* filename, float point_size = 12, + FT_UInt resolution = 100 ); + + /*! + * \param data_base the memory location (base pointer) which contains the font face. + * \param data_size the size (in bytes) of the font data found at \ref data_base. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API GrayscaleTexture ( const FT_Byte* data_base, const FT_Long data_size, + float point_size = 12, FT_UInt resolution = 100 ); + + /*! + * \param face open FreeType FT_Face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API GrayscaleTexture ( FT_Face face, float point_size = 12, + FT_UInt resolution = 100 ); + /*! + * The grayscale texture destructor doesn't really do anything. + */ + OGLFT_API ~GrayscaleTexture ( void ); + private: + GLubyte* invertPixmap ( const FT_Bitmap& bitmap, int* width, int* height ); + void bindTexture ( FT_Face face, FT_UInt glyph_index ); + }; + + //! Render text as texture mapped translucent quads. + /*! + * \image html texture_translucent_class.png + * This style is similar to the Translucent raster style, except instead + * of using \c glDrawPixels to draw the raster image, the image is used + * as a texture map on a quad. If drawing is confined to the Z plane, + * then you will see no difference between this style and Translucent. + * However, because the quad is a 3D object, it can be transformed + * by the usual modeling operations; so, texture mapped glyphs can be + * rotated in the X and Y directions as well as Z direction. Also, + * if the viewing (or modeling) transformation has a non-unity scale or + * shear, the glyphs will also be scaled or sheared (unlike the raster + * styles). Also, there is no problem with clipping glyphs which lie + * off the screen; texture mapped quads are properly clipped to the + * screen boundary. + * + * If this is not convincing enough, the performance of texture mapped + * glyphs is generally as good as or better than the equivalent + * raster style (especially with hardware texture acceleration). However, + * they do consume more memory space. + * + * Note: you \em must call + * \code + * glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + * \endcode + * before drawing in order for textured glyphs to be rendered properly. + * Additionally, you need to activate blending in order to achieve the + * translucent effect: + * \code + * glEnable( GL_BLEND ); + * glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + * \endcode + */ + class TranslucentTexture : public Texture { + public: + /*! + * \param filename the filename which contains the font face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API TranslucentTexture ( const char* filename, float point_size = 12, + FT_UInt resolution = 100 ); + + /*! + * \param data_base the memory location (base pointer) which contains the font face. + * \param data_size the size (in bytes) of the font data found at \ref data_base. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API TranslucentTexture ( const FT_Byte* data_base, const FT_Long data_size, + float point_size = 12, FT_UInt resolution = 100 ); + + /*! + * \param face open FreeType FT_Face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API TranslucentTexture ( FT_Face face, float point_size = 12, + FT_UInt resolution = 100 ); + /*! + * The translucent texture destructor doesn't really do anything. + */ + OGLFT_API ~TranslucentTexture ( void ); + private: + GLubyte* invertPixmap ( const FT_Bitmap& bitmap, int* width, int* height ); + void bindTexture ( FT_Face face, FT_UInt glyph_index ); + }; +} // Close OGLFT namespace +#endif /* OGLFT_H */ diff --git a/engine/libraries/oglft/README b/engine/libraries/oglft/README new file mode 100644 index 0000000..6089682 --- /dev/null +++ b/engine/libraries/oglft/README @@ -0,0 +1,52 @@ +OGLFT Version 0.8 + +OGLFT is a library for drawing text in OpenGL. It usees the FreeType 2 +library to extract information from font files on your system. OGLFT +can handle any of the files which FreeType can read, both vector and +bitmapped (although currently the bitmap routines are not implemented). + +The software is available from www.sf.net/project/oglft. + +The documentation is in ./doc/html/index.html. + +The mailing list is oglft-devel@lists.sourceforge.net. + +A couple of installation notes: + +OGLFT depends on the GLE Tubing and Extrusion library to create solid +characters. If you don't have this library (or don't want solid +glyphs), you can disable this feature with: + +./configure --disable-solid + +If the configure script can't find the GLE library and header files, +it is disabled automatically. + +UNICODE character rendering, Demo3 and the performance test code +depend on the Qt library. You can disable them with: + +./configure --disable-qt + +Note that the configure script depends on finding the environment +variable QTDIR in order to locate the headers and library for Qt (if +not disabled). If the configure script can't successfully build a +simple Qt app, Qt is disabled automatically. + +'make check' builds the test programs in the tests/ directory. + +There is also a (mostly untested) Qt project file from which it may be +possible to generate a Makefile for operating systems for which +autoconf is not supported. Edit the files ./OGLFT/OGLFT.pro and +./tests/tests.pro to change the values to those appropriate for your +operating system. Generate the library and the test program by doing a +"make install" (this will actually only copy the library to the ./lib +directory). + +--------------------------------------------------------------------- +lignum Computing, Inc., July, 2002 +oglft@lignumcomputing.com + + +;;;Local variables: *** +;;;mode: text *** +;;;End: *** diff --git a/engine/libraries/oglft/liboglft/CMakeLists.txt b/engine/libraries/oglft/liboglft/CMakeLists.txt new file mode 100644 index 0000000..fb6d0ad --- /dev/null +++ b/engine/libraries/oglft/liboglft/CMakeLists.txt @@ -0,0 +1,45 @@ +CMAKE_MINIMUM_REQUIRED (VERSION 2.6) + +FIND_PACKAGE ( FreeType2 REQUIRED ) + +FILE( GLOB sources *.cpp ) +IF( DESIRED_QT_VERSION EQUAL 3) + INCLUDE_DIRECTORIES( ${QT_INCLUDE_DIR} ) +ELSEIF( DESIRED_QT_VERSION EQUAL 4 ) + INCLUDE_DIRECTORIES( ${QT_QTCORE_INCLUDE_DIR} ) + INCLUDE_DIRECTORIES( ${QT_QTGUI_INCLUDE_DIR} ) +ENDIF( DESIRED_QT_VERSION EQUAL 3) + +INCLUDE_DIRECTORIES( + ${FREETYPE2_INCLUDE_DIR} + ) +INCLUDE_DIRECTORIES( ${PROJECT_BINARY_DIR} ) +CONFIGURE_FILE( + "${CMAKE_CURRENT_SOURCE_DIR}/OGLFT.h.cmake" + "${PROJECT_BINARY_DIR}/OGLFT.h" +) + +IF( WIN32 ) + ADD_DEFINITIONS( -DOGLFT_BUILD ) +ENDIF( WIN32 ) + +ADD_LIBRARY( oglft STATIC ${sources} ) +TARGET_LINK_LIBRARIES( + oglft + ${FREETYPE2_LIBRARIES} + ${OPENGL_LIBRARIES} + ) + +INSTALL( + TARGETS oglft + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib + COMPONENT libraries +) + +INSTALL( + FILES "${PROJECT_BINARY_DIR}/OGLFT.h" + DESTINATION include/OGLFT + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ + COMPONENT headers +) diff --git a/engine/libraries/oglft/liboglft/OGLFT.cpp b/engine/libraries/oglft/liboglft/OGLFT.cpp new file mode 100644 index 0000000..db61802 --- /dev/null +++ b/engine/libraries/oglft/liboglft/OGLFT.cpp @@ -0,0 +1,3816 @@ +/* + * OGLFT: A library for drawing text with OpenGL using the FreeType library + * Copyright (C) 2002 lignum Computing, Inc. + * Copyright (C) 2008 Allen Barnett + * $Id:$ + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include + +#include "OGLFT.h" + +#ifndef OGLFT_NO_QT +#include +#endif + +namespace OGLFT { + + // This is the static instance of the FreeType library wrapper ... + + Library Library::library; + + // ... and this is the FreeType library handle itself. + + FT_Library Library::library_; + + // The static instance above causes this constructor to be called + // when the object module is loaded. + + Library::Library ( void ) + { + + FT_Error error = FT_Init_FreeType( &library_ ); + + if ( error != 0 ) { + std::cerr << "Could not initialize the FreeType library. Exiting." << std::endl; + exit( 1 ); + } + } + + Library::~Library ( void ) + { + FT_Error error = FT_Done_FreeType( library_ ); + + if ( error != 0 ) { + std::cerr << "Could not terminate the FreeType library." << std::endl; + } + } + + // Return the only instance in the process + + FT_Library& Library::instance ( void ) + { + return library_; + } + + // Load a new face from file + + Face::Face ( const char* filename, float point_size, FT_UInt resolution ) + : point_size_( point_size ), resolution_( resolution ) + { + valid_ = true; // Assume the best :-) + + FT_Face ft_face; + + FT_Error error = FT_New_Face( Library::instance(), filename, 0, &ft_face ); + + if ( error != 0 ) { + valid_ = false; + return; + } + + // As of FreeType 2.1: only a UNICODE charmap is automatically activated. + // If no charmap is activated automatically, just use the first one. + if ( ft_face->charmap == 0 && ft_face->num_charmaps > 0 ) + FT_Select_Charmap( ft_face, ft_face->charmaps[0]->encoding ); + + faces_.push_back( FaceData( ft_face ) ); + + init(); + } + + // Load a new face from memory + + Face::Face ( const FT_Byte* data_base, const FT_Long data_size, float point_size, FT_UInt resolution ) + : point_size_( point_size ), resolution_( resolution ) + { + valid_ = true; // Assume the best :-) + + FT_Face ft_face; + + FT_Error error = FT_New_Memory_Face( Library::instance(), data_base, data_size, 0, &ft_face ); + + if ( error != 0 ) { + valid_ = false; + return; + } + + // As of FreeType 2.1: only a UNICODE charmap is automatically activated. + // If no charmap is activated automatically, just use the first one. + if ( ft_face->charmap == 0 && ft_face->num_charmaps > 0 ) + FT_Select_Charmap( ft_face, ft_face->charmaps[0]->encoding ); + + faces_.push_back( FaceData( ft_face ) ); + + init(); + } + + // Go with a face that the user has already opened. + + Face::Face ( FT_Face face, float point_size, FT_UInt resolution ) + : point_size_( point_size ), resolution_( resolution ) + { + valid_ = true; + + // As of FreeType 2.1: only a UNICODE charmap is automatically activated. + // If no charmap is activated automatically, just use the first one. + if ( face->charmap == 0 && face->num_charmaps > 0 ) + FT_Select_Charmap( face, face->charmaps[0]->encoding ); + + faces_.push_back( FaceData( face, false ) ); + + init(); + } + + // Standard initialization behavior once the font file is opened. + + void Face::init ( void ) + { + // By default, each glyph is compiled into a display list the first + // time it is encountered + + compile_mode_ = COMPILE; + + // By default, all drawing is wrapped with push/pop matrix so that the + // MODELVIEW matrix is not modified. If advance_ is set, then subsequent + // drawings follow from the advance of the last glyph rendered. + + advance_ = false; + + // Initialize the default colors + + foreground_color_[R] = 0.; + foreground_color_[G] = 0.; + foreground_color_[B] = 0.; + foreground_color_[A] = 1.; + + background_color_[R] = 1.; + background_color_[G] = 1.; + background_color_[B] = 1.; + background_color_[A] = 0.; + + // The default positioning of the text is at the origin of the first glyph + horizontal_justification_ = ORIGIN; + vertical_justification_ = BASELINE; + + // By default, strings are rendered in their nominal direction + string_rotation_ = 0; + + // setCharacterRotationReference calls the virtual function clearCaches() + // so it is up to a subclass to set the real default + rotation_reference_glyph_ = 0; + rotation_reference_face_ = 0; + rotation_offset_y_ = 0.; + } + + Face::~Face ( void ) + { + for ( unsigned int i = 0; i < faces_.size(); i++ ) + if ( faces_[i].free_on_exit_ ) + FT_Done_Face( faces_[i].face_ ); + } + + // Add another Face to select characters from file. + + bool Face::addAuxiliaryFace ( const char* filename ) + { + FT_Face ft_face; + + FT_Error error = FT_New_Face( Library::instance(), filename, 0, &ft_face ); + + if ( error != 0 ) + return false; + + faces_.push_back( FaceData( ft_face ) ); + + setCharSize(); + + return true; + } + + // Add another Face to select characters from memory. + + bool Face::addAuxiliaryFace ( const FT_Byte* data_base, const FT_Long data_size ) + { + FT_Face ft_face; + + FT_Error error = FT_New_Memory_Face( Library::instance(), data_base, data_size, 0, &ft_face ); + + if ( error != 0 ) + return false; + + faces_.push_back( FaceData( ft_face ) ); + + setCharSize(); + + return true; + } + + // Add another Face to select characters from (face) + + bool Face::addAuxiliaryFace ( FT_Face face ) + { + faces_.push_back( FaceData( face, false ) ); + + setCharSize(); + + return true; + } + + // Note: Changing the point size also clears the display list cache + + void Face::setPointSize ( float point_size ) + { + if ( point_size != point_size_ ) { + + point_size_ = point_size; + + clearCaches(); + + setCharSize(); + } + } + + // Note: Changing the resolution also clears the display list cache + + void Face::setResolution ( FT_UInt resolution ) + { + if ( resolution != resolution_ ) { + + resolution_ = resolution; + + clearCaches(); + + setCharSize(); + } + } + + // Note: Changing the background color also clears the display list cache. + + void Face::setBackgroundColor ( GLfloat red, GLfloat green, GLfloat blue, + GLfloat alpha ) + { + if ( background_color_[R] != red || + background_color_[G] != green || + background_color_[B] != blue || + background_color_[A] != alpha ) { + + background_color_[R] = red; + background_color_[G] = green; + background_color_[B] = blue; + background_color_[A] = alpha; + + clearCaches(); + } + } + + // Note: Changing the foreground color also clears the display list cache. + + void Face::setForegroundColor ( GLfloat red, GLfloat green, GLfloat blue, + GLfloat alpha ) + { + if ( foreground_color_[R] != red || + foreground_color_[G] != green || + foreground_color_[B] != blue || + foreground_color_[A] != alpha ) { + + foreground_color_[R] = red; + foreground_color_[G] = green; + foreground_color_[B] = blue; + foreground_color_[A] = alpha; + + clearCaches(); + } + } + + // Note: Changing the foreground color also clears the display list cache. + + void Face::setForegroundColor ( const GLfloat foreground_color[4] ) + { + if ( foreground_color_[R] != foreground_color[R] || + foreground_color_[G] != foreground_color[G] || + foreground_color_[B] != foreground_color[B] || + foreground_color_[A] != foreground_color[A] ) { + + foreground_color_[R] = foreground_color[R]; + foreground_color_[G] = foreground_color[G]; + foreground_color_[B] = foreground_color[B]; + foreground_color_[A] = foreground_color[A]; + + clearCaches(); + } + } + + // Note: Changing the background color also clears the display list cache. + + void Face::setBackgroundColor ( const GLfloat background_color[4] ) + { + if ( background_color_[R] != background_color[R] || + background_color_[G] != background_color[G] || + background_color_[B] != background_color[B] || + background_color_[A] != background_color[A] ) { + + background_color_[R] = background_color[R]; + background_color_[G] = background_color[G]; + background_color_[B] = background_color[B]; + background_color_[A] = background_color[A]; + + clearCaches(); + } + } +#ifndef OGLFT_NO_QT + // Note: Changing the foreground color also clears the display list cache. + + void Face::setForegroundColor ( const QRgb foreground_rgba ) + { + GLfloat foreground_color[4]; + foreground_color[R] = qRed( foreground_rgba ) / 255.; + foreground_color[G] = qGreen( foreground_rgba ) / 255.; + foreground_color[B] = qBlue( foreground_rgba ) / 255.; + foreground_color[A] = qAlpha( foreground_rgba ) / 255.; + + if ( foreground_color_[R] != foreground_color[R] || + foreground_color_[G] != foreground_color[G] || + foreground_color_[B] != foreground_color[B] || + foreground_color_[A] != foreground_color[A] ) { + + foreground_color_[R] = foreground_color[R]; + foreground_color_[G] = foreground_color[G]; + foreground_color_[B] = foreground_color[B]; + foreground_color_[A] = foreground_color[A]; + + clearCaches(); + } + } + + // Note: Changing the background color also clears the display list cache. + + void Face::setBackgroundColor ( const QRgb background_rgba ) + { + GLfloat background_color[4]; + background_color[R] = qRed( background_rgba ) / 255.; + background_color[G] = qGreen( background_rgba ) / 255.; + background_color[B] = qBlue( background_rgba ) / 255.; + background_color[A] = qAlpha( background_rgba ) / 255.; + + if ( background_color_[R] != background_color[R] || + background_color_[G] != background_color[G] || + background_color_[B] != background_color[B] || + background_color_[A] != background_color[A] ) { + + background_color_[R] = background_color[R]; + background_color_[G] = background_color[G]; + background_color_[B] = background_color[B]; + background_color_[A] = background_color[A]; + + clearCaches(); + } + } +#endif /* OGLFT_NO_QT */ + // Note: Changing the string rotation angle clears the display list cache + + void Face::setStringRotation ( GLfloat string_rotation ) + { + if ( string_rotation != string_rotation_ ) { + string_rotation_ = string_rotation; + + clearCaches(); + + // Note that this affects ALL glyphs accessed through + // the Face, both the vector and the raster glyphs. Very nice! + + if ( string_rotation_ != 0. ) { + float angle; + if ( string_rotation_ < 0. ) { + angle = 360.f - fmod( fabs( string_rotation_ ), 360.f ); + } + else { + angle = fmod( string_rotation_, 360.f ); + } + + FT_Matrix rotation_matrix; + FT_Vector sinus; + + FT_Vector_Unit( &sinus, (FT_Angle)(angle * 0x10000L) ); + + rotation_matrix.xx = sinus.x; + rotation_matrix.xy = -sinus.y; + rotation_matrix.yx = sinus.y; + rotation_matrix.yy = sinus.x; + + for ( unsigned int i = 0; i < faces_.size(); i++ ) + FT_Set_Transform( faces_[i].face_, &rotation_matrix, 0 ); + } + else + for ( unsigned int i = 0; i < faces_.size(); i++ ) + FT_Set_Transform( faces_[i].face_, 0, 0 ); + } + } + + // Note: Changing the rotation reference character clears the display list cache. + + void Face::setCharacterRotationReference ( unsigned char c ) + { + unsigned int f; + FT_UInt glyph_index = 0; + + for ( f = 0; f < faces_.size(); f++ ) { + glyph_index = FT_Get_Char_Index( faces_[f].face_, c ); + if ( glyph_index != 0 ) break; + } + + if ( f < faces_.size() && glyph_index != rotation_reference_glyph_ ) { + + FT_Error error = FT_Load_Glyph( faces_[f].face_, glyph_index, + FT_LOAD_DEFAULT ); + + if ( error != 0 ) return; + + rotation_reference_glyph_ = glyph_index; + + rotation_reference_face_ = faces_[f].face_; + + setRotationOffset(); + + clearCaches(); + } + } + + BBox Face::measure ( const char* s ) + { + BBox bbox; + char c; + + if ( ( c = *s++ ) != 0 ) { + + bbox = measure( c ); + + for ( c = *s; c != 0; c = *++s ) { + + BBox char_bbox = measure( c ); + + bbox += char_bbox; + } + } + + return bbox; + } + + BBox Face::measureRaw ( const char* s ) + { + BBox bbox; + + for ( char c = *s; c != 0; c = *++s ) { + BBox char_bbox; + + unsigned int f; + FT_UInt glyph_index = 0; + + for ( f = 0; f < faces_.size(); f++ ) { + glyph_index = FT_Get_Char_Index( faces_[f].face_, c ); + if ( glyph_index != 0 ) break; + } + + if ( glyph_index == 0 ) continue; + + FT_Error error = FT_Load_Glyph( faces_[f].face_, glyph_index, + FT_LOAD_DEFAULT ); + if ( error != 0 ) continue; + + FT_Glyph glyph; + error = FT_Get_Glyph( faces_[f].face_->glyph, &glyph ); + if ( error != 0 ) continue; + + FT_BBox ft_bbox; + FT_Glyph_Get_CBox( glyph, ft_glyph_bbox_unscaled, &ft_bbox ); + + FT_Done_Glyph( glyph ); + + char_bbox = ft_bbox; + char_bbox.advance_ = faces_[f].face_->glyph->advance; + + bbox += char_bbox; + } + + return bbox; + } + +#ifndef OGLFT_NO_QT + BBox Face::measure ( const QString& s ) + { + BBox bbox; + + if ( s.length() > 0 ) { + + bbox = measure( s.at( 0 ) ); + + for ( unsigned int i = 1; i < s.length(); i++ ) { + + BBox char_bbox = measure( s.at( i ) ); + + bbox += char_bbox; + } + } + + return bbox; + } + + BBox Face::measure ( const QString& format, double number ) + { + return measure( format_number( format, number ) ); + } + + BBox Face::measureRaw ( const QString& s ) + { + BBox bbox; + + for ( unsigned int i = 0; i < s.length(); i++ ) { + BBox char_bbox; + + unsigned int f; + FT_UInt glyph_index = 0; + + for ( f = 0; f < faces_.size(); f++ ) { + glyph_index = FT_Get_Char_Index( faces_[f].face_, s.at( i ).unicode() ); + if ( glyph_index != 0 ) break; + } + + if ( glyph_index == 0 ) { + continue; + } + + FT_Error error = FT_Load_Glyph( faces_[f].face_, glyph_index, + FT_LOAD_DEFAULT ); + if ( error != 0 ) continue; + + FT_Glyph glyph; + error = FT_Get_Glyph( faces_[f].face_->glyph, &glyph ); + if ( error != 0 ) continue; + + FT_BBox ft_bbox; + FT_Glyph_Get_CBox( glyph, ft_glyph_bbox_unscaled, &ft_bbox ); + + FT_Done_Glyph( glyph ); + + char_bbox = ft_bbox; + char_bbox.advance_ = faces_[f].face_->glyph->advance; + + bbox += char_bbox; + } + + return bbox; + } +#endif /* OGLFT_NO_QT */ + + // Measure the bounding box as if the (latin1) string were not rotated + + BBox Face::measure_nominal ( const char* s ) + { + if ( string_rotation_ == 0. ) + return measure( s ); + + for ( unsigned int f = 0; f < faces_.size(); f++ ) + FT_Set_Transform( faces_[f].face_, 0, 0 ); + + BBox bbox = measure( s ); + + float angle; + if ( string_rotation_ < 0. ) { + angle = 360.f - fmod( fabs( string_rotation_ ), 360.f ); + } + else { + angle = fmod( string_rotation_, 360.f ); + } + + FT_Matrix rotation_matrix; + FT_Vector sinus; + + FT_Vector_Unit( &sinus, (FT_Angle)(angle * 0x10000L) ); + + rotation_matrix.xx = sinus.x; + rotation_matrix.xy = -sinus.y; + rotation_matrix.yx = sinus.y; + rotation_matrix.yy = sinus.x; + + for ( unsigned int f = 0; f < faces_.size(); f++ ) + FT_Set_Transform( faces_[f].face_, &rotation_matrix, 0 ); + + return bbox; + } + +#ifndef OGLFT_NO_QT + // Measure the bounding box as if the (UNICODE) string were not rotated + + BBox Face::measure_nominal ( const QString& s ) + { + if ( string_rotation_ == 0. ) + return measure( s ); + + for ( unsigned int f = 0; f < faces_.size(); f++ ) + FT_Set_Transform( faces_[f].face_, 0, 0 ); + + BBox bbox = measure( s ); + + float angle; + if ( string_rotation_ < 0. ) { + angle = 360. - fmod( fabs( string_rotation_ ), 360.f ); + } + else { + angle = fmod( string_rotation_, 360.f ); + } + + FT_Matrix rotation_matrix; + FT_Vector sinus; + + FT_Vector_Unit( &sinus, (FT_Angle)(angle * 0x10000L) ); + + rotation_matrix.xx = sinus.x; + rotation_matrix.xy = -sinus.y; + rotation_matrix.yx = sinus.y; + rotation_matrix.yy = sinus.x; + + for ( unsigned int f = 0; f < faces_.size(); f++ ) + FT_Set_Transform( faces_[f].face_, &rotation_matrix, 0 ); + + return bbox; + } + + // Format the number per the given format. Mostly pointless + // for the standard formats, e.g. %12e. You can use the regular + // Qt functions to format such a string and avoid the parsing + // which is done here. + + QString Face::format_number ( const QString& format, double number ) + { + // This regexp says: + // 1. optionally match any thing up to a format, + // 2. the optional format (%...), and + // 3. optionally anything after it. + // Note that since everything is optional, the match always succeeds. + QRegExp format_regexp("((?:[^%]|%%)*)(%[0-9]*\\.?[0-9]*[efgp])?((?:[^%]|%%)*)"); +#if OGLFT_QT_VERSION == 3 + /*int pos = */ format_regexp.search( format ); +#elif OGLFT_QT_VERSION == 4 + /*int pos = */ format_regexp.exactMatch( format ); +#endif + + QStringList list = format_regexp.capturedTexts(); + + QStringList::Iterator it = list.begin(); + +#if OGLFT_QT_VERSION == 3 + it = list.remove( it ); // Remove the "matched" string, leaving the pieces +#elif OGLFT_QT_VERSION == 4 + it = list.erase( it ); // Remove the "matched" string, leaving the pieces +#endif + + if ( it == list.end() ) return QString::null; // Probably an error + + // Extract each piece from the list + + QString prefix, value_format, postfix; + char type = '\0'; + + if ( !(*it).isEmpty() ) + prefix = *it; + + ++it; + + if ( it != list.end() ) { + if ( !(*it).isEmpty() ) { + // Reparse this to extract the details of the format + QRegExp specifier_regexp( "([0-9]*)\\.?([0-9]*)([efgp])" ); +#if OGLFT_QT_VERSION == 3 + (void)specifier_regexp.search( *it ); +#elif OGLFT_QT_VERSION == 4 + (void)specifier_regexp.exactMatch( *it ); +#endif + QStringList specifier_list = specifier_regexp.capturedTexts(); + + QStringList::Iterator sit = specifier_list.begin(); +#if OGLFT_QT_VERSION == 3 + sit = specifier_list.remove( sit ); +#elif OGLFT_QT_VERSION == 4 + sit = specifier_list.erase( sit ); +#endif + int width = (*sit).toInt(); + ++sit; + int precision = (*sit).toInt(); + ++sit; +#if OGLFT_QT_VERSION == 3 + type = (*sit).at(0).latin1(); +#elif OGLFT_QT_VERSION == 4 + type = (*sit).at(0).toLatin1(); +#endif + // The regular formats just use Qt's number formatting capability + if ( type == 'e' || type == 'f' || type == 'g' ) + value_format = QString( "%1" ).arg( number, width, type, precision ); + + // For the fraction, though, we have to convert it the special + // UNICODE encoding + else if ( type == 'p' ) { + // Fixed for now... + if ( fabs( number ) < 1./256. ) + value_format = "0"; + else { + // Extract the integral part + int a = (int)number; + + if ( a != 0 ) + value_format = QString::number( a ); + + // Extract the fractional part: NOTE: THIS IS LIMITED TO + // REPRESENTING ALL FRACTIONS AS n/256 + int b = (int)rint( 256. * fabs( number - a ) ); + + // If b is exactly 256, then the original number was + // essentially an integer (to within 1/256-th) + if ( b == 256 ) + value_format = QString::number( rint( number ) ); + + else if ( b != 0 ) { + int c = 256; + // Remove common factors of two from the numerator and denominator + for ( ; ( b & 0x1 ) == 0; b >>= 1, c >>= 1 ); + + // Format the numerator and shift to 0xE000 sequence + QString numerator = QString::number( b ); + for ( uint i = 0; i < numerator.length(); i++ ) { + numerator[i] = QChar( numerator.at(i).unicode() - + QChar('0').unicode() + + 0xE000 ); + } + value_format += numerator; + value_format += QChar( 0xE00a ); // The '/' + // Format the denominator and shift to 0xE010 sequence + QString denominator = QString::number( c ); + for ( uint i = 0; i < denominator.length(); i++ ) { + denominator[i] = QChar( denominator.at(i).unicode() - + QChar('0').unicode() + + 0xE010 ); + } + value_format += denominator; + } + } + } + } + + ++it; + + if ( it != list.end() && !(*it).isEmpty() ) + postfix = *it; + } + + return prefix + value_format + postfix; + } +#endif /* OGLFT_NO_QT */ + + // Compile a (latin1) string into a display list + + GLuint Face::compile ( const char* s ) + { + // First, make sure all the characters in the string are themselves + // in display lists + const char* s_tmp = s; + + for ( char c = *s_tmp; c != 0; c = *++s_tmp ) { + compile( c ); + } + + GLuint dlist = glGenLists( 1 ); + glNewList( dlist, GL_COMPILE ); + + glColor4f( foreground_color_[R], foreground_color_[G], foreground_color_[B], + foreground_color_[A] ); + if ( !advance_ ) + glPushMatrix(); + + draw( s ); + + if ( !advance_ ) + glPopMatrix(); + + glEndList(); + + return dlist; + } +#ifndef OGLFT_NO_QT + // Compile a (UNICODE) string into a display list + + GLuint Face::compile ( const QString& s ) + { + // First, make sure all the characters in the string are themselves + // in display lists + for ( unsigned int i = 0; i < s.length(); i++ ) { + compile( s.at( i ) ); + } + + GLuint dlist = glGenLists( 1 ); + glNewList( dlist, GL_COMPILE ); + + glColor4f( foreground_color_[R], foreground_color_[G], foreground_color_[B], + foreground_color_[A] ); + if ( !advance_ ) + glPushMatrix(); + + draw( s ); + + if ( !advance_ ) + glPopMatrix(); + + glEndList(); + + return dlist; + } +#endif /* OGLFT_NO_QT */ + // Compile a (latin1) character glyph into a display list and cache + // it for later + + GLuint Face::compile ( unsigned char c ) + { + // See if we've done it already + + GDLCI fgi = glyph_dlists_.find( c ); + + if ( fgi != glyph_dlists_.end() ) + return fgi->second; + + unsigned int f; + FT_UInt glyph_index = 0; + + for ( f = 0; f < faces_.size(); f++ ) { + glyph_index = FT_Get_Char_Index( faces_[f].face_, c ); + if ( glyph_index != 0 ) break; + } + + if ( glyph_index == 0 ) + return 0; + + GLuint dlist = compileGlyph( faces_[f].face_, glyph_index ); + + glyph_dlists_[ c ] = dlist; + + return dlist; + } + +#ifndef OGLFT_NO_QT + // Compile a (UNICODE) character glyph into a display list and cache + // it for later + + GLuint Face::compile ( const QChar c ) + { + // See if we've done it already + + GDLCI fgi = glyph_dlists_.find( c.unicode() ); + + if ( fgi != glyph_dlists_.end() ) + return fgi->second; + + unsigned int f; + FT_UInt glyph_index = 0; + + for ( f = 0; f < faces_.size(); f++ ) { + glyph_index = FT_Get_Char_Index( faces_[f].face_, c.unicode() ); + if ( glyph_index != 0 ) break; + } + + if ( glyph_index == 0 ) + return 0; + + GLuint dlist = compileGlyph( faces_[f].face_, glyph_index ); + + glyph_dlists_[ c.unicode() ] = dlist; + + return dlist; + } +#endif /* OGLFT_NO_QT */ + // Assume the MODELVIEW matrix is already set and draw the (latin1) + // string. Note: this routine now ignores almost all settings: + // including the position (both modelview and raster), color, + // justification and advance settings. Consider this to be the raw + // drawing routine for which you are responsible for most of the + // setup. + + void Face::draw ( const char* s ) + { + DLCI character_display_list = character_display_lists_.begin(); + + for ( char c = *s; c != 0; c = *++s ) { + + if ( character_display_list != character_display_lists_.end() ) { + glCallList( *character_display_list ); + character_display_list++; + } + + draw( c ); + } + } +#ifndef OGLFT_NO_QT + // Assume the MODELVIEW matrix is already set and draw the (UNICODE) + // string. Note: this routine now ignores almost all settings: + // including the position (both modelview and raster), color, + // justification and advance settings. Consider this to be the raw + // drawing routine for which you are responsible for most of the + // setup. + + void Face::draw ( const QString& s ) + { + DLCI character_display_list = character_display_lists_.begin(); + + for ( unsigned int i = 0; i < s.length(); i++ ) { + + if ( character_display_list != character_display_lists_.end() ) { + glCallList( *character_display_list ); + character_display_list++; + } + + draw( s.at( i ) ); + } + } +#endif /* OGLFT_NO_QT */ + + // Assume the MODELVIEW matrix is already setup and draw the + // (latin1) character. + + void Face::draw ( unsigned char c ) + { + // See if we've done it already + + GDLCI fgi = glyph_dlists_.find( c ); + + if ( fgi != glyph_dlists_.end( ) ) { + glCallList( fgi->second ); + return; + } + + unsigned int f; + FT_UInt glyph_index = 0; + + for ( f = 0; f < faces_.size(); f++ ) { + glyph_index = FT_Get_Char_Index( faces_[f].face_, c ); + if ( glyph_index != 0 ) break; + } + + if ( glyph_index == 0 ) + return; + + // Otherwise, either compile it (and call it) or ... + + else if ( compile_mode_ == COMPILE ) { + GLuint dlist = compile( c ); + glCallList( dlist ); + } + + // ... render it immediately + + else { + renderGlyph( faces_[f].face_, glyph_index ); + } + } +#ifndef OGLFT_NO_QT + // Assume the MODELVIEW matrix is already setup and draw the + // (UNICODE) character. + + void Face::draw ( const QChar c ) + { + // See if we've done it already + + GDLCI fgi = glyph_dlists_.find( c.unicode() ); + + if ( fgi != glyph_dlists_.end( ) ) { + glCallList( fgi->second ); + return; + } + + unsigned int f; + FT_UInt glyph_index = 0; + + for ( f = 0; f < faces_.size(); f++ ) { + glyph_index = FT_Get_Char_Index( faces_[f].face_, c.unicode() ); + if ( glyph_index != 0 ) { + break; + } + } + + if ( glyph_index == 0 ) + return; + + // Otherwise, either compile it (and call it) or ... + + if ( compile_mode_ == COMPILE ) { + GLuint dlist = compile( c ); + glCallList( dlist ); + } + + // ... render it immediately + + else { + renderGlyph( faces_[f].face_, glyph_index ); + } + } +#endif /* OGLFT_NO_QT */ + // Draw the (latin1) character at the given position. The MODELVIEW + // matrix is modified by the glyph advance. + + void Face::draw ( GLfloat x, GLfloat y, unsigned char c ) + { + glTranslatef( x, y, 0. ); + + glColor4f( foreground_color_[R], foreground_color_[G], foreground_color_[B], + foreground_color_[A] ); + + glRasterPos2i( 0, 0 ); + + draw( c ); + } +#ifndef OGLFT_NO_QT + // Draw the (UNICODE) character at the given position. The MODELVIEW + // matrix is modified by the glyph advance. + + void Face::draw ( GLfloat x, GLfloat y, QChar c ) + { + glTranslatef( x, y, 0. ); + + glColor4f( foreground_color_[R], foreground_color_[G], foreground_color_[B], + foreground_color_[A] ); + + glRasterPos2i( 0, 0 ); + + draw( c ); + } +#endif /* OGLFT_NO_QT */ + // Draw the (latin1) string at the given position. + + void Face::draw ( GLfloat x, GLfloat y, const char* s ) + { + if ( !advance_ ) + glPushMatrix(); + + if ( horizontal_justification_ != ORIGIN || + vertical_justification_ != BASELINE ) { + glPushMatrix(); + + BBox bbox = measure_nominal( s ); + + GLfloat dx = 0, dy = 0; + + switch ( horizontal_justification_ ) { + case LEFT: + dx = -bbox.x_min_; break; + case CENTER: + dx = -( bbox.x_min_ + bbox.x_max_ ) / 2.f; break; + case RIGHT: + dx = -bbox.x_max_; break; + default: + break; + } + switch ( vertical_justification_ ) { + case BOTTOM: + dy = -bbox.y_min_; break; + case MIDDLE: + dy = -( bbox.y_min_ + bbox.y_max_ ) / 2.f; break; + case TOP: + dy = -bbox.y_max_; break; + default: + break; + } + + // There is probably a less expensive way to compute this + + glRotatef( string_rotation_, 0., 0., 1. ); + glTranslatef( dx, dy, 0 ); + glRotatef( -string_rotation_, 0., 0., 1. ); + } + + glTranslatef( x, y, 0. ); + + glColor4f( foreground_color_[R], foreground_color_[G], foreground_color_[B], + foreground_color_[A] ); + + glRasterPos2i( 0, 0 ); + + draw( s ); + + if ( horizontal_justification_ != ORIGIN || + vertical_justification_ != BASELINE ) + glPopMatrix(); + + if ( !advance_ ) + glPopMatrix(); + } + +#ifndef OGLFT_NO_QT + // Draw the (UNICODE) string at the given position. + + void Face::draw ( GLfloat x, GLfloat y, const QString& s ) + { + if ( !advance_ ) + glPushMatrix(); + + if ( horizontal_justification_ != ORIGIN || + vertical_justification_ != BASELINE ) { + glPushMatrix(); + + BBox bbox = measure_nominal( s ); + + GLfloat dx = 0, dy = 0; + + switch ( horizontal_justification_ ) { + case LEFT: + dx = -bbox.x_min_; break; + case CENTER: + dx = -( bbox.x_min_ + bbox.x_max_ ) / 2.; break; + case RIGHT: + dx = -bbox.x_max_; break; + case ORIGIN: + break; + } + switch ( vertical_justification_ ) { + case BOTTOM: + dy = -bbox.y_min_; break; + case MIDDLE: + dy = -( bbox.y_min_ + bbox.y_max_ ) / 2.; break; + case TOP: + dy = -bbox.y_max_; break; + case BASELINE: + break; + } + + // There is probably a less expensive way to compute this + + glRotatef( string_rotation_, 0., 0., 1. ); + glTranslatef( dx, dy, 0 ); + glRotatef( -string_rotation_, 0., 0., 1. ); + } + + glTranslatef( x, y, 0. ); + + glColor4f( foreground_color_[R], foreground_color_[G], foreground_color_[B], + foreground_color_[A] ); + + glRasterPos2i( 0, 0 ); + + draw( s ); + + if ( horizontal_justification_ != ORIGIN || + vertical_justification_ != BASELINE ) + glPopMatrix(); + + if ( !advance_ ) + glPopMatrix(); + } + + // Draw the number at the given position per the given format. + + void Face::draw ( GLfloat x, GLfloat y, const QString& format, double number ) + { + draw( x, y, format_number( format, number ) ); + } +#endif /* OGLFT_NO_QT */ + + Raster::Raster ( const char* filename, float point_size, FT_UInt resolution ) + : Face( filename, point_size, resolution ) + { + if ( !isValid() ) return; + + init(); + } + + Raster::Raster ( const FT_Byte* data_base, const FT_Long data_size, + float point_size, FT_UInt resolution ) + : Face( data_base, data_size, point_size, resolution ) + { + if ( !isValid() ) return; + + init(); + } + + Raster::Raster ( FT_Face face, float point_size, FT_UInt resolution ) + : Face( face, point_size, resolution ) + { + init(); + } + + void Raster::init ( void ) + { + character_rotation_z_ = 0; + + setCharSize(); + + setCharacterRotationReference( 'o' ); + } + + Raster::~Raster ( void ) + { + clearCaches(); + } + + void Raster::setCharacterRotationZ ( GLfloat character_rotation_z ) + { + if ( character_rotation_z != character_rotation_z_ ) { + character_rotation_z_ = character_rotation_z; + + clearCaches(); + } + } + + double Raster::height ( void ) const + { + if ( faces_[0].face_->height > 0 ) + return faces_[0].face_->height / 64.; + else + return faces_[0].face_->size->metrics.y_ppem; + } + + BBox Raster::measure ( unsigned char c ) + { + BBox bbox; + // For starters, just get the unscaled glyph bounding box + unsigned int f; + FT_UInt glyph_index = 0; + + for ( f = 0; f < faces_.size(); f++ ) { + glyph_index = FT_Get_Char_Index( faces_[f].face_, c ); + if ( glyph_index != 0 ) break; + } + + if ( glyph_index == 0 ) + return bbox; + + FT_Error error = FT_Load_Glyph( faces_[f].face_, glyph_index, + FT_LOAD_DEFAULT ); + if ( error != 0 ) + return bbox; + + FT_Glyph glyph; + error = FT_Get_Glyph( faces_[f].face_->glyph, &glyph ); + if ( error != 0 ) + return bbox; + + FT_BBox ft_bbox; + FT_Glyph_Get_CBox( glyph, ft_glyph_bbox_unscaled, &ft_bbox ); + + FT_Done_Glyph( glyph ); + + bbox = ft_bbox; + bbox.advance_ = faces_[f].face_->glyph->advance; + + // In order to be accurate regarding the placement of text not + // aligned at the glyph's origin (CENTER/MIDDLE), the bounding box + // of the raster format has to be projected back into the + // view's coordinates + + GLint viewport[4]; + GLdouble modelview[16], projection[16]; + + glGetIntegerv( GL_VIEWPORT, viewport ); + glGetDoublev( GL_MODELVIEW_MATRIX, modelview ); + glGetDoublev( GL_PROJECTION_MATRIX, projection ); + + // Well, first we have to get the Origin, since that is the basis + // of the bounding box + GLdouble x0, y0, z0; + gluUnProject( 0., 0., 0., modelview, projection, viewport, &x0, &y0, &z0 ); + + GLdouble x, y, z; + gluUnProject( bbox.x_min_, bbox.y_min_, 0., modelview, projection, viewport, + &x, &y, &z ); + bbox.x_min_ = (float)( x - x0 ); + bbox.y_min_ = (float)( y - y0 ); + + gluUnProject( bbox.x_max_, bbox.y_max_, 0., modelview, projection, viewport, + &x, &y, &z ); + bbox.x_max_ = (float)( x - x0 ); + bbox.y_max_ = (float)( y - y0 ); + + gluUnProject( bbox.advance_.dx_, bbox.advance_.dy_, 0., modelview, projection, + viewport, + &x, &y, &z ); + bbox.advance_.dx_ = (float)( x - x0 ); + bbox.advance_.dy_ = (float)( y - y0 ); + + return bbox; + } + +#ifndef OGLFT_NO_QT + BBox Raster::measure ( const QChar c ) + { + BBox bbox; + // For starters, just get the unscaled glyph bounding box + unsigned int f; + FT_UInt glyph_index = 0; + + for ( f = 0; f < faces_.size(); f++ ) { + glyph_index = FT_Get_Char_Index( faces_[f].face_, c.unicode() ); + if ( glyph_index != 0 ) break; + } + + if ( glyph_index == 0 ) + return bbox; + + FT_Error error = FT_Load_Glyph( faces_[f].face_, glyph_index, + FT_LOAD_DEFAULT ); + if ( error != 0 ) + return bbox; + + FT_Glyph glyph; + error = FT_Get_Glyph( faces_[f].face_->glyph, &glyph ); + if ( error != 0 ) + return bbox; + + FT_BBox ft_bbox; + FT_Glyph_Get_CBox( glyph, ft_glyph_bbox_unscaled, &ft_bbox ); + + FT_Done_Glyph( glyph ); + + bbox = ft_bbox; + bbox.advance_ = faces_[f].face_->glyph->advance; + + // In order to be accurate regarding the placement of text not + // aligned at the glyph's origin (CENTER/MIDDLE), the bounding box + // of the raster format has to be projected back into the + // view's coordinates + + GLint viewport[4]; + GLdouble modelview[16], projection[16]; + + glGetIntegerv( GL_VIEWPORT, viewport ); + glGetDoublev( GL_MODELVIEW_MATRIX, modelview ); + glGetDoublev( GL_PROJECTION_MATRIX, projection ); + + // Well, first we have to get the Origin, since that is the basis + // of the bounding box + GLdouble x0, y0, z0; + gluUnProject( 0., 0., 0., modelview, projection, viewport, &x0, &y0, &z0 ); + + GLdouble x, y, z; + gluUnProject( bbox.x_min_, bbox.y_min_, 0., modelview, projection, viewport, + &x, &y, &z ); + bbox.x_min_ = x - x0; + bbox.y_min_ = y - y0; + + gluUnProject( bbox.x_max_, bbox.y_max_, 0., modelview, projection, viewport, + &x, &y, &z ); + bbox.x_max_ = x - x0; + bbox.y_max_ = y - y0; + + gluUnProject( bbox.advance_.dx_, bbox.advance_.dy_, 0., modelview, projection, + viewport, + &x, &y, &z ); + bbox.advance_.dx_ = x - x0; + bbox.advance_.dy_ = y - y0; + + return bbox; + } + + BBox Raster::measure ( const QString& format, double number ) + { + return Face::measure( format, number ); + } +#endif /* OGLFT_NO_QT */ + + GLuint Raster::compileGlyph ( FT_Face face, FT_UInt glyph_index ) + { + GLuint dlist = glGenLists( 1 ); + glNewList( dlist, GL_COMPILE ); + + renderGlyph( face, glyph_index ); + + glEndList( ); + + return dlist; + } + + void Raster::setCharSize ( void ) + { + FT_Error error; + for ( unsigned int i = 0; i < faces_.size(); i++ ) { + error = FT_Set_Char_Size( faces_[i].face_, + (FT_F26Dot6)( point_size_ * 64 ), + (FT_F26Dot6)( point_size_ * 64 ), + resolution_, + resolution_ ); + if ( error != 0 ) return; + } + + if ( rotation_reference_glyph_ != 0 ) + setRotationOffset(); + } + + void Raster::setRotationOffset ( void ) + { + FT_Error error = FT_Load_Glyph( rotation_reference_face_, + rotation_reference_glyph_, + FT_LOAD_RENDER ); + + if ( error != 0 ) + return; + + rotation_offset_y_ = rotation_reference_face_->glyph->bitmap.rows / 2.f; + } + + void Raster::clearCaches ( void ) + { + GDLI fgi = glyph_dlists_.begin(); + + for ( ; fgi != glyph_dlists_.end(); ++fgi ) { + glDeleteLists( fgi->second, 1 ); + } + + glyph_dlists_.clear(); + } + + Monochrome::Monochrome ( const char* filename, float point_size, + FT_UInt resolution ) + : Raster( filename, point_size, resolution ) + {} + + Monochrome::Monochrome ( const FT_Byte* data_base, const FT_Long data_size, + float point_size, FT_UInt resolution ) + : Raster( data_base, data_size, point_size, resolution ) + {} + + Monochrome::Monochrome ( FT_Face face, float point_size, FT_UInt resolution ) + : Raster( face, point_size, resolution ) + {} + + Monochrome::~Monochrome ( void ) + {} + + GLubyte* Monochrome::invertBitmap ( const FT_Bitmap& bitmap ) + { + // In FreeType 2.0.9, the pitch of bitmaps was rounded up to an + // even number. In general, this disagrees with what we had been + // using for OpenGL. + + int width = bitmap.width / 8 + ( ( bitmap.width & 7 ) > 0 ? 1 : 0 ); + + GLubyte* inverse = new GLubyte[ bitmap.rows * width ]; + GLubyte* inverse_ptr = inverse; + + for ( int r = 0; r < bitmap.rows; r++ ) { + + GLubyte* bitmap_ptr = &bitmap.buffer[bitmap.pitch * ( bitmap.rows - r - 1 )]; + + for ( int p = 0; p < width; p++ ) + *inverse_ptr++ = *bitmap_ptr++; + } + + return inverse; + } + + void Monochrome::renderGlyph ( FT_Face face, FT_UInt glyph_index ) + { + // Start by retrieving the glyph's data. + + FT_Error error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT ); + + if ( error != 0 ) + return; + + FT_Glyph original_glyph; + FT_Glyph glyph; + + error = FT_Get_Glyph( face->glyph, &original_glyph ); + + if ( error != 0 ) + return; + + error = FT_Glyph_Copy( original_glyph, &glyph ); + + FT_Done_Glyph( original_glyph ); + + if ( error != 0 ) + return; + + // If the individual characters are rotated (as distinct from string + // rotation), then apply that extra rotation here. This is equivalent + // to the sequence + // glTranslate(x_center,y_center); + // glRotate(angle); + // glTranslate(-x_center,-y_center); + // which is used for the polygonal styles. The deal with the raster + // styles is that you must retain the advance from the string rotation + // so that the glyphs are laid out properly. So, we make a copy of + // the string rotated glyph, and then rotate that and add back an + // additional offset to (in effect) restore the proper origin and + // advance of the glyph. + + if ( character_rotation_z_ != 0. ) { + FT_Matrix rotation_matrix; + FT_Vector sinus; + + FT_Vector_Unit( &sinus, (FT_Angle)(character_rotation_z_ * 0x10000L) ); + + rotation_matrix.xx = sinus.x; + rotation_matrix.xy = -sinus.y; + rotation_matrix.yx = sinus.y; + rotation_matrix.yy = sinus.x; + + FT_Vector original_offset, rotation_offset; + + original_offset.x = ( face->glyph->metrics.width / 2 + + face->glyph->metrics.horiBearingX ) / 64 * 0x10000L; + original_offset.y = (FT_Pos)(rotation_offset_y_ * 0x10000L); + + rotation_offset = original_offset; + + FT_Vector_Rotate( &rotation_offset, + (FT_Angle)(character_rotation_z_ * 0x10000L) ); + + rotation_offset.x = original_offset.x - rotation_offset.x; + rotation_offset.y = original_offset.y - rotation_offset.y; + + rotation_offset.x /= 1024; + rotation_offset.y /= 1024; + + error = FT_Glyph_Transform( glyph, &rotation_matrix, &rotation_offset ); + } + + error = FT_Glyph_To_Bitmap( &glyph, ft_render_mode_mono, 0, 1 ); + + if ( error != 0 ) { + FT_Done_Glyph( glyph ); + return; + } + + FT_BitmapGlyph bitmap_glyph = (FT_BitmapGlyph)glyph; + + // Evidently, in FreeType2, you can only get "upside-down" bitmaps and + // OpenGL won't invert a bitmap with PixelZoom, so we have to invert the + // glyph's bitmap ourselves. + + GLubyte* inverted_bitmap = invertBitmap( bitmap_glyph->bitmap ); + + glBitmap( bitmap_glyph->bitmap.width, bitmap_glyph->bitmap.rows, + (GLfloat)-bitmap_glyph->left, + (GLfloat)( bitmap_glyph->bitmap.rows - bitmap_glyph->top ), + face->glyph->advance.x / 64.f, + face->glyph->advance.y / 64.f, + inverted_bitmap ); + + FT_Done_Glyph( glyph ); + + delete[] inverted_bitmap; + } + + Grayscale::Grayscale ( const char* filename, float point_size, + FT_UInt resolution ) + : Raster( filename, point_size, resolution ) + {} + + Grayscale::Grayscale ( const FT_Byte* data_base, const FT_Long data_size, + float point_size, FT_UInt resolution ) + : Raster( data_base, data_size, point_size, resolution ) + {} + + Grayscale::Grayscale ( FT_Face face, float point_size, FT_UInt resolution ) + : Raster( face, point_size, resolution ) + {} + + Grayscale::~Grayscale ( void ) + {} + + GLubyte* Grayscale::invertPixmap ( const FT_Bitmap& bitmap ) + { + GLubyte* inverse = new GLubyte[ bitmap.rows * bitmap.pitch ]; + GLubyte* inverse_ptr = inverse; + + for ( int r = 0; r < bitmap.rows; r++ ) { + + GLubyte* bitmap_ptr = &bitmap.buffer[bitmap.pitch * ( bitmap.rows - r - 1 )]; + + for ( int p = 0; p < bitmap.pitch; p++ ) { + *inverse_ptr++ = *bitmap_ptr++; + } + } + + return inverse; + } + + void Grayscale::renderGlyph ( FT_Face face, FT_UInt glyph_index ) + { + FT_Error error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT ); + + if ( error != 0 ) + return; + + FT_Glyph original_glyph; + FT_Glyph glyph; + + error = FT_Get_Glyph( face->glyph, &original_glyph ); + + if ( error != 0 ) return; + + error = FT_Glyph_Copy( original_glyph, &glyph ); + + FT_Done_Glyph( original_glyph ); + + if ( error != 0 ) return; + + if ( character_rotation_z_ != 0. ) { + FT_Matrix rotation_matrix; + FT_Vector sinus; + + FT_Vector_Unit( &sinus, (FT_Angle)(character_rotation_z_ * 0x10000L) ); + + rotation_matrix.xx = sinus.x; + rotation_matrix.xy = -sinus.y; + rotation_matrix.yx = sinus.y; + rotation_matrix.yy = sinus.x; + + FT_Vector original_offset, rotation_offset; + + original_offset.x = ( face->glyph->metrics.width / 2 + + face->glyph->metrics.horiBearingX ) / 64 * 0x10000L; + original_offset.y = (FT_Pos)(rotation_offset_y_ * 0x10000L); + + rotation_offset = original_offset; + + FT_Vector_Rotate( &rotation_offset, + (FT_Angle)(character_rotation_z_ * 0x10000L) ); + + rotation_offset.x = original_offset.x - rotation_offset.x; + rotation_offset.y = original_offset.y - rotation_offset.y; + + rotation_offset.x /= 1024; + rotation_offset.y /= 1024; + + error = FT_Glyph_Transform( glyph, &rotation_matrix, &rotation_offset ); + } + + error = FT_Glyph_To_Bitmap( &glyph, ft_render_mode_normal, 0, 1 ); + + if ( error != 0 ) { + FT_Done_Glyph( glyph ); + return; + } + + FT_BitmapGlyph bitmap_glyph = (FT_BitmapGlyph)glyph; + + // Evidently, in FreeType2, you can only get "upside-down" bitmaps + // (this could be cured with PixelZoom, but that an additional function) + + GLubyte* inverted_pixmap = invertPixmap( bitmap_glyph->bitmap ); + + // :-( If this is compiled in a display list, it may or not be in effect + // later when the list is actually called. So, the client should be alerted + // to this fact: unpack alignment must be 1 + + glPushAttrib( GL_PIXEL_MODE_BIT ); + glPixelTransferf( GL_RED_SCALE, foreground_color_[R] - background_color_[R] ); + glPixelTransferf( GL_GREEN_SCALE, foreground_color_[G] - background_color_[G] ); + glPixelTransferf( GL_BLUE_SCALE, foreground_color_[B] - background_color_[B] ); + glPixelTransferf( GL_ALPHA_SCALE, foreground_color_[A] ); + glPixelTransferf( GL_RED_BIAS, background_color_[R] ); + glPixelTransferf( GL_GREEN_BIAS, background_color_[G] ); + glPixelTransferf( GL_BLUE_BIAS, background_color_[B] ); + glPixelTransferf( GL_ALPHA_BIAS, background_color_[A] ); + + glBitmap( 0, 0, 0, 0, + (GLfloat)bitmap_glyph->left, + (GLfloat)( bitmap_glyph->top - bitmap_glyph->bitmap.rows ), + 0 ); + + glDrawPixels( bitmap_glyph->bitmap.width, bitmap_glyph->bitmap.rows, + GL_LUMINANCE, GL_UNSIGNED_BYTE, + inverted_pixmap ); + + // This is how you advance the raster position when drawing PIXMAPS + // (without querying the state) + + glBitmap( 0, 0, 0, 0, + (GLfloat)( -bitmap_glyph->left + face->glyph->advance.x / 64.f ), + (GLfloat)( bitmap_glyph->bitmap.rows - bitmap_glyph->top + + face->glyph->advance.y / 64. ), + 0 ); + + FT_Done_Glyph( glyph ); + + glPopAttrib(); + + delete[] inverted_pixmap; + } + + Translucent::Translucent ( const char* filename, float point_size, + FT_UInt resolution ) + : Raster( filename, point_size, resolution ) + {} + + Translucent::Translucent ( const FT_Byte* data_base, const FT_Long data_size, + float point_size, FT_UInt resolution ) + : Raster( data_base, data_size, point_size, resolution ) + {} + + Translucent::Translucent ( FT_Face face, float point_size, FT_UInt resolution ) + : Raster( face, point_size, resolution ) + {} + + Translucent::~Translucent ( void ) + {} + + // The simplest format which glDrawPixels can render with (varying) transparency + // is GL_LUMINANCE_ALPHA; so, we take the grayscale bitmap from FreeType + // and treat all non-zero values as full luminance (basically the mask for + // rendering) and duplicate the grayscale values as alpha values + // (as well as turn it upside-down). + + GLubyte* Translucent::invertPixmapWithAlpha ( const FT_Bitmap& bitmap ) + { + GLubyte* inverse = new GLubyte[ 2 * bitmap.rows * bitmap.pitch ]; + GLubyte* inverse_ptr = inverse; + + for ( int r = 0; r < bitmap.rows; r++ ) { + + GLubyte* bitmap_ptr = &bitmap.buffer[bitmap.pitch * ( bitmap.rows - r - 1 )]; + + for ( int p = 0; p < bitmap.pitch; p++ ) { + *inverse_ptr++ = *bitmap_ptr ? 255 : 0; + *inverse_ptr++ = *bitmap_ptr++; + } + } + + return inverse; + } + + void Translucent::renderGlyph ( FT_Face face, FT_UInt glyph_index ) + { + FT_Error error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT ); + + if ( error != 0 ) + return; + + FT_Glyph original_glyph; + FT_Glyph glyph; + + error = FT_Get_Glyph( face->glyph, &original_glyph ); + + if ( error != 0 ) return; + + error = FT_Glyph_Copy( original_glyph, &glyph ); + + FT_Done_Glyph( original_glyph ); + + if ( error != 0 ) return; + + if ( character_rotation_z_ != 0. ) { + FT_Matrix rotation_matrix; + FT_Vector sinus; + + FT_Vector_Unit( &sinus, (FT_Angle)(character_rotation_z_ * 0x10000L) ); + + rotation_matrix.xx = sinus.x; + rotation_matrix.xy = -sinus.y; + rotation_matrix.yx = sinus.y; + rotation_matrix.yy = sinus.x; + + FT_Vector original_offset, rotation_offset; + + original_offset.x = ( face->glyph->metrics.width / 2 + + face->glyph->metrics.horiBearingX ) / 64 * 0x10000L; + original_offset.y = (FT_Pos)(rotation_offset_y_ * 0x10000L); + + rotation_offset = original_offset; + + FT_Vector_Rotate( &rotation_offset, + (FT_Angle)(character_rotation_z_ * 0x10000L) ); + + rotation_offset.x = original_offset.x - rotation_offset.x; + rotation_offset.y = original_offset.y - rotation_offset.y; + + rotation_offset.x /= 1024; + rotation_offset.y /= 1024; + + error = FT_Glyph_Transform( glyph, &rotation_matrix, &rotation_offset ); + } + + error = FT_Glyph_To_Bitmap( &glyph, ft_render_mode_normal, 0, 1 ); + + if ( error != 0 ) { + FT_Done_Glyph( glyph ); + return; + } + + FT_BitmapGlyph bitmap_glyph = (FT_BitmapGlyph)glyph; + + // Evidently, in FreeType2, you can only get "upside-down" bitmaps. For + // translucency, the grayscale bitmap generated by FreeType is expanded + // to include an alpha value (and the non-zero values of the + // grayscale bitmap are saturated to provide a "mask" of the glyph). + + GLubyte* inverted_pixmap = invertPixmapWithAlpha( bitmap_glyph->bitmap ); + + glPushAttrib( GL_PIXEL_MODE_BIT ); + glPixelTransferf( GL_RED_SCALE, foreground_color_[R] - background_color_[R] ); + glPixelTransferf( GL_GREEN_SCALE, foreground_color_[G] -background_color_[G] ); + glPixelTransferf( GL_BLUE_SCALE, foreground_color_[B] - background_color_[B] ); + glPixelTransferf( GL_ALPHA_SCALE, foreground_color_[A] ); + glPixelTransferf( GL_RED_BIAS, background_color_[R] ); + glPixelTransferf( GL_GREEN_BIAS, background_color_[G] ); + glPixelTransferf( GL_BLUE_BIAS, background_color_[B] ); + glPixelTransferf( GL_ALPHA_BIAS, background_color_[A] ); + + // Set the proper raster position for rendering this glyph (why doesn't + // OpenGL have a similar function for pixmaps?) + + glBitmap( 0, 0, 0, 0, + (GLfloat)bitmap_glyph->left, + (GLfloat)( bitmap_glyph->top - bitmap_glyph->bitmap.rows ), + 0 ); + + glDrawPixels( bitmap_glyph->bitmap.width, bitmap_glyph->bitmap.rows, + GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, + inverted_pixmap ); + + // This is how you advance the raster position when drawing PIXMAPS + // (without querying the state) + + glBitmap( 0, 0, 0, 0, + -bitmap_glyph->left + face->glyph->advance.x / 64.f, + bitmap_glyph->bitmap.rows - bitmap_glyph->top + + face->glyph->advance.y / 64.f, + 0 ); + + FT_Done_Glyph( glyph ); + + glPopAttrib(); + + delete[] inverted_pixmap; + } + + Polygonal::Polygonal ( const char* filename, float point_size, FT_UInt resolution ) + : Face( filename, point_size, resolution ) + { + if ( !isValid() ) return; + + init(); + } + + Polygonal::Polygonal ( const FT_Byte* data_base, const FT_Long data_size, float point_size, FT_UInt resolution) + : Face( data_base, data_size, point_size, resolution ) + { + if ( !isValid() ) return; + + init(); + } + + Polygonal::Polygonal ( FT_Face face, float point_size, FT_UInt resolution ) + : Face( face, point_size, resolution ) + { + init(); + } + + void Polygonal::init ( void ) + { + character_rotation_.active_ = false; + character_rotation_.x_ = 0; + character_rotation_.y_ = 0; + character_rotation_.z_ = 0; + + tessellation_steps_ = DEFAULT_TESSELLATION_STEPS; + + delta_ = 1. / (double)tessellation_steps_; + delta2_ = delta_ * delta_; + delta3_ = delta2_ * delta_; + + // For vector rendition modes, FreeType is allowed to generate the + // lines and arcs at the original face definition resolution. To + // get to the proper glyph size, the vertices are scaled before + // they're passed to the GLU tessellation routines. + + if ( resolution_ != 0 ) + vector_scale_ = ( point_size_ * resolution_ ) / + (float)( faces_.front().face_->units_per_EM * 72 ); + else // According to the FreeType documentation, resolution == 0 -> 72 DPI + vector_scale_ = ( point_size_ ) / + (float)( faces_.front().face_->units_per_EM ); + + color_tess_ = 0; + texture_tess_ = 0; + + setCharSize(); + + // Can't call this until a valid character size is set! + + setCharacterRotationReference( 'o' ); + } + + Polygonal::~Polygonal ( void ) + { + clearCaches(); + } + + // Note: Changing the color tessellation object also clears the + // display list cache + + void Polygonal::setColorTess ( ColorTess* color_tess ) + { + color_tess_ = color_tess; + + clearCaches(); + } + + // Note: Changing the texture coordinate tessellation object also + // clears the display list cache + + void Polygonal::setTextureTess ( TextureTess* texture_tess ) + { + texture_tess_ = texture_tess; + + clearCaches(); + } + + // Note: Changing the appoximation steps also clears the display list cache + + void Polygonal::setTessellationSteps ( unsigned int tessellation_steps ) + { + if ( tessellation_steps != tessellation_steps_ ) { + + tessellation_steps_ = tessellation_steps; + + delta_ = 1. / (double)tessellation_steps_; + delta2_ = delta_ * delta_; + delta3_ = delta2_ * delta_; + + clearCaches(); + } + } + + // Note: Changing the character rotation also clears the display list cache. + + void Polygonal::setCharacterRotationX ( GLfloat character_rotation_x ) + { + if ( character_rotation_x != character_rotation_.x_ ) { + character_rotation_.x_ = character_rotation_x; + + if ( character_rotation_.x_ != 0. || character_rotation_.y_ != 0. || + character_rotation_.z_ != 0. ) + character_rotation_.active_ = true; + else + character_rotation_.active_ = false; + + clearCaches(); + } + } + + void Polygonal::setCharacterRotationY ( GLfloat character_rotation_y ) + { + if ( character_rotation_y != character_rotation_.y_ ) { + character_rotation_.y_ = character_rotation_y; + + if ( character_rotation_.x_ != 0. || character_rotation_.y_ != 0. || + character_rotation_.z_ != 0. ) + character_rotation_.active_ = true; + else + character_rotation_.active_ = false; + + clearCaches(); + } + } + + void Polygonal::setCharacterRotationZ ( GLfloat character_rotation_z ) + { + if ( character_rotation_z != character_rotation_.z_ ) { + character_rotation_.z_ = character_rotation_z; + + if ( character_rotation_.x_ != 0. || character_rotation_.y_ != 0. || + character_rotation_.z_ != 0. ) + character_rotation_.active_ = true; + else + character_rotation_.active_ = false; + + clearCaches(); + } + } + + void Polygonal::setCharSize ( void ) + { + for ( unsigned int i = 0; i < faces_.size(); i++ ) { + FT_Error error = FT_Set_Char_Size( faces_[i].face_, + 0, + faces_[i].face_->units_per_EM * 64, + 0, + 0 ); + if ( error != 0 ) return; + } + + if ( rotation_reference_glyph_ != 0 ) + setRotationOffset(); + } + + void Polygonal::setRotationOffset ( void ) + { + FT_Error error = FT_Load_Glyph( rotation_reference_face_, + rotation_reference_glyph_, + FT_LOAD_RENDER ); + + if ( error != 0 ) + return; + + vector_scale_ = ( point_size_ * resolution_ ) / + ( 72.f * rotation_reference_face_->units_per_EM ); + + rotation_offset_y_ = + ( rotation_reference_face_->glyph->metrics.horiBearingY / 2.f ) / 64.f + * vector_scale_; + } + + double Polygonal::height ( void ) const + { + if ( faces_[0].face_->height > 0 ) + return ( faces_[0].face_->height * point_size_ * resolution_ ) / + ( 72. * faces_[0].face_->units_per_EM ); + else + return ( faces_[0].face_->size->metrics.y_ppem * point_size_ * resolution_ ) / + ( 72. * faces_[0].face_->units_per_EM ); + } + + BBox Polygonal::measure ( unsigned char c ) + { + BBox bbox; + // For starters, just get the unscaled glyph bounding box + unsigned int f; + FT_UInt glyph_index = 0; + + for ( f = 0; f < faces_.size(); f++ ) { + glyph_index = FT_Get_Char_Index( faces_[f].face_, c ); + if ( glyph_index != 0 ) break; + } + + if ( glyph_index == 0 ) + return bbox; + + FT_Error error = FT_Load_Glyph( faces_[f].face_, glyph_index, + FT_LOAD_DEFAULT ); + if ( error != 0 ) + return bbox; + + FT_Glyph glyph; + error = FT_Get_Glyph( faces_[f].face_->glyph, &glyph ); + if ( error != 0 ) + return bbox; + + FT_BBox ft_bbox; + FT_Glyph_Get_CBox( glyph, ft_glyph_bbox_unscaled, &ft_bbox ); + + FT_Done_Glyph( glyph ); + + bbox = ft_bbox; + bbox.advance_ = faces_[f].face_->glyph->advance; + + bbox *= + ( point_size_ * resolution_ ) / ( 72.f * faces_[f].face_->units_per_EM ); + + return bbox; + } +#ifndef OGLFT_NO_QT + BBox Polygonal::measure ( const QChar c ) + { + BBox bbox; + // For starters, just get the unscaled glyph bounding box + unsigned int f; + FT_UInt glyph_index = 0; + + for ( f = 0; f < faces_.size(); f++ ) { + glyph_index = FT_Get_Char_Index( faces_[f].face_, c.unicode() ); + if ( glyph_index != 0 ) break; + } + + if ( glyph_index == 0 ) + return bbox; + + FT_Error error = FT_Load_Glyph( faces_[f].face_, glyph_index, + FT_LOAD_DEFAULT ); + if ( error != 0 ) + return bbox; + + FT_Glyph glyph; + error = FT_Get_Glyph( faces_[f].face_->glyph, &glyph ); + if ( error != 0 ) + return bbox; + + FT_BBox ft_bbox; + FT_Glyph_Get_CBox( glyph, ft_glyph_bbox_unscaled, &ft_bbox ); + + FT_Done_Glyph( glyph ); + + bbox = ft_bbox; + bbox.advance_ = faces_[f].face_->glyph->advance; + + bbox *= ( point_size_ * resolution_ ) / ( 72. * faces_[f].face_->units_per_EM ); + + return bbox; + } +#endif /* OGLFT_NO_QT */ + + GLuint Polygonal::compileGlyph ( FT_Face face, FT_UInt glyph_index ) + { + GLuint dlist = glGenLists( 1 ); + + glNewList( dlist, GL_COMPILE ); + + renderGlyph( face, glyph_index ); + + glEndList( ); + + return dlist; + } + + void Polygonal::clearCaches ( void ) + { + GDLI fgi = glyph_dlists_.begin(); + + for ( ; fgi != glyph_dlists_.end(); ++fgi ) { + glDeleteLists( fgi->second, 1 ); + } + + glyph_dlists_.clear(); + } + + Outline::Outline ( const char* filename, float point_size, FT_UInt resolution ) + : Polygonal( filename, point_size, resolution ) + { + if ( !isValid() ) return; + + init(); + } + + Outline::Outline ( const FT_Byte* data_base, const FT_Long data_size, float point_size, FT_UInt resolution) + : Polygonal( data_base, data_size, point_size, resolution ) + { + if ( !isValid() ) return; + + init(); + } + + Outline::Outline ( FT_Face face, float point_size, FT_UInt resolution ) + : Polygonal( face, point_size, resolution ) + { + init(); + } + + void Outline::init ( void ) + { + interface_.move_to = (FT_Outline_MoveTo_Func)moveToCallback; + interface_.line_to = (FT_Outline_LineTo_Func)lineToCallback; + interface_.conic_to = (FT_Outline_ConicTo_Func)conicToCallback; + interface_.cubic_to = (FT_Outline_CubicTo_Func)cubicToCallback; + interface_.shift = 0; + interface_.delta = 0; + } + + Outline::~Outline ( void ) + {} + + void Outline::renderGlyph ( FT_Face face, FT_UInt glyph_index ) + { + FT_Error error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT ); + + if ( error != 0 ) + return; + + FT_Glyph g; + + error = FT_Get_Glyph( face->glyph, &g ); + + if ( error != 0 || g->format != FT_GLYPH_FORMAT_OUTLINE ) + return; + + vector_scale_ = ( point_size_ * resolution_ ) / + ( 72.f * face->units_per_EM ); + + if ( character_rotation_.active_ ) { + glPushMatrix(); + glTranslatef( ( face->glyph->metrics.width / 2.f + + face->glyph->metrics.horiBearingX ) / 64.f + * vector_scale_, + rotation_offset_y_, + 0.f ); + + if ( character_rotation_.x_ != 0. ) + glRotatef( character_rotation_.x_, 1., 0., 0. ); + + if ( character_rotation_.y_ != 0. ) + glRotatef( character_rotation_.y_, 0., 1., 0. ); + + if ( character_rotation_.z_ != 0. ) + glRotatef( character_rotation_.z_, 0., 0., 1. ); + + glTranslatef( -( face->glyph->metrics.width / 2.f + + face->glyph->metrics.horiBearingX ) / 64.f + * vector_scale_, + -rotation_offset_y_, + 0.f ); + } + + contour_open_ = false; + + // The Big Kahuna: the FreeType glyph decomposition routine traverses + // the outlines of the font by calling the various routines stored in + // outline_interface_. These routines in turn call the GL vertex routines. + + error = FT_Outline_Decompose( &((FT_OutlineGlyph)g)->outline, + &interface_, this ); + + FT_Done_Glyph( (FT_Glyph)g ); + + // Some glyphs may be empty (the 'blank' for instance!) + + if ( contour_open_ ) + glEnd( ); + + + if ( character_rotation_.active_ ) { + glPopMatrix(); + } + + // Drawing a character always advances the MODELVIEW. + + glTranslatef( face->glyph->advance.x / 64.f * vector_scale_, + face->glyph->advance.y / 64.f * vector_scale_, + 0.f ); + + for ( VILI vili = vertices_.begin(); vili != vertices_.end(); vili++ ) + delete *vili; + + vertices_.clear(); + } + + int Outline::moveToCallback ( FT_Vector* to, Outline* outline ) + { + if ( outline->contour_open_ ) { + glEnd(); + } + + outline->last_vertex_ = VertexInfo( to, + outline->colorTess(), + outline->textureTess() ); + + glBegin( GL_LINE_LOOP ); + + outline->contour_open_ = true; + + return 0; + } + + int Outline::lineToCallback ( FT_Vector* to, Outline* outline ) + { + outline->last_vertex_ = VertexInfo( to, + outline->colorTess(), + outline->textureTess() ); + GLdouble g[2]; + + g[X] = outline->last_vertex_.v_[X] * outline->vector_scale_; + g[Y] = outline->last_vertex_.v_[Y] * outline->vector_scale_; + + glVertex2dv( g ); + + return 0; + } + + int Outline::conicToCallback ( FT_Vector* control, FT_Vector* to, Outline* outline ) + { + // This is crude: Step off conics with a fixed number of increments + + VertexInfo to_vertex( to, outline->colorTess(), outline->textureTess() ); + VertexInfo control_vertex( control, outline->colorTess(), outline->textureTess() ); + + double b[2], c[2], d[2], f[2], df[2], d2f[2]; + GLdouble g[3]; + + g[Z] = 0.; + + b[X] = outline->last_vertex_.v_[X] - 2 * control_vertex.v_[X] + + to_vertex.v_[X]; + b[Y] = outline->last_vertex_.v_[Y] - 2 * control_vertex.v_[Y] + + to_vertex.v_[Y]; + + c[X] = -2 * outline->last_vertex_.v_[X] + 2 * control_vertex.v_[X]; + c[Y] = -2 * outline->last_vertex_.v_[Y] + 2 * control_vertex.v_[Y]; + + d[X] = outline->last_vertex_.v_[X]; + d[Y] = outline->last_vertex_.v_[Y]; + + f[X] = d[X]; + f[Y] = d[Y]; + df[X] = c[X] * outline->delta_ + b[X] * outline->delta2_; + df[Y] = c[Y] * outline->delta_ + b[Y] * outline->delta2_; + d2f[X] = 2 * b[X] * outline->delta2_; + d2f[Y] = 2 * b[Y] * outline->delta2_; + + for ( unsigned int i = 0; i < outline->tessellation_steps_-1; i++ ) { + + f[X] += df[X]; + f[Y] += df[Y]; + + g[X] = f[X] * outline->vector_scale_; + g[Y] = f[Y] * outline->vector_scale_; + + if ( outline->colorTess() ) + glColor4fv( outline->colorTess()->color( g ) ); + + glVertex2dv( g ); + + df[X] += d2f[X]; + df[Y] += d2f[Y]; + } + + g[X] = to_vertex.v_[X] * outline->vector_scale_; + g[Y] = to_vertex.v_[Y] * outline->vector_scale_; + + if ( outline->colorTess() ) + glColor4fv( outline->colorTess()->color( g ) ); + + glVertex2dv( g ); + + outline->last_vertex_ = to_vertex; + + return 0; + } + + int Outline::cubicToCallback ( FT_Vector* control1, FT_Vector* control2, + FT_Vector* to, Outline* outline ) + { + // This is crude: Step off cubics with a fixed number of increments + + VertexInfo to_vertex( to, outline->colorTess(), outline->textureTess() ); + VertexInfo control1_vertex( control1, outline->colorTess(), outline->textureTess() ); + VertexInfo control2_vertex( control2, outline->colorTess(), outline->textureTess() ); + + double a[2], b[2], c[2], d[2], f[2], df[2], d2f[2], d3f[2]; + GLdouble g[3]; + + g[Z] = 0.; + + a[X] = -outline->last_vertex_.v_[X] + 3 * control1_vertex.v_[X] + -3 * control2_vertex.v_[X] + to_vertex.v_[X]; + a[Y] = -outline->last_vertex_.v_[Y] + 3 * control1_vertex.v_[Y] + -3 * control2_vertex.v_[Y] + to_vertex.v_[Y]; + + b[X] = 3 * outline->last_vertex_.v_[X] - 6 * control1_vertex.v_[X] + + 3 * control2_vertex.v_[X]; + b[Y] = 3 * outline->last_vertex_.v_[Y] - 6 * control1_vertex.v_[Y] + + 3 * control2_vertex.v_[Y]; + + c[X] = -3 * outline->last_vertex_.v_[X] + 3 * control1_vertex.v_[X]; + c[Y] = -3 * outline->last_vertex_.v_[Y] + 3 * control1_vertex.v_[Y]; + + d[X] = outline->last_vertex_.v_[X]; + d[Y] = outline->last_vertex_.v_[Y]; + + f[X] = d[X]; + f[Y] = d[Y]; + df[X] = c[X] * outline->delta_ + b[X] * outline->delta2_ + + a[X] * outline->delta3_; + df[Y] = c[Y] * outline->delta_ + b[Y] * outline->delta2_ + + a[Y] * outline->delta3_; + d2f[X] = 2 * b[X] * outline->delta2_ + 6 * a[X] * outline->delta3_; + d2f[Y] = 2 * b[Y] * outline->delta2_ + 6 * a[Y] * outline->delta3_; + d3f[X] = 6 * a[X] * outline->delta3_; + d3f[Y] = 6 * a[Y] * outline->delta3_; + + for ( unsigned int i = 0; i < outline->tessellation_steps_-1; i++ ) { + + f[X] += df[X]; + f[Y] += df[Y]; + + g[X] = f[X] * outline->vector_scale_; + g[Y] = f[Y] * outline->vector_scale_; + + if ( outline->colorTess() ) + glColor4fv( outline->colorTess()->color( g ) ); + + glVertex2dv( g ); + + df[X] += d2f[X]; + df[Y] += d2f[Y]; + d2f[X] += d3f[X]; + d2f[Y] += d3f[Y]; + } + + g[X] = to_vertex.v_[X] * outline->vector_scale_; + g[Y] = to_vertex.v_[Y] * outline->vector_scale_; + + if ( outline->colorTess() ) + glColor4fv( outline->colorTess()->color( g ) ); + + glVertex2dv( g ); + + outline->last_vertex_ = to_vertex; + + return 0; + } + + Filled::Filled ( const char* filename, float point_size, FT_UInt resolution ) + : Polygonal( filename, point_size, resolution ) + { + if ( !isValid() ) return; + + init(); + } + + Filled::Filled ( const FT_Byte* data_base, const FT_Long data_size, + float point_size, FT_UInt resolution ) + : Polygonal( data_base, data_size, point_size, resolution ) + { + if ( !isValid() ) return; + + init(); + } + + Filled::Filled ( FT_Face face, float point_size, FT_UInt resolution ) + : Polygonal( face, point_size, resolution ) + { + init(); + } + + void Filled::init ( void ) + { + depth_offset_ = 0; + + interface_.move_to = (FT_Outline_MoveTo_Func)moveToCallback; + interface_.line_to = (FT_Outline_LineTo_Func)lineToCallback; + interface_.conic_to = (FT_Outline_ConicTo_Func)conicToCallback; + interface_.cubic_to = (FT_Outline_CubicTo_Func)cubicToCallback; + interface_.shift = 0; + interface_.delta = 0; + + tess_obj_ = gluNewTess(); + +#if defined(WIN32) + typedef void (CALLBACK*(CB))(); +#else + typedef GLUTessCallback CB; +#endif + gluTessCallback( tess_obj_, GLU_TESS_VERTEX, CB(vertexCallback) ); + gluTessCallback( tess_obj_, GLU_TESS_BEGIN, CB(beginCallback) ); + gluTessCallback( tess_obj_, GLU_TESS_END, CB(endCallback) ); + gluTessCallback( tess_obj_, GLU_TESS_COMBINE_DATA, CB(combineCallback) ); + gluTessCallback( tess_obj_, GLU_TESS_ERROR, CB(errorCallback) ); + } + + Filled::~Filled ( void ) + { + gluDeleteTess( tess_obj_ ); + } + + void Filled::renderGlyph ( FT_Face face, FT_UInt glyph_index ) + { + FT_Error error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT ); + + if ( error != 0 ) + return; + + FT_Glyph g; + + error = FT_Get_Glyph( face->glyph, &g ); + + if ( error != 0 || g->format != FT_GLYPH_FORMAT_OUTLINE ) + return; + + vector_scale_ = ( point_size_ * resolution_ ) / + ( 72.f * face->units_per_EM ); + + if ( character_rotation_.active_ ) { + glPushMatrix(); + glTranslatef( ( face->glyph->metrics.width / 2.f + + face->glyph->metrics.horiBearingX ) / 64.f + * vector_scale_, + rotation_offset_y_, + 0. ); + + if ( character_rotation_.x_ != 0. ) + glRotatef( character_rotation_.x_, 1., 0., 0. ); + + if ( character_rotation_.y_ != 0. ) + glRotatef( character_rotation_.y_, 0., 1., 0. ); + + if ( character_rotation_.z_ != 0. ) + glRotatef( character_rotation_.z_, 0., 0., 1. ); + + glTranslatef( -( face->glyph->metrics.width / 2.f + + face->glyph->metrics.horiBearingX ) / 64.f + * vector_scale_, + -rotation_offset_y_, + 0.f ); + } + + if ( depth_offset_ != 0. ) { + glPushMatrix(); + glTranslatef( 0., 0., depth_offset_ ); + glNormal3f( 0., 0., 1. ); + } + else { + glNormal3f( 0., 0., -1. ); + } + + glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); + + contour_open_ = false; + + gluTessBeginPolygon( tess_obj_, this ); + + // The Big Kahuna: the FreeType glyph decomposition routine traverses + // the outlines of the font by calling the various routines stored in + // interface_. These routines in turn call the GLU tessellation routines + // to create OGL polygons. + + error = FT_Outline_Decompose( &((FT_OutlineGlyph)g)->outline, + &interface_, this ); + + FT_Done_Glyph( (FT_Glyph)g ); + + // Some glyphs may be empty (the 'blank' for instance!) + + if ( contour_open_ ) + gluTessEndContour( tess_obj_ ); + + gluTessEndPolygon( tess_obj_ ); + + if ( depth_offset_ != 0. ) { + glPopMatrix(); + } + if ( character_rotation_.active_ ) { + glPopMatrix(); + } + + // Drawing a character always advances the MODELVIEW. + + glTranslatef( face->glyph->advance.x / 64 * vector_scale_, + face->glyph->advance.y / 64 * vector_scale_, + 0. ); + + for ( VILI vili = extra_vertices_.begin(); vili != extra_vertices_.end(); vili++ ) + delete *vili; + + extra_vertices_.clear(); + + for ( VILI vili = vertices_.begin(); vili != vertices_.end(); vili++ ) + delete *vili; + + vertices_.clear(); + } + + int Filled::moveToCallback ( FT_Vector* to, Filled* filled ) + { + if ( filled->contour_open_ ) { + gluTessEndContour( filled->tess_obj_ ); + } + + filled->last_vertex_ = VertexInfo( to, filled->colorTess(), filled->textureTess() ); + + gluTessBeginContour( filled->tess_obj_ ); + + filled->contour_open_ = true; + + return 0; + } + + int Filled::lineToCallback ( FT_Vector* to, Filled* filled ) + { + filled->last_vertex_ = VertexInfo( to, filled->colorTess(), filled->textureTess() ); + + VertexInfo* vertex = new VertexInfo( to, filled->colorTess(), filled->textureTess() ); + + vertex->v_[X] *= filled->vector_scale_; + vertex->v_[Y] *= filled->vector_scale_; + + gluTessVertex( filled->tess_obj_, vertex->v_, vertex ); + + filled->vertices_.push_back( vertex ); + + return 0; + } + + int Filled::conicToCallback ( FT_Vector* control, FT_Vector* to, Filled* filled ) + { + // This is crude: Step off conics with a fixed number of increments + + VertexInfo to_vertex( to, filled->colorTess(), filled->textureTess() ); + VertexInfo control_vertex( control, filled->colorTess(), filled->textureTess() ); + + double b[2], c[2], d[2], f[2], df[2], d2f[2]; + + b[X] = filled->last_vertex_.v_[X] - 2 * control_vertex.v_[X] + + to_vertex.v_[X]; + b[Y] = filled->last_vertex_.v_[Y] - 2 * control_vertex.v_[Y] + + to_vertex.v_[Y]; + + c[X] = -2 * filled->last_vertex_.v_[X] + 2 * control_vertex.v_[X]; + c[Y] = -2 * filled->last_vertex_.v_[Y] + 2 * control_vertex.v_[Y]; + + d[X] = filled->last_vertex_.v_[X]; + d[Y] = filled->last_vertex_.v_[Y]; + + f[X] = d[X]; + f[Y] = d[Y]; + df[X] = c[X] * filled->delta_ + b[X] * filled->delta2_; + df[Y] = c[Y] * filled->delta_ + b[Y] * filled->delta2_; + d2f[X] = 2 * b[X] * filled->delta2_; + d2f[Y] = 2 * b[Y] * filled->delta2_; + + for ( unsigned int i = 0; i < filled->tessellation_steps_-1; i++ ) { + + f[X] += df[X]; + f[Y] += df[Y]; + + VertexInfo* vertex = new VertexInfo( f, filled->colorTess(), filled->textureTess() ); + + vertex->v_[X] *= filled->vector_scale_; + vertex->v_[Y] *= filled->vector_scale_; + + filled->vertices_.push_back( vertex ); + + gluTessVertex( filled->tess_obj_, vertex->v_, vertex ); + + df[X] += d2f[X]; + df[Y] += d2f[Y]; + } + + VertexInfo* vertex = new VertexInfo( to, filled->colorTess(), filled->textureTess() ); + + vertex->v_[X] *= filled->vector_scale_; + vertex->v_[Y] *= filled->vector_scale_; + + filled->vertices_.push_back( vertex ); + + gluTessVertex( filled->tess_obj_, vertex->v_, vertex ); + + filled->last_vertex_ = to_vertex; + + return 0; + } + + int Filled::cubicToCallback ( FT_Vector* control1, FT_Vector* control2, + FT_Vector* to, Filled* filled ) + { + // This is crude: Step off cubics with a fixed number of increments + + VertexInfo to_vertex( to, filled->colorTess(), filled->textureTess() ); + VertexInfo control1_vertex( control1, filled->colorTess(), filled->textureTess() ); + VertexInfo control2_vertex( control2, filled->colorTess(), filled->textureTess() ); + + double a[2], b[2], c[2], d[2], f[2], df[2], d2f[2], d3f[2]; + + a[X] = -filled->last_vertex_.v_[X] + 3 * control1_vertex.v_[X] + -3 * control2_vertex.v_[X] + to_vertex.v_[X]; + a[Y] = -filled->last_vertex_.v_[Y] + 3 * control1_vertex.v_[Y] + -3 * control2_vertex.v_[Y] + to_vertex.v_[Y]; + + b[X] = 3 * filled->last_vertex_.v_[X] - 6 * control1_vertex.v_[X] + + 3 * control2_vertex.v_[X]; + b[Y] = 3 * filled->last_vertex_.v_[Y] - 6 * control1_vertex.v_[Y] + + 3 * control2_vertex.v_[Y]; + + c[X] = -3 * filled->last_vertex_.v_[X] + 3 * control1_vertex.v_[X]; + c[Y] = -3 * filled->last_vertex_.v_[Y] + 3 * control1_vertex.v_[Y]; + + d[X] = filled->last_vertex_.v_[X]; + d[Y] = filled->last_vertex_.v_[Y]; + + f[X] = d[X]; + f[Y] = d[Y]; + df[X] = c[X] * filled->delta_ + b[X] * filled->delta2_ + + a[X] * filled->delta3_; + df[Y] = c[Y] * filled->delta_ + b[Y] * filled->delta2_ + + a[Y] * filled->delta3_; + d2f[X] = 2 * b[X] * filled->delta2_ + 6 * a[X] * filled->delta3_; + d2f[Y] = 2 * b[Y] * filled->delta2_ + 6 * a[Y] * filled->delta3_; + d3f[X] = 6 * a[X] * filled->delta3_; + d3f[Y] = 6 * a[Y] * filled->delta3_; + + for ( unsigned int i = 0; i < filled->tessellation_steps_-1; i++ ) { + + f[X] += df[X]; + f[Y] += df[Y]; + + VertexInfo* vertex = new VertexInfo( f, filled->colorTess(), filled->textureTess() ); + + vertex->v_[X] *= filled->vector_scale_; + vertex->v_[Y] *= filled->vector_scale_; + + filled->vertices_.push_back( vertex ); + + gluTessVertex( filled->tess_obj_, vertex->v_, vertex ); + + df[X] += d2f[X]; + df[Y] += d2f[Y]; + d2f[X] += d3f[X]; + d2f[Y] += d3f[Y]; + } + + VertexInfo* vertex = new VertexInfo( to, filled->colorTess(), filled->textureTess() ); + + vertex->v_[X] *= filled->vector_scale_; + vertex->v_[Y] *= filled->vector_scale_; + + filled->vertices_.push_back( vertex ); + + gluTessVertex( filled->tess_obj_, vertex->v_, vertex ); + + filled->last_vertex_ = to_vertex; + + return 0; + } + + void Filled::vertexCallback ( VertexInfo* vertex ) + { + if ( vertex->color_tess_ != 0 ) + glColor4fv( vertex->color_tess_->color( vertex->v_ ) ); + + if ( vertex->texture_tess_ != 0 ) + glTexCoord2fv( vertex->texture_tess_->texCoord( vertex->v_ ) ); + + glVertex3dv( vertex->v_ ); + } + + void Filled::beginCallback ( GLenum which ) + { + glBegin( which ); + } + + void Filled::endCallback ( void ) + { + glEnd(); + } + + void Filled::combineCallback ( GLdouble coords[3], void* vertex_data[4], + GLfloat weight[4], void** out_data, + Filled* filled ) + { + (void)vertex_data; + (void)weight; + // std::cerr << "called combine" << std::endl; + VertexInfo* vertex = new VertexInfo( coords ); + *out_data = vertex; + filled->extraVertices().push_back( vertex ); + } + + void Filled::errorCallback ( GLenum error_code ) + { + std::cerr << "hmm. error during tessellation?:" << gluErrorString( error_code ) << std::endl; + } + +#ifndef OGLFT_NO_SOLID + Solid::Solid ( const char* filename, float point_size, FT_UInt resolution ) + : Filled( filename, point_size, resolution ) + { + if ( !isValid() ) return; + + init(); + } + + Solid::Solid ( const FT_Byte* data_base, const FT_Long data_size, + float point_size, FT_UInt resolution ) + : Filled( data_base, data_size, point_size, resolution ) + { + if ( !isValid() ) return; + + init(); + } + + Solid::Solid ( FT_Face face, float point_size, FT_UInt resolution ) + : Filled( face, point_size, resolution ) + { + init(); + } + + void Solid::init ( void ) + { + interface_.move_to = (FT_Outline_MoveTo_Func)moveToCallback; + interface_.line_to = (FT_Outline_LineTo_Func)lineToCallback; + interface_.conic_to = (FT_Outline_ConicTo_Func)conicToCallback; + interface_.cubic_to = (FT_Outline_CubicTo_Func)cubicToCallback; + interface_.shift = 0; + interface_.delta = 0; + + // Set up for extrusion. Default depth is 1 (units of what?) + extrusion_.depth_ = 1.; + extrusion_.up_[X] = 0.; + extrusion_.up_[Y] = 1.; + extrusion_.up_[Z] = 0.; + extrusion_.n_polyline_pts_ = N_POLYLINE_PTS; + + assign( extrusion_.point_array_[0], 0., 0., extrusion_.depth_ + 1. ); + assign( extrusion_.point_array_[1], 0., 0., extrusion_.depth_ ); + assign( extrusion_.point_array_[2], 0., 0., 0. ); + assign( extrusion_.point_array_[3], 0., 0., -1. ); + + // Turn on closed contours and smooth vertices; turn off end capping + + gleSetJoinStyle( TUBE_JN_RAW | TUBE_CONTOUR_CLOSED | TUBE_NORM_EDGE ); + } + + Solid::~Solid ( void ) + {} + + // Note: as usual, setting this clears the caches + + void Solid::setDepth ( double depth ) + { + if ( depth > 0. && depth != extrusion_.depth_ ) { + extrusion_.depth_ = depth; + + assign( extrusion_.point_array_[0], 0., 0., extrusion_.depth_ + 1. ); + assign( extrusion_.point_array_[1], 0., 0., extrusion_.depth_ ); + + clearCaches(); + } + } + + void Solid::renderGlyph ( FT_Face face, FT_UInt glyph_index ) + { + FT_Error error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT ); + + if ( error != 0 ) + return; + + FT_OutlineGlyph g; + + error = FT_Get_Glyph( face->glyph, (FT_Glyph*)&g ); + + if ( error != 0 ) + return; + + vector_scale_ = ( point_size_ * resolution_ ) / ( 72. * face->units_per_EM ); + + if ( character_rotation_.active_ ) { + glPushMatrix(); + + glTranslatef( ( face->glyph->metrics.width / 2. + + face->glyph->metrics.horiBearingX ) / 64. + * vector_scale_, + rotation_offset_y_, + 0. ); + + if ( character_rotation_.x_ != 0. ) + glRotatef( character_rotation_.x_, 1., 0., 0. ); + + if ( character_rotation_.y_ != 0. ) + glRotatef( character_rotation_.y_, 0., 1., 0. ); + + if ( character_rotation_.z_ != 0. ) + glRotatef( character_rotation_.z_, 0., 0., 1. ); + + glTranslatef( -( face->glyph->metrics.width / 2. + + face->glyph->metrics.horiBearingX ) / 64. + * vector_scale_, + -rotation_offset_y_, + 0. ); + } + + contour_open_ = false; + + // In theory, TrueType contours are defined clockwise and Type1 contours + // are defined counter-clockwise. Trust the flag set by FreeType to + // indicate this since it is critical to getting the orientation of the + // surface normals correct. + if ( g->outline.flags & ft_outline_reverse_fill ) { + extrusion_.normal_sign_.x_ = -1; + extrusion_.normal_sign_.y_ = 1; + } + else { + extrusion_.normal_sign_.x_ = 1; + extrusion_.normal_sign_.y_ = -1; + } + // The Big Kahuna: the FreeType glyph decomposition routine traverses + // the outlines of the font by calling the various routines stored in + // extrude_interface_. These in turn call the gleExtrusion routine. + + error = FT_Outline_Decompose( &g->outline, &interface_, this ); + + FT_Done_Glyph( (FT_Glyph)g ); + + // Some glyphs may be empty (the 'blank' for instance!) + + if ( contour_open_ ) { + extrusion_.contour_normals_.push_back( extrusion_.contour_normals_.front() ); + + gleExtrusion( extrusion_.contour_.size(), + &extrusion_.contour_.begin()->p_, + &extrusion_.contour_normals_[1].p_, + extrusion_.up_, + extrusion_.n_polyline_pts_, + extrusion_.point_array_, + 0 ); + + extrusion_.contour_.clear(); + extrusion_.contour_normals_.clear(); + } + + if ( character_rotation_.active_ ) { + glPopMatrix(); + } + + // Apply the front and back faces of the solid character (recall that + // drawing a character advances the MODELVIEW, so defend against that + // with the stack operations) + + glPushMatrix(); + depth_offset_ = 0.; + Filled::renderGlyph( face, glyph_index ); + glPopMatrix(); + + glPushMatrix(); + depth_offset_ = extrusion_.depth_; + Filled::renderGlyph( face, glyph_index ); + glPopMatrix(); + + // Drawing a character always advances the MODELVIEW. + + glTranslatef( face->glyph->advance.x / 64. * vector_scale_, + face->glyph->advance.y / 64. * vector_scale_, + 0. ); + + for ( VILI vili = vertices_.begin(); vili != vertices_.end(); vili++ ) + delete *vili; + + vertices_.clear(); + } + + int Solid::moveToCallback ( FT_Vector* to, Solid* solid ) + { + if ( solid->contour_open_ ) { + + // A word of explanation: since you can't predict when the + // contour is going to end (its end is signaled by calling this + // routine, i.e., the contour ends when another is started + // abruptly), only the lineTo and arcTo functions generate contour + // points. The upshot is that the normals, which are computed for the + // current segment, are one behind the segment described in the + // the contour array. To make things match up at the end, the first + // normal is copied to the end of the normal array and the extrusion + // routine is passed the list of normals starting at the second entry. + + solid->extrusion_.contour_normals_. + push_back( solid->extrusion_.contour_normals_.front() ); +#if 1 + gleExtrusion( solid->extrusion_.contour_.size(), + &solid->extrusion_.contour_.begin()->p_, + &solid->extrusion_.contour_normals_[1].p_, + solid->extrusion_.up_, + solid->extrusion_.n_polyline_pts_, + solid->extrusion_.point_array_, + 0 ); +#endif + solid->extrusion_.contour_.clear(); + solid->extrusion_.contour_normals_.clear(); + } + + solid->last_vertex_ = VertexInfo( to, solid->colorTess(), solid->textureTess() ); + + solid->contour_open_ = true; + + return 0; + } + + int Solid::lineToCallback ( FT_Vector* to, Solid* solid ) + { + VertexInfo vertex( to, solid->colorTess(), solid->textureTess() ); + + VertexInfo normal( solid->extrusion_.normal_sign_.y_ * + ( vertex.v_[Y] - solid->last_vertex_.v_[Y] ), + solid->extrusion_.normal_sign_.x_ * + ( vertex.v_[X] - solid->last_vertex_.v_[X] ) ); + + solid->last_vertex_ = vertex; + + vertex.v_[X] *= solid->vector_scale_; + vertex.v_[Y] *= solid->vector_scale_; + + normal.normalize(); + + solid->extrusion_.contour_.push_back( vertex ); + solid->extrusion_.contour_normals_.push_back( normal ); + + return 0; + } + + int Solid::conicToCallback ( FT_Vector* control, FT_Vector* to, Solid* solid ) + { + // This is crude: Step off conics with a fixed number of increments + + VertexInfo to_vertex( to, solid->colorTess(), solid->textureTess() ); + VertexInfo control_vertex( control, solid->colorTess(), solid->textureTess() ); + + double b[2], c[2], d[2], f[2], df[2], d2f[2]; + + b[X] = solid->last_vertex_.v_[X] - 2 * control_vertex.v_[X] + + to_vertex.v_[X]; + b[Y] = solid->last_vertex_.v_[Y] - 2 * control_vertex.v_[Y] + + to_vertex.v_[Y]; + + c[X] = -2 * solid->last_vertex_.v_[X] + 2 * control_vertex.v_[X]; + c[Y] = -2 * solid->last_vertex_.v_[Y] + 2 * control_vertex.v_[Y]; + + d[X] = solid->last_vertex_.v_[X]; + d[Y] = solid->last_vertex_.v_[Y]; + + f[X] = d[X]; + f[Y] = d[Y]; + df[X] = c[X] * solid->delta_ + b[X] * solid->delta2_; + df[Y] = c[Y] * solid->delta_ + b[Y] * solid->delta2_; + d2f[X] = 2 * b[X] * solid->delta2_; + d2f[Y] = 2 * b[Y] * solid->delta2_; + + for ( unsigned int i = 0; i < solid->tessellation_steps_-1; i++ ) { + + f[X] += df[X]; + f[Y] += df[Y]; + + VertexInfo vertex( f, solid->colorTess(), solid->textureTess() ); + + VertexInfo normal( solid->extrusion_.normal_sign_.y_ * df[Y], + solid->extrusion_.normal_sign_.x_ * df[X] ); + + vertex.v_[X] *= solid->vector_scale_; + vertex.v_[Y] *= solid->vector_scale_; + + normal.normalize(); + + solid->extrusion_.contour_.push_back( vertex ); + solid->extrusion_.contour_normals_.push_back( normal ); + + df[X] += d2f[X]; + df[Y] += d2f[Y]; + } + + VertexInfo vertex( to, solid->colorTess(), solid->textureTess() ); + + VertexInfo normal( solid->extrusion_.normal_sign_.y_ * df[Y], + solid->extrusion_.normal_sign_.x_ * df[X] ); + + vertex.v_[X] *= solid->vector_scale_; + vertex.v_[Y] *= solid->vector_scale_; + + normal.normalize(); + + solid->extrusion_.contour_.push_back( vertex ); + solid->extrusion_.contour_normals_.push_back( normal ); + + solid->last_vertex_ = to_vertex; + + return 0; + } + + int Solid::cubicToCallback ( FT_Vector* control1, FT_Vector* control2, + FT_Vector* to, Solid* solid ) + { + // This is crude: Step off cubics with a fixed number of increments + + VertexInfo to_vertex( to, solid->colorTess(), solid->textureTess() ); + VertexInfo control1_vertex( control1, solid->colorTess(), solid->textureTess() ); + VertexInfo control2_vertex( control2, solid->colorTess(), solid->textureTess() ); + + double a[2], b[2], c[2], d[2], f[2], df[2], d2f[2], d3f[2]; + + a[X] = -solid->last_vertex_.v_[X] + 3 * control1_vertex.v_[X] + -3 * control2_vertex.v_[X] + to_vertex.v_[X]; + a[Y] = -solid->last_vertex_.v_[Y] + 3 * control1_vertex.v_[Y] + -3 * control2_vertex.v_[Y] + to_vertex.v_[Y]; + + b[X] = 3 * solid->last_vertex_.v_[X] - 6 * control1_vertex.v_[X] + + 3 * control2_vertex.v_[X]; + b[Y] = 3 * solid->last_vertex_.v_[Y] - 6 * control1_vertex.v_[Y] + + 3 * control2_vertex.v_[Y]; + + c[X] = -3 * solid->last_vertex_.v_[X] + 3 * control1_vertex.v_[X]; + c[Y] = -3 * solid->last_vertex_.v_[Y] + 3 * control1_vertex.v_[Y]; + + d[X] = solid->last_vertex_.v_[X]; + d[Y] = solid->last_vertex_.v_[Y]; + + f[X] = d[X]; + f[Y] = d[Y]; + df[X] = c[X] * solid->delta_ + b[X] * solid->delta2_ + + a[X] * solid->delta3_; + df[Y] = c[Y] * solid->delta_ + b[Y] * solid->delta2_ + + a[Y] * solid->delta3_; + d2f[X] = 2 * b[X] * solid->delta2_ + 6 * a[X] * solid->delta3_; + d2f[Y] = 2 * b[Y] * solid->delta2_ + 6 * a[Y] * solid->delta3_; + d3f[X] = 6 * a[X] * solid->delta3_; + d3f[Y] = 6 * a[Y] * solid->delta3_; + + for ( unsigned int i = 0; i < solid->tessellation_steps_-1; i++ ) { + + f[X] += df[X]; + f[Y] += df[Y]; + + VertexInfo vertex( f, solid->colorTess(), solid->textureTess() ); + + VertexInfo normal( solid->extrusion_.normal_sign_.y_ * df[Y], + solid->extrusion_.normal_sign_.x_ * df[X] ); + + vertex.v_[X] *= solid->vector_scale_; + vertex.v_[Y] *= solid->vector_scale_; + + normal.normalize(); + + solid->extrusion_.contour_.push_back( vertex ); + solid->extrusion_.contour_normals_.push_back( normal ); + + df[X] += d2f[X]; + df[Y] += d2f[Y]; + d2f[X] += d3f[X]; + d2f[Y] += d3f[Y]; + } + + VertexInfo vertex( to, solid->colorTess(), solid->textureTess() ); + + VertexInfo normal( solid->extrusion_.normal_sign_.y_ * df[Y], + solid->extrusion_.normal_sign_.x_ * df[X] ); + + vertex.v_[X] *= solid->vector_scale_; + vertex.v_[Y] *= solid->vector_scale_; + + normal.normalize(); + + solid->extrusion_.contour_.push_back( vertex ); + solid->extrusion_.contour_normals_.push_back( normal ); + + solid->last_vertex_ = to_vertex; + + return 0; + } +#endif // OGLFT_NO_SOLID + + Texture::Texture ( const char* filename, float point_size, FT_UInt resolution ) + : Face( filename, point_size, resolution ) + { + if ( !isValid() ) return; + + init(); + } + + Texture::Texture ( const FT_Byte* data_base, const FT_Long data_size, + float point_size, FT_UInt resolution ) + : Face( data_base, data_size, point_size, resolution ) + { + if ( !isValid() ) return; + + init(); + } + + Texture::Texture ( FT_Face face, float point_size, FT_UInt resolution ) + : Face( face, point_size, resolution ) + { + init(); + } + + void Texture::init ( void ) + { + character_rotation_.active_ = false; + character_rotation_.x_ = 0; + character_rotation_.y_ = 0; + character_rotation_.z_ = 0; + + setCharSize(); + + setCharacterRotationReference( 'o' ); + } + + Texture::~Texture ( void ) + { + clearCaches(); + } + + // Note: Changing the character rotation also clears the display list cache. + + void Texture::setCharacterRotationX ( GLfloat character_rotation_x ) + { + if ( character_rotation_x != character_rotation_.x_ ) { + character_rotation_.x_ = character_rotation_x; + + if ( character_rotation_.x_ != 0. || character_rotation_.y_ != 0. || + character_rotation_.z_ != 0. ) + character_rotation_.active_ = true; + else + character_rotation_.active_ = false; + + clearCaches(); + } + } + + void Texture::setCharacterRotationY ( GLfloat character_rotation_y ) + { + if ( character_rotation_y != character_rotation_.y_ ) { + character_rotation_.y_ = character_rotation_y; + + if ( character_rotation_.x_ != 0. || character_rotation_.y_ != 0. || + character_rotation_.z_ != 0. ) + character_rotation_.active_ = true; + else + character_rotation_.active_ = false; + + clearCaches(); + } + } + + void Texture::setCharacterRotationZ ( GLfloat character_rotation_z ) + { + if ( character_rotation_z != character_rotation_.z_ ) { + character_rotation_.z_ = character_rotation_z; + + if ( character_rotation_.x_ != 0. || character_rotation_.y_ != 0. || + character_rotation_.z_ != 0. ) + character_rotation_.active_ = true; + else + character_rotation_.active_ = false; + + clearCaches(); + } + } + + void Texture::setCharSize ( void ) + { + for ( unsigned int f = 0; f < faces_.size(); f++ ) { + FT_Error error = FT_Set_Char_Size( faces_[f].face_, + (FT_F26Dot6)( point_size_ * 64 ), + (FT_F26Dot6)( point_size_ * 64 ), + resolution_, + resolution_ ); + if ( error != 0 ) + return; + } + + if ( rotation_reference_glyph_ != 0 ) + setRotationOffset(); + } + + void Texture::setRotationOffset ( void ) + { + FT_Error error = FT_Load_Glyph( rotation_reference_face_, + rotation_reference_glyph_, + FT_LOAD_RENDER ); + + if ( error != 0 ) + return; + + rotation_offset_y_ = rotation_reference_face_->glyph->bitmap.rows / 2.f; + } + + BBox Texture::measure ( unsigned char c ) + { + BBox bbox; + // For starters, just get the unscaled glyph bounding box + unsigned int f; + FT_UInt glyph_index = 0; + + for ( f = 0; f < faces_.size(); f++ ) { + glyph_index = FT_Get_Char_Index( faces_[f].face_, c ); + if ( glyph_index != 0 ) break; + } + + if ( glyph_index == 0 ) + return bbox; + + FT_Error error = FT_Load_Glyph( faces_[f].face_, glyph_index, + FT_LOAD_DEFAULT ); + if ( error != 0 ) + return bbox; + + FT_Glyph glyph; + error = FT_Get_Glyph( faces_[f].face_->glyph, &glyph ); + if ( error != 0 ) + return bbox; + + FT_BBox ft_bbox; + FT_Glyph_Get_CBox( glyph, ft_glyph_bbox_unscaled, &ft_bbox ); + + FT_Done_Glyph( glyph ); + + bbox = ft_bbox; + bbox.advance_ = faces_[f].face_->glyph->advance; + + return bbox; + } + + double Texture::height ( void ) const + { + if ( faces_[0].face_->height > 0 ) + return faces_[0].face_->height / 64.; + else + return faces_[0].face_->size->metrics.y_ppem; + } + +#ifndef OGLFT_NO_QT + + BBox Texture::measure ( const QChar c ) + { + BBox bbox; + // For starters, just get the unscaled glyph bounding box + unsigned int f; + FT_UInt glyph_index = 0; + + for ( f = 0; f < faces_.size(); f++ ) { + glyph_index = FT_Get_Char_Index( faces_[f].face_, c.unicode() ); + if ( glyph_index != 0 ) break; + } + + if ( glyph_index == 0 ) + return bbox; + + FT_Error error = FT_Load_Glyph( faces_[f].face_, glyph_index, + FT_LOAD_DEFAULT ); + if ( error != 0 ) + return bbox; + + FT_Glyph glyph; + error = FT_Get_Glyph( faces_[f].face_->glyph, &glyph ); + if ( error != 0 ) + return bbox; + + FT_BBox ft_bbox; + FT_Glyph_Get_CBox( glyph, ft_glyph_bbox_unscaled, &ft_bbox ); + + FT_Done_Glyph( glyph ); + + bbox = ft_bbox; + bbox.advance_ = faces_[f].face_->glyph->advance; + + return bbox; + } +#endif /* OGLFT_NO_QT */ + GLuint Texture::compileGlyph ( FT_Face face, FT_UInt glyph_index ) + { + bindTexture( face, glyph_index ); + + GLuint dlist = glGenLists( 1 ); + glNewList( dlist, GL_COMPILE ); + + renderGlyph( face, glyph_index ); + + glEndList( ); + + return dlist; + } + + void Texture::renderGlyph ( FT_Face face, FT_UInt glyph_index ) + { + FT_Error error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT ); + + if ( error != 0 ) + return; + + TextureInfo texture_info; + + GTOCI texture_object = glyph_texobjs_.find( glyph_index ); + + if ( texture_object == glyph_texobjs_.end() ) { + + bindTexture( face, glyph_index ); + + texture_object = glyph_texobjs_.find( glyph_index ); + + if ( texture_object == glyph_texobjs_.end() ) + return; + } + + texture_info = texture_object->second; + + glBindTexture( GL_TEXTURE_2D, texture_info.texture_name_ ); + + if ( character_rotation_.active_ ) { + glPushMatrix(); + glTranslatef( ( texture_info.width_ / 2.f + + texture_info.left_bearing_ ), + rotation_offset_y_, 0.f ); + + if ( character_rotation_.x_ != 0. ) + glRotatef( character_rotation_.x_, 1.f, 0.f, 0.f ); + + if ( character_rotation_.y_ != 0. ) + glRotatef( character_rotation_.y_, 0.f, 1.f, 0.f ); + + if ( character_rotation_.z_ != 0. ) + glRotatef( character_rotation_.z_, 0.f, 0.f, 1.f ); + + glTranslatef( -( texture_info.width_ / 2.f + + texture_info.left_bearing_ ), + -rotation_offset_y_, 0.f ); + } + + glBegin( GL_QUADS ); + + glTexCoord2i( 0, 0 ); + glVertex2i( texture_info.left_bearing_, texture_info.bottom_bearing_ ); + + glTexCoord2f( texture_info.texture_s_, 0.f ); + glVertex2i( texture_info.left_bearing_ + texture_info.width_, + texture_info.bottom_bearing_ ); + + glTexCoord2f( texture_info.texture_s_, texture_info.texture_t_ ); + glVertex2i( texture_info.left_bearing_ + texture_info.width_, + texture_info.bottom_bearing_ + texture_info.height_ ); + + glTexCoord2f( 0.f, texture_info.texture_t_ ); + glVertex2i( texture_info.left_bearing_, + texture_info.bottom_bearing_ + texture_info.height_ ); + + glEnd(); + + if ( character_rotation_.active_ ) { + glPopMatrix(); + } + + // Drawing a character always advances the MODELVIEW. + glTranslatef( texture_info.advance_.x / 64.f, + texture_info.advance_.y / 64.f, + 0. ); + } + + void Texture::clearCaches ( void ) + { + GDLI fgi = glyph_dlists_.begin(); + + for ( ; fgi != glyph_dlists_.end(); ++fgi ) { + glDeleteLists( fgi->second, 1 ); + } + + glyph_dlists_.clear(); + + GTOI fti = glyph_texobjs_.begin(); + + for ( ; fti != glyph_texobjs_.end(); ++fti ) { + glDeleteTextures( 1, &fti->second.texture_name_ ); + } + + glyph_texobjs_.clear(); + } + + unsigned int Texture::nearestPowerCeil ( unsigned int a ) + { + unsigned int b = a; + unsigned int c = 1; + + if ( a == 0 ) return 1; + + // Take the log-2 of a + for ( ; ; ) { + if ( b == 1 ) + break; + + else if ( b == 3 ) { + c *= 4; + break; + } + + b >>= 1; + c *= 2; + } + // If it's too small, raise it another power + if ( c < a ) c *= 2; + + return c; + } + + MonochromeTexture::MonochromeTexture ( const char* filename, float point_size, + FT_UInt resolution ) + : Texture( filename, point_size, resolution ) + {} + + MonochromeTexture::MonochromeTexture ( const FT_Byte* data_base, const FT_Long data_size, + float point_size, FT_UInt resolution ) + : Texture( data_base, data_size, point_size, resolution ) + {} + + MonochromeTexture::MonochromeTexture ( FT_Face face, float point_size, + FT_UInt resolution ) + : Texture( face, point_size, resolution ) + {} + + MonochromeTexture::~MonochromeTexture ( void ) + {} + + // Round up the size of the image to a power of two, but otherwise + // use the bitmap as is (i.e., don't expand it into separate + // luminance and alpha components) + + GLubyte* MonochromeTexture::invertBitmap ( const FT_Bitmap& bitmap, + int* width, int* height ) + { + *width = nearestPowerCeil( bitmap.width ); + *height = nearestPowerCeil( bitmap.rows ); + + GLubyte* inverse = new GLubyte[ ( *width + 7) / 8 * *height ]; + GLubyte* inverse_ptr = inverse; + + memset( inverse, 0, sizeof( GLubyte )*( *width + 7 ) / 8 * *height ); + + for ( int r = 0; r < bitmap.rows; r++ ) { + + GLubyte* bitmap_ptr = &bitmap.buffer[bitmap.pitch * ( bitmap.rows - r - 1 )]; + + for ( int p = 0; p < bitmap.pitch; p++ ) { + + *inverse_ptr++ = *bitmap_ptr++; + } + + inverse_ptr += ( ( *width + 7 ) / 8 - bitmap.pitch ); + } + + return inverse; + } + + // Hmm. This is the only routine which is different between the different + // styles. + + void MonochromeTexture::bindTexture ( FT_Face face, FT_UInt glyph_index ) + { + GTOCI texobj = glyph_texobjs_.find( glyph_index ); + + if ( texobj != glyph_texobjs_.end() ) + return; + + // Retrieve the glyph's data. + + FT_Error error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT ); + + if ( error != 0 ) + return; + + error = FT_Render_Glyph( face->glyph, ft_render_mode_mono ); + + if ( error != 0 ) + return; + + TextureInfo texture_info; + + glGenTextures( 1, &texture_info.texture_name_ ); + glBindTexture( GL_TEXTURE_2D, texture_info.texture_name_ ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + + // Texture maps have be a power of 2 in size (is 1 a power of 2?), so + // pad it out while flipping it over + int width, height; + GLubyte* inverted_pixmap = + invertBitmap( face->glyph->bitmap, &width, &height ); + + GLfloat red_map[2] = { background_color_[R], foreground_color_[R] }; + GLfloat green_map[2] = { background_color_[G], foreground_color_[G] }; + GLfloat blue_map[2] = { background_color_[B], foreground_color_[B] }; + GLfloat alpha_map[2] = { background_color_[A], foreground_color_[A] }; + + glPixelMapfv( GL_PIXEL_MAP_I_TO_R, 2, red_map ); + glPixelMapfv( GL_PIXEL_MAP_I_TO_G, 2, green_map ); + glPixelMapfv( GL_PIXEL_MAP_I_TO_B, 2, blue_map ); + glPixelMapfv( GL_PIXEL_MAP_I_TO_A, 2, alpha_map ); + + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width, height, + 0, GL_COLOR_INDEX, GL_BITMAP, inverted_pixmap ); + + // Save a good bit of the data about this glyph + texture_info.left_bearing_ = face->glyph->bitmap_left; + texture_info.bottom_bearing_ = -( face->glyph->bitmap.rows + - face->glyph->bitmap_top ); + texture_info.width_ = face->glyph->bitmap.width; + texture_info.height_ = face->glyph->bitmap.rows; + texture_info.texture_s_ = (GLfloat)texture_info.width_ / width; + texture_info.texture_t_ = (GLfloat)texture_info.height_ / height; + texture_info.advance_ = face->glyph->advance; + + glyph_texobjs_[ glyph_index ] = texture_info; + + delete[] inverted_pixmap; + } + + GrayscaleTexture::GrayscaleTexture ( const char* filename, float point_size, + FT_UInt resolution ) + : Texture( filename, point_size, resolution ) + {} + + GrayscaleTexture::GrayscaleTexture ( const FT_Byte* data_base, const FT_Long data_size, + float point_size, FT_UInt resolution ) + : Texture( data_base, data_size, point_size, resolution ) + {} + + GrayscaleTexture::GrayscaleTexture ( FT_Face face, float point_size, + FT_UInt resolution ) + : Texture( face, point_size, resolution ) + {} + + GrayscaleTexture::~GrayscaleTexture ( void ) + {} + + // For the grayscale style, the luminance is the grayscale FreeType value, + // so this just rounds up to a power of two and inverts the pixmap + + GLubyte* GrayscaleTexture::invertPixmap ( const FT_Bitmap& bitmap, + int* width, int* height ) + { + *width = nearestPowerCeil( bitmap.width ); + *height = nearestPowerCeil( bitmap.rows ); + + GLubyte* inverse = new GLubyte[ *width * *height ]; + GLubyte* inverse_ptr = inverse; + + for ( int r = 0; r < bitmap.rows; r++ ) { + + GLubyte* bitmap_ptr = &bitmap.buffer[bitmap.pitch * ( bitmap.rows - r - 1 )]; + + for ( int p = 0; p < bitmap.width; p++ ) { + *inverse_ptr++ = *bitmap_ptr++; + } + + inverse_ptr += ( *width - bitmap.pitch ); + } + return inverse; + } + + // Hmm. This is the only routine which is different between the different + // styles. + + void GrayscaleTexture::bindTexture ( FT_Face face, FT_UInt glyph_index ) + { + GTOCI texobj = glyph_texobjs_.find( glyph_index ); + + if ( texobj != glyph_texobjs_.end() ) + return; + + // Retrieve the glyph's data. + + FT_Error error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT ); + + if ( error != 0 ) + return; + + error = FT_Render_Glyph( face->glyph, ft_render_mode_normal ); + + if ( error != 0 ) + return; + + TextureInfo texture_info; + + glGenTextures( 1, &texture_info.texture_name_ ); + glBindTexture( GL_TEXTURE_2D, texture_info.texture_name_ ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + + // Texture maps have be a power of 2 in size (is 1 a power of 2?), so + // pad it out while flipping it over + int width, height; + GLubyte* inverted_pixmap = + invertPixmap( face->glyph->bitmap, &width, &height ); + + glPushAttrib( GL_PIXEL_MODE_BIT ); + glPixelTransferf( GL_RED_SCALE, foreground_color_[R] - background_color_[R] ); + glPixelTransferf( GL_GREEN_SCALE, foreground_color_[G]-background_color_[G] ); + glPixelTransferf( GL_BLUE_SCALE, foreground_color_[B]-background_color_[B] ); + glPixelTransferf( GL_ALPHA_SCALE, foreground_color_[A]-background_color_[A] ); + glPixelTransferf( GL_RED_BIAS, background_color_[R] ); + glPixelTransferf( GL_GREEN_BIAS, background_color_[G] ); + glPixelTransferf( GL_BLUE_BIAS, background_color_[B] ); + glPixelTransferf( GL_ALPHA_BIAS, background_color_[A] ); + + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width, height, + 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, inverted_pixmap ); + + glPopAttrib(); + // Save a good bit of the data about this glyph + texture_info.left_bearing_ = face->glyph->bitmap_left; + texture_info.bottom_bearing_ = -( face->glyph->bitmap.rows + - face->glyph->bitmap_top ); + texture_info.width_ = face->glyph->bitmap.width; + texture_info.height_ = face->glyph->bitmap.rows; + texture_info.texture_s_ = (GLfloat)texture_info.width_ / width; + texture_info.texture_t_ = (GLfloat)texture_info.height_ / height; + texture_info.advance_ = face->glyph->advance; + + glyph_texobjs_[ glyph_index ] = texture_info; + + delete[] inverted_pixmap; + } + + TranslucentTexture::TranslucentTexture ( const char* filename, float point_size, + FT_UInt resolution ) + : Texture( filename, point_size, resolution ) + {} + + TranslucentTexture::TranslucentTexture ( const FT_Byte* data_base, const FT_Long data_size, + float point_size, FT_UInt resolution ) + : Texture( data_base, data_size, point_size, resolution ) + {} + + TranslucentTexture::TranslucentTexture ( FT_Face face, float point_size, + FT_UInt resolution ) + : Texture( face, point_size, resolution ) + {} + + TranslucentTexture::~TranslucentTexture ( void ) + {} + + // For the translucent style, the luminance is saturated and alpha value + // is the translucent FreeType value + + GLubyte* TranslucentTexture::invertPixmap ( const FT_Bitmap& bitmap, + int* width, int* height ) + { + *width = nearestPowerCeil( bitmap.width ); + *height = nearestPowerCeil( bitmap.rows ); + + GLubyte* inverse = new GLubyte[ 2 * *width * *height ]; + GLubyte* inverse_ptr = inverse; + + for ( int r = 0; r < bitmap.rows; r++ ) { + + GLubyte* bitmap_ptr = &bitmap.buffer[bitmap.pitch * ( bitmap.rows - r - 1 )]; + + for ( int p = 0; p < bitmap.width; p++ ) { + *inverse_ptr++ = 0xff; + *inverse_ptr++ = *bitmap_ptr++; + } + + inverse_ptr += 2 * ( *width - bitmap.pitch ); + } + return inverse; + } + + // Hmm. This is the only routine which is different between the different + // styles. + + void TranslucentTexture::bindTexture ( FT_Face face, FT_UInt glyph_index ) + { + GTOCI texobj = glyph_texobjs_.find( glyph_index ); + + if ( texobj != glyph_texobjs_.end() ) + return; + + // Retrieve the glyph's data. + + FT_Error error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT ); + + if ( error != 0 ) + return; + + error = FT_Render_Glyph( face->glyph, ft_render_mode_normal ); + + if ( error != 0 ) + return; + + TextureInfo texture_info; + + glGenTextures( 1, &texture_info.texture_name_ ); + glBindTexture( GL_TEXTURE_2D, texture_info.texture_name_ ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + + // Texture maps have be a power of 2 in size (is 1 a power of 2?), so + // pad it out while flipping it over + int width, height; + GLubyte* inverted_pixmap = + invertPixmap( face->glyph->bitmap, &width, &height ); + + glPushAttrib( GL_PIXEL_MODE_BIT ); + glPixelTransferf( GL_RED_SCALE, foreground_color_[R] - background_color_[R] ); + glPixelTransferf( GL_GREEN_SCALE, foreground_color_[G]-background_color_[G] ); + glPixelTransferf( GL_BLUE_SCALE, foreground_color_[B]-background_color_[B] ); + glPixelTransferf( GL_ALPHA_SCALE, foreground_color_[A]-background_color_[A] ); + glPixelTransferf( GL_RED_BIAS, background_color_[R] ); + glPixelTransferf( GL_GREEN_BIAS, background_color_[G] ); + glPixelTransferf( GL_BLUE_BIAS, background_color_[B] ); + glPixelTransferf( GL_ALPHA_BIAS, background_color_[A] ); + + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width, height, + 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, inverted_pixmap ); + + glPopAttrib(); + + // Save a good bit of the data about this glyph + texture_info.left_bearing_ = face->glyph->bitmap_left; + texture_info.bottom_bearing_ = -( face->glyph->bitmap.rows + - face->glyph->bitmap_top ); + texture_info.width_ = face->glyph->bitmap.width; + texture_info.height_ = face->glyph->bitmap.rows; + texture_info.texture_s_ = (GLfloat)texture_info.width_ / width; + texture_info.texture_t_ = (GLfloat)texture_info.height_ / height; + texture_info.advance_ = face->glyph->advance; + + glyph_texobjs_[ glyph_index ] = texture_info; + + delete[] inverted_pixmap; + } + +} // close OGLFT namespace diff --git a/engine/libraries/oglft/liboglft/OGLFT.h.cmake b/engine/libraries/oglft/liboglft/OGLFT.h.cmake new file mode 100644 index 0000000..ab686d6 --- /dev/null +++ b/engine/libraries/oglft/liboglft/OGLFT.h.cmake @@ -0,0 +1,2321 @@ +// -*- c++ -*- +/* + * OGLFT: A library for drawing text with OpenGL using the FreeType library + * Copyright (C) 2002 lignum Computing, Inc. + * $Id: OGLFT.h.cmake 107 2008-04-25 09:29:24Z brevilo $ + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef OGLFT_H +#define OGLFT_H + +// CMake activates these definitions. +#cmakedefine ENABLE_QT +#cmakedefine ENABLE_GLE +#cmakedefine GLU_TESS_CALLBACK_TRIPLEDOT +#cmakedefine HAVE_OPENGL_DIR +// Convert to our old options. +#if !defined(ENABLE_QT) +#define OGLFT_NO_QT +#endif +#if !defined(ENABLE_GLE) +#define OGLFT_NO_SOLID +#endif + +#include +#include +#include +#include + +#ifdef WIN32 +#include +#endif + +#ifdef HAVE_OPENGL_DIR +#include +#include +#else +#include +#include +#endif + +#ifndef OGLFT_NO_SOLID +#ifdef HAVE_OPENGL_DIR +#include +#else +#include +#endif +#endif + +#ifndef OGLFT_NO_QT +#define OGLFT_QT_VERSION @DESIRED_QT_VERSION@ +#if OGLFT_QT_VERSION == 3 +#include +#include +#elif OGLFT_QT_VERSION == 4 +#include +#include +#endif +#endif + +#include +#include FT_FREETYPE_H +#include FT_GLYPH_H +#include FT_OUTLINE_H +#include FT_TRIGONOMETRY_H + +#ifdef _MSC_VER +#ifdef OGLFT_BUILD +#define OGLFT_API __declspec(dllexport) +#else +#define OGLFT_API __declspec(dllimport) +#endif +#else +#define OGLFT_API +#endif + +//! All of OGLFT C++ objects are in this namespace. + +namespace OGLFT { + + //! Thanks to DesCartes, I'd consider these manifest constants. + enum Coordinates { + X, //!< The X component of space + Y, //!< The Y component of space + Z, //!< The Z component of space + W //!< The projection component of space + }; + + //! Who to credit? Newton? I'd consider these manifest constants. + enum ColorSpace { + R, //!< The Red component of a color + G, //!< The Green component of a color + B, //!< The Blue component of a color + A, //!< The Alpha (or transparency) of a color + }; + + //! Callback from GLU tessellation routines. +#ifdef GLU_TESS_CALLBACK_TRIPLEDOT + typedef GLvoid (*GLUTessCallback)(...); +#else + typedef GLvoid (*GLUTessCallback)(); +#endif + + //! The FreeType library instance. + /*! + * The FreeType library has a single, global instance of a library + * handle. This reference is used to load font faces. This detail + * is generally hidden from the user of OGLFT, however, it + * can be useful to get the FT_Library instance if you want to open + * a font file yourself, either from disk or embedded in the program. + */ + class Library { + public: + /*! + * The FreeType library's library handle is only available through this + * accessor method. + * \return the global OGLFT FreeType library handle. + */ + static OGLFT_API FT_Library& instance ( void ); + + protected: + /*! + * The constructor for this class is automatically called when + * this library is loaded. Access the instance through the instance() + * method. + */ + OGLFT_API Library ( void ); + /*! + * This destructor is automatically called when the program exits. + */ + OGLFT_API ~Library( void ); + + private: + static Library library; + static FT_Library library_; + }; + + //! Advance describes the "advance" of a glyph, namely the distance in + //! model space at which the NEXT glyph should be drawn. This class exists + //! to assist the computation of string metrics. + struct OGLFT_API Advance { + float dx_; //!< Advance increment in the X direction. + float dy_; //!< Advance increment in the Y direction. + + //! Default constructor. An otherwise uninitialized Advance contains zeros. + Advance ( float dx = 0, float dy = 0 ) : dx_( dx ), dy_( dy ) + {} + + //! Initialize an advance from a FreeType advance member. + Advance ( FT_Vector v ) + { + dx_ = v.x / 64.f; + dy_ = v.y / 64.f; + } + + //! Increment Advance with a FreeType advance member. + //! \return a reference to oneself. + Advance& operator+= ( const FT_Vector v ) + { + dx_ += v.x / 64.f; + dy_ += v.y / 64.f; + return *this; + } + }; + + //! Describe the metrics of a glyph or string relative to the origin + //! of the first character + struct OGLFT_API BBox { + float x_min_; //!< The left-most position at which "ink" appears. + float y_min_; //!< the bottom-most position at which "ink" appears. + float x_max_; //!< The right-most position at which "ink" appears. + float y_max_; //!< The top-most position at which "ink" appears. + Advance advance_; //!< The (total) advancement + + //! Default constructor is all zeros. + BBox () : x_min_( 0 ), y_min_( 0 ), x_max_( 0 ), y_max_( 0 ) + {} + + /*! + *(Partially) initialize a BBox from a FreeType bounding box member. + *(The advancement is initialized to zero by its default constructor). + * \param ft_bbox a FreeType bounding box as retrieved from + * \c FT_Glyph_Get_CBox. + */ + BBox ( FT_BBox ft_bbox ) + { + x_min_ = ft_bbox.xMin / 64.f; + y_min_ = ft_bbox.yMin / 64.f; + x_max_ = ft_bbox.xMax / 64.f; + y_max_ = ft_bbox.yMax / 64.f; + } + + //! Scale the bounding box by a constant. + //! \param k a constant to scale the bounding box by. + //! \return a reference to oneself. + BBox& operator*= ( float k ) + { + x_min_ *= k; + y_min_ *= k; + x_max_ *= k; + y_max_ *= k; + advance_.dx_ *= k; + advance_.dy_ *= k; + + return *this; + } + + /*! + * Merge a bounding box into the current one (not really addition). + * Each time a BBox is "added", the current BBox is expanded to include + * the metrics of the new BBox. May only work for horizontal fonts, though. + * \param b the bounding box to merge. + * \return a reference to oneself. + */ + BBox& operator+= ( const BBox& b ) + { + float new_value; + + new_value = b.x_min_ + advance_.dx_; + if ( new_value < x_min_ ) x_min_ = new_value; + + new_value = b.y_min_ + advance_.dy_; + if ( new_value < y_min_ ) y_min_ = new_value; + + new_value = b.x_max_ + advance_.dx_; + if ( new_value > x_max_ ) x_max_ = new_value; + + new_value = b.y_max_ + advance_.dy_; + if ( new_value > y_max_ ) y_max_ = new_value; + + advance_.dx_ += b.advance_.dx_; + advance_.dy_ += b.advance_.dy_; + + return *this; + } + }; + + //! During tesselation of a polygonal Face (outline, filled or solid), + //! an object which implements this interface can be used to compute a + //! different color for each vertex. + class OGLFT_API ColorTess { + public: + virtual ~ColorTess ( void ) {} + //! Compute a color for this position. Note that the position is + //! in the glyph's local coordinate system. + //! \param p vertex position in glyph's local coordinate system. Argument is + //! a GLdouble[3]. + //! \return GLfloat[4] (RGBA) color specification. + virtual GLfloat* color ( GLdouble* p ) = 0; + }; + + //! During tesselation of a polygonal Face (outline, filled or solid), + //! an object which implements this interface can be used to compute a + //! different texture coordinate for each vertex. + class OGLFT_API TextureTess { + public: + virtual ~TextureTess ( void ) {} + //! Compute a texture coordinate for this position. Note that the + //! position is in the glyph's local coordinate system. + //! \param p vertex position in glyph's local coordinate system. Argument is + //! a GLdouble[3]. + //! \return GLfloat[2] (s,t) texture coordinates. + virtual GLfloat* texCoord ( GLdouble* p ) = 0; + }; + + //! The argument to setCharacterDisplayLists is an STL vector of + //! OpenGL display list names (GLuints). + typedef std::vector DisplayLists; + + //! A convenience definition of an iterator for display list vectors. + typedef DisplayLists::const_iterator DLCI; + + //! A convenience definition of an iterator for display list vectors. + typedef DisplayLists::iterator DLI; + + //! A face (aka font) used to render text with OpenGL. + /*! + * This is an abstract class, but it does define most the functions that + * you are likely to call to manipulate the rendering of the text. + */ + class Face { + public: + //! Thanks to the standard formerly known as PHIGS. Horizontal text + //! justification constants. + enum OGLFT_API HorizontalJustification { + LEFT, //!< Left justified justification of text + ORIGIN, //!< Natural origin alignment of text (default) + CENTER, //!< Center justified alignment of text + RIGHT //!< Right justified alignment of text + }; + + //! Thanks to the standard formerly known as PHIGS. Vertical text + //! justification constants. + enum OGLFT_API VerticalJustification { + BOTTOM, //!< Descender alignment of text + BASELINE, //!< Baseline alignment of text (default) + MIDDLE, //!< Centered alignment of text + TOP //!< Ascender justification of text + }; + + //! Control how OpenGL display lists are created for individual glyphs. + //! The default mode is to create display lists for each glyph as it + //! is requested. Therefore, the Face drawing routines cannot themselves + //! be called from within an open display list. In IMMEDIATE mode, + //! cached glyphs will be drawn if available, otherwise the FreeType + //! data for a glyph is re-rendered each time. + enum OGLFT_API GlyphCompileMode { + COMPILE, //!< Compile new glyphs when seen for the first time. + IMMEDIATE //!< Do not \em create display lists for glyphs. + }; + + private: + //! We allow a Face to be constructed either from a file name + //! or passed in as an already opened FreeType FT_Face. In the case + //! of the later (already opened), we don't close the FT_Face on + //! destruction. This way you can share FT_Faces between related + //! OGLFT faces. Also, we're experimenting with being able to use + //! multiple FT_Faces in a single OGLFT Face, so this is represented + //! as a data structure. + struct FaceData { + FT_Face face_; + bool free_on_exit_; + FaceData ( FT_Face face, bool free_on_exit = true ) + : face_( face ), free_on_exit_( free_on_exit ) + {} + }; + protected: + //! The FreeType face - experimentally, this is now an array of + //! faces so that we can handle a wider range of UNICODE points + //! in case a face doesn't cover the points of interest. + std::vector< FaceData > faces_; + + //! Did a font load OK? + bool valid_; + + //! Glyph display list creation mode. + enum GlyphCompileMode compile_mode_; + + //! Nominal point size. + float point_size_; + + //! Display resolution in pixels per inch. + FT_UInt resolution_; + + //! Does rendering text affect the MODELVIEW matrix? + bool advance_; + + //! Foreground color (I really wanted to avoid this, but not really + //! possible without state queries, which you can't put into + //! display lists. Anyway, you'll be able to get even more fancy + //! by passing in a function to map the color with, so why balk at + //! this?) + GLfloat foreground_color_[4]; + + //! Background color (what modes would use this?) + GLfloat background_color_[4]; + + //! PHIGS-like horizontal positioning of text. + enum HorizontalJustification horizontal_justification_; + + //! PHIGS-like vertical positioning of text. + enum VerticalJustification vertical_justification_; + + //! Rotate an entire string in the Z plane + GLfloat string_rotation_; + + //! Let the user decide which character to use as the rotation reference. + //! Use "o" by default, I suppose. + FT_UInt rotation_reference_glyph_; + + //! The rotation reference character could be in any face. + FT_Face rotation_reference_face_; + + //! These are the translation offsets provided by the rotation reference + //! character; for whom, we've discovered, only the Y position is relevant. + GLfloat rotation_offset_y_; + + //! Type of the cache of defined glyph to display list mapping. + typedef std::map< FT_UInt, GLuint > GlyphDLists; + + //! A convenience definition of the iterator over the glyph to display + //! list map. + typedef GlyphDLists::const_iterator GDLCI; + + //! A convenience definition of the iterator over the glyph to display + //! list map. + typedef GlyphDLists::iterator GDLI; + + //! Cache of defined glyph display lists + GlyphDLists glyph_dlists_; + + //! The user can supply an array of display list which are invoked + //! before each glyph is rendered. + DisplayLists character_display_lists_; + + public: + /*! + * Construct a Face by loading a font from the given file. + * \param filename the filename which contains the font face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + Face ( const char* filename, float point_size = 12, FT_UInt resolution = 100 ); + + /*! + * Construct a Face by loading a font from the given memory location. + * \param data_base the memory location (base pointer) which contains the font face. + * \param data_size the size (in bytes) of the font data found at \ref data_base. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + Face ( const FT_Byte* data_base, const FT_Long data_size, + float point_size = 12, FT_UInt resolution = 100 ); + + /*! + * Alternatively, the user may have already opened a face and just + * wants to draw with it. This is useful for Multiple Master fonts or + * combining multiple files to increase UNICODE point coverage. + * \param face open Freetype FT_Face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + Face ( FT_Face face, float point_size = 12, FT_UInt resolution = 100 ); + + /*! + * Deleting a Face frees its FreeType face (and anything else it's + * styles have allocated). + */ + virtual ~Face ( void ); + + /*! + * Let the user test to see if the font was loaded OK. + * \return true if the FT_Face was successfully created. + */ + bool isValid ( void ) const { return valid_; } + + /*! + * Add another FT_Face to the OGLFT Face. Generally used to add more + * coverage of UNICODE points (at least that's the plan). This + * routine takes a filename and takes ownership of the FT_Face. + * \param filename name of file containing font face data. + * \return true if face was successfully added. + */ + bool addAuxiliaryFace ( const char* filename ); + + /*! + * Add another FT_Face to the OGLFT Face. Generally used to add more + * coverage of UNICODE points (at least that's the plan). This + * routine takes a memory location and takes ownership of the FT_Face. + * \param data_base the memory location (base pointer) which contains the font face. + * \param data_size the size (in bytes) of the font data found at \ref data_base. + * \return true if face was successfully added. + */ + bool addAuxiliaryFace ( const FT_Byte* data_base, const FT_Long data_size ); + + /*! + * Add another FT_Face to the OGLFT Face. Generally used to add more + * coverage of UNICODE points (at least that's the plan). This + * routine takes an already open FT_Face. The user is responsible + * for clean up. + * \param face open FreeType FT_Face + * \return true if face was successfully added. + */ + bool addAuxiliaryFace ( FT_Face face ); + + /*! + * By default, each time a new character is seen, its glyph is rendered + * into a display list. This means that a display list cannot already + * be open (since OpenGL doesn't allow nested display list creation). + * Rendering can be set into immediate mode in which case glyphs are + * rendered from display lists if available, but are otherwise generated + * anew each time. + * \param compile_mode the new compile mode. + */ + void setCompileMode ( enum GlyphCompileMode compile_mode ) + { + compile_mode_ = compile_mode; + } + + /*! + * \return the current glyph compile mode. + */ + enum GlyphCompileMode compileMode ( void ) const { return compile_mode_; } + + /*! + * For the rasterized styles (Monochrome, Grayscale, Translucent, Texture), + * glyphs are rendered at the pixel size given by: + * + * point_size [pts] * / 72 [pts/in] * resolution [dots/in] = [dots]. + * + * For the polygon styles (Outline, Filled, Solid), the "nominal" size of + * the glyphs is: + * + * point_size[pts] / 72 [pts/in] * resolution [dots/in] + * / units_per_EM [font unit/EM] = [dots * EM]. + * + * If the MODELVIEW and PROJECTION matrices are such that one screen pixel + * corresponds to one modeling unit, then polygonal Faces will + * be the same size as raster Faces. + * + * Note that changing the point size after Face creation will invalidate + * the cache of OpenGL display lists and any other information which + * the individual styles have cached. + * \param point_size the new point size in points (1/72-th inch). + */ + void setPointSize ( float point_size ); + + /*! + * \return the current point size. + */ + float pointSize ( void ) { return point_size_; } + + /*! + * For the rasterized styles (Monochrome, Grayscale, + * Translucent, Texture), the exact rendered size of the glyphs depends on + * the resolution of the display (as opposed to the polygon styles + * whose size is controlled by the viewing matrices). The Texture + * style is slightly different because the glyphs are texture-mapped + * onto an arbitrary rectangle; here, the resolution only controls + * how accurately the glyph is rendered. + * \param resolution the resolution in DPI (dots per inch). + */ + void setResolution ( FT_UInt resolution ); + + /*! + * \return the current raster resolution. + */ + FT_UInt resolution ( void ) { return resolution_; } + + /*! + * If advance is true, then the changes made to the MODELVIEW matrix + * to render a string are allowed to remain. Otherwise, the library + * pushes the current MODELVIEW matrix onto the matrix stack, renders + * the string and then pops it off again. Rendering a character always + * modifies the MODELVIEW matrix. + * \param advance whether or not the MODELVIEW matrix should be left + * translated by the advancement of a rendered string. + */ + void setAdvance ( bool advance ) { advance_ = advance; } + + /*! + * \return the advance value. + */ + bool advance ( void ) const { return advance_; } + + /*! + * This is the nominal color of the glyphs. A lot of other things + * can alter what you actually see! Note that changing the foreground + * color invalidates the glyph cache. + * \param red the red component of the foreground color. + * \param green the green component of the foreground color. + * \param blue the blue component of the foreground color. + * \param alpha the alpha component of the foreground color. + */ + void setForegroundColor ( GLfloat red = 0.0, + GLfloat green = 0.0, + GLfloat blue = 0.0, + GLfloat alpha = 1.0 ); + + /*! + * This is the nominal color of the glyphs. A lot of other things + * can alter what you actually see! Note that changing the foreground + * color invalidates the glyph cache. + * \param foreground_color an array of 4 values corresponding to the + * red, green, blue and alpha components of the foreground color. + */ + void setForegroundColor ( const GLfloat foreground_color[4] ); +#ifndef OGLFT_NO_QT + /*! + * This is the nominal color of the glyphs. A lot of other things + * can alter what you actually see! Note that changing the foreground + * color invalidates the glyph cache. + * \param foreground_color the foreground color as an unsigned int. + */ + void setForegroundColor ( const QRgb foreground_color ); +#endif /* OGLFT_NO_QT */ + /*! + * \return the red component of the foreground color + */ + GLfloat foregroundRed ( void ) const { return foreground_color_[R]; } + /*! + * \return the green component of the foreground color + */ + GLfloat foregroundGreen ( void ) const { return foreground_color_[G]; } + /*! + * \return the blue component of the foreground color + */ + GLfloat foregroundBlue ( void ) const { return foreground_color_[B]; } + /*! + * \return the alpha component of the foreground color + */ + GLfloat foregroundAlpha ( void ) const { return foreground_color_[A]; } + + /*! + * This is the nominal background color of the glyphs. A lot of other things + * can alter what you actually see! Note that changing the background + * color invalidates the glyph cache. + * \param red the red component of the background color. + * \param green the green component of the background color. + * \param blue the blue component of the background color. + * \param alpha the alpha component of the background color. + */ + void setBackgroundColor ( GLfloat red = 1.0, + GLfloat green = 1.0, + GLfloat blue = 1.0, + GLfloat alpha = 0.0 ); + + /*! + * This is the nominal background color of the glyphs. A lot of other things + * can alter what you actually see! Note that changing the background + * color invalidates the glyph cache. + * \param background_color an array of 4 values corresponding to the + * red, green, blue and alpha components of the background color. + */ + void setBackgroundColor ( const GLfloat background_color[4] ); +#ifndef OGLFT_NO_QT + /*! + * This is the nominal background color of the glyphs. A lot of other things + * can alter what you actually see! Note that changing the background + * color invalidates the glyph cache. + * \param background_color the background color as an unsigned int. + */ + void setBackgroundColor ( const QRgb background_color ); +#endif /* OGLFT_NO_QT */ + /*! + * \return the red component of the background color + */ + GLfloat backgroundRed ( void ) const { return background_color_[R]; } + /*! + * \return the green component of the background color + */ + GLfloat backgroundGreen ( void ) const { return background_color_[G]; } + /*! + * \return the blue component of the background color + */ + GLfloat backgroundBlue ( void ) const { return background_color_[B]; } + /*! + * \return the alpha component of the background color + */ + GLfloat backgroundAlpha ( void ) const { return background_color_[A]; } + + /*! + * Set the individual character rotation in the Z direction. + * \param character_rotation_z angle in degrees of z rotation. + */ + virtual void setCharacterRotationZ ( GLfloat character_rotation_z ) = 0; + + /*! + * \return the character rotation in the Z direction. + */ + virtual GLfloat characterRotationZ ( void ) const = 0; + + /*! + * The z rotation angle needs a center. Nominate a character whose + * center is to be the center of rotation. By default, use "o". + * \param c rotation reference character. + */ + void setCharacterRotationReference ( unsigned char c ); + + /*! + * Rotate an entire string through the given angle (in the Z plane only). + * (Somewhat pointless for the vector styles since you can do mostly + * the same thing with the MODELVIEW transform, however, for what its + * worth, this routine uses the FreeType rotation function to compute + * the "proper" metrics for glyph advance.) + * \param string_rotation angle in degrees of z rotation. + */ + void setStringRotation ( GLfloat string_rotation ); + + /*! + * \return the (Z plane) string rotation angle. + */ + GLfloat stringRotation ( void ) const { return string_rotation_; } + + /*! + * Set the horizontal justification. + * \param horizontal_justification the new horizontal justification. + */ + void setHorizontalJustification ( enum HorizontalJustification + horizontal_justification ) + { + horizontal_justification_ = horizontal_justification; + } + + /*! + * \return the horizontal justification. + */ + enum HorizontalJustification horizontalJustification ( void ) const + { return horizontal_justification_; } + + /*! + * Set the vertical justification. + * \param vertical_justification the new vertical justification + */ + void setVerticalJustification ( enum VerticalJustification + vertical_justification ) + { + vertical_justification_ = vertical_justification; + } + + /*! + * \return the vertical justification. + */ + enum VerticalJustification verticaljustification ( void ) + const { return vertical_justification_; } + + /*! + * Specify an OpenGL display list to be invoked before + * each character in a string. Face makes a copy of the argument. Pass + * an empty DisplayLists to disable this feature. + * \param character_display_lists STL vector containing a display + * list to invoke before each glyph in a string is drawn. + */ + void setCharacterDisplayLists ( const DisplayLists& character_display_lists ) + { + character_display_lists_ = character_display_lists; + } + + /*! + * \return a reference to the array of character display lists. This is + * the live list as stored in the Face. + */ + DisplayLists& characterDisplayLists ( void ) + { return character_display_lists_; } + + /*! + * \return the height (i.e., line spacing) at the current character size. + */ + virtual double height ( void ) const = 0; + + /*! + * Compute the bounding box info for a character. + * \param c the (latin1) character to measure. + * \return the bounding box of c. + */ + virtual BBox measure ( unsigned char c ) = 0; +#ifndef OGLFT_NO_QT + /*! + * Compute the bounding box info for a character. + * \param c the (UNICODE) character to measure. + * \return the bounding box of c. + */ + virtual BBox measure ( const QChar c ) = 0; +#endif /* OGLFT_NO_QT */ + /*! + * Compute the bounding box info for a string. + * \param s the (latin1) string to measure. + * \return the bounding box of s. + */ + virtual BBox measure ( const char* s ); + /*! + * Compute the bounding box info for a string without conversion + * to modeling coordinates. + * \param s the (latin1) string to measure. + * \return the bounding box of s. + */ + virtual BBox measureRaw ( const char* s ); +#ifndef OGLFT_NO_QT + /*! + * Compute the bounding box info for a string. + * \param s the (UNICODE) string to measure. + * \return the bounding box of s. + */ + virtual BBox measure ( const QString& s ); + /*! + * Compute the bounding box info for a real number formatted as specified. + * \param format (see draw for valid formats) + * \param number real number. + * \return the bounding box of the formatted number. + */ + virtual BBox measure ( const QString& format, double number ); + /*! + * Compute the bounding box info for a string without conversion + * to modeling coordinates. + * \param s the (UNICODE) string to measure. + * \return the bounding box of s. + */ + virtual BBox measureRaw ( const QString& s ); +#endif /* OGLFT_NO_QT */ + /*! + * Compile a string into an OpenGL display list for later + * rendering. Essentially, the string is rendered at the origin + * of the current MODELVIEW. Note: no other display lists should + * be open when this routine is called. Also, the Face does not + * keep track of these lists, so you must delete them in order + * to recover the memory. + * \param s the (latin1) string to compile. + * \return the display list name for the string. + */ + GLuint compile ( const char* s ); +#ifndef OGLFT_NO_QT + /*! + * Compile a string into an OpenGL display list for later + * rendering. Essentially, the string is rendered at the origin + * of the current MODELVIEW. Note: no other display lists should + * be open when this routine is called. Also, the Face does not + * keep track of these lists, so you must delete them in order + * to recover the memory. + * \param s the (UNICODE) string to compile. + * \return the display list name for the string. + */ + GLuint compile ( const QString& s ); +#endif /* OGLFT_NO_QT */ + /*! + * Compile a single character (glyph) into an OpenGL display list + * for later rendering. The Face \em does keep track of these + * display lists, so do not delete them. + * \param c the (latin1) character to compile. + * \return the display list name for the character. + */ + GLuint compile ( unsigned char c ); +#ifndef OGLFT_NO_QT + /*! + * Compile a single character (glyph) into an OpenGL display list + * for later rendering. The Face \em does keep track of these + * display lists, so do not delete them. + * \param c the (UNICODE) character to compile. + * \return the display list name for the character. + */ + GLuint compile ( const QChar c ); +#endif /* OGLFT_NO_QT */ + /*! + * Draw a (latin1) string using the current MODELVIEW matrix. If + * advance is true, then the final glyph advance changes to the + * MODELVIEW matrix are left in place. + * \param s the (latin1) string to draw. + */ + void draw ( const char* s ); +#ifndef OGLFT_NO_QT + /*! + * Draw a (UNICODE) string using the current MODELVIEW + * matrix. If advance is true, then the final glyph advance + * changes to the MODELVIEW matrix are left in place. + * \param s the (UNICODE) string to draw. + */ + void draw ( const QString& s ); +#endif /* OGLFT_NO_QT */ + /*! + * Draw the character using the current MODELVIEW matrix. Note that + * the MODELVIEW matrix is modified by the glyph advance. Draw a + * string if you don't want the MODELVIEW matrix changed. + * \param c the (latin1) character to draw. + */ + void draw ( unsigned char c ); + +#ifndef OGLFT_NO_QT + /*! + * Draw the character using the current MODELVIEW matrix. Note that + * the MODELVIEW matrix is modified by the glyph advance. Draw a + * string if you don't want the MODELVIEW matrix changed. + * \param c the (UNICODE) character to draw. + */ + void draw ( const QChar c ); +#endif /* OGLFT_NO_QT */ + /*! + * Draw the (latin1) character at the given 2D point. Note that + * the MODELVIEW matrix is modified by the glyph advance. Draw + * a string if you don't want the MODELVIEW matrix changed. + * \param x the X position. + * \param y the Y position. + * \param c the (latin1) character to draw. + */ + void draw ( GLfloat x, GLfloat y, unsigned char c ); + /*! + * Draw the (latin1) character at the given 3D point. Note that + * the MODELVIEW matrix is modified by the glyph advance. Draw + * a string if you don't want the MODELVIEW matrix changed. + * \param x the X position. + * \param y the Y position. + * \param z the Z position. + * \param c the (latin1) character to draw. + */ + void draw ( GLfloat x, GLfloat y, GLfloat z, unsigned char c ); +#ifndef OGLFT_NO_QT + /*! + * Draw the (UNICODE) character at the given 2D point. Note that + * the MODELVIEW matrix is modified by the glyph advance. Draw + * a string if you don't want the MODELVIEW matrix changed. + * \param x the X position. + * \param y the Y position. + * \param c the (UNICODE) character to draw. + */ + void draw ( GLfloat x, GLfloat y, QChar c ); + /*! + * Draw the (UNICODE) character at the given 3D point. Note that + * the MODELVIEW matrix is modified by the glyph advance. Draw + * a string if you don't want the MODELVIEW matrix changed. + * \param x the X position. + * \param y the Y position. + * \param z the Z position. + * \param c the (UNICODE) character to draw. + */ + void draw ( GLfloat x, GLfloat y, GLfloat z, QChar c ); +#endif /* OGLFT_NO_QT */ + /*! + * Draw a string at the given 2D point. + * \param x the X position. + * \param y the Y position. + * \param s the (latin1) string to draw. + */ + void draw ( GLfloat x, GLfloat y, const char* s ); + /*! + * Draw a string at the given 3D point. + * \param x the X position. + * \param y the Y position. + * \param z the Z position. + * \param s the (latin1) string to draw. + */ + void draw ( GLfloat x, GLfloat y, GLfloat z, const char* s ); +#ifndef OGLFT_NO_QT + /*! + * Draw a string at the given 2D point. + * \param x the X position. + * \param y the Y position. + * \param s the (UNICODE) string to draw. + */ + void draw ( GLfloat x, GLfloat y, const QString& s ); + /*! + * Draw a string at the given 3D point. + * \param x the X position. + * \param y the Y position. + * \param z the Z position. + * \param s the (UNICODE) string to draw. + */ + void draw ( GLfloat x, GLfloat y, GLfloat z, const QString& s ); + /*! + * Draw a real number per the given format at the given 2D point. + * \param x the X position. + * \param y the Y position. + * \param format Like a typical printf format. Regular text is printed + * while a '%' introduces the real number's format. Includes the + * following format flags: + * \li %%x.yf - floating point in field width x and precision y + * \li %%x.ye - scientific notation in field width x and precision y + * \li %%x.yg - pick best floating or scientific in field width x and + * precision y + * \li %%p - draw as a proper fraction, e.g. 1 1/2. Note: this currently + * requires a special font which encodes glyphs to be drawn for the + * numerator and demoninator in the UNICODE Private Area (0xE000). + * + * \param number the numeric value. + */ + void draw ( GLfloat x, GLfloat y, const QString& format, double number ); + /*! + * Draw a real number per the given format at the given 3D point. + * \param x the X position. + * \param y the Y position. + * \param z the Z position. + * \param format Like a typical printf format. Regular text is printed + * while a '%' introduces the real number's format. Includes the + * following format flags: + * \li %%x.yf - floating point in field width x and precision y + * \li %%x.ye - scientific notation in field width x and precision y + * \li %%x.yg - pick best floating or scientific in field width x and + * precision y + * \li %%p - draw as a proper fraction, e.g. 1 1/2. Note: this currently + * requires a special font which encodes glyphs to be drawn for the + * numerator and demoninator in the UNICODE Private Area (0xE000). + * + * \param number the numeric value. + */ + void draw ( GLfloat x, GLfloat y, GLfloat z, const QString& format, + double number ); +#endif /* OGLFT_NO_QT */ + /*! + * \return the face ascender, in point units. + */ + int ascender ( void ); + + /*! + * \return the face descender, in point units. + */ + int descender ( void ); + + protected: + // The various styles override these routines + + //! Some styles, in particular the Texture, need specialized steps + //! to compile a glyph into an OpenGL display list. + //! \param face the FT_Face containing the glyph. + //! \param glyph_index the index of the glyph in face. + //! \return the display list of the compiled glyph. + virtual GLuint compileGlyph ( FT_Face face, FT_UInt glyph_index ) = 0; + + //! Each style implements its own glyph rendering routine. + //! \param face the FT_Face containing the glyph. + //! \param glyph_index the index of the glyph in face. + virtual void renderGlyph ( FT_Face face, FT_UInt glyph_index ) = 0; + + //! There is a slight different between the way in which the polygonal + //! and raster styles select the character size for FreeType to generate. + virtual void setCharSize ( void ) = 0; + + //! The different styles have different caching needs (well, really only + //! the texture style currently has more than the display list cache). + virtual void clearCaches ( void ) = 0; + + //! The polygonal and raster styles compute different values for the + //! Z rotation offset. (It's in integer pixels for the raster styles and + //! in floating point pixels for the polygonal styles.) + virtual void setRotationOffset ( void ) = 0; + + private: + void init ( void ); + BBox measure_nominal ( const char* s ); +#ifndef OGLFT_NO_QT + BBox measure_nominal ( const QString& s ); + QString format_number ( const QString& format, double number ); +#endif /* OGLFT_NO_QT */ + }; + + //! This is the base class of the polygonal styles: outline, filled and solid. + /*! + * In the polygonal styles, the detailed geometric outlines of the glyphs + * are extracted from the font file and rendered as polygons. + */ + class Polygonal : public Face { + protected: + //! Angle of rotation of characters relative to text orientation. + struct { + bool active_; + GLfloat x_, y_, z_; + } character_rotation_; + + //! The tessellation of curves is pretty crude; regardless of length, + //! use the same number of increments (and as near as I can tell, this + //! is more than sufficient unless the glyph takes up the whole screen). + unsigned int tessellation_steps_; + + //! When curves are tessellated, we use the forward difference algorithm + //! from Foley and van Dam for parametric curves (pg. 511 of 2nd Ed. in C). + //! So, the step size, delta, is in the parametric variable which is always + //! on the interval [0,1]. Therefore, delta = 1/tessellation_steps + double delta_, delta2_, delta3_; + + //! For vector rendition modes, FreeType is allowed to generate the + //! lines and arcs at the original face definition resolution. To + //! get to the proper glyph size, the vertices are scaled before + //! they're passed to the GLU tessellation routines. + float vector_scale_; + + //! Callbacks for FreeType glyph decomposition into outlines + FT_Outline_Funcs interface_; + + //! Default number of steps to break TrueType and Type1 arcs into. + //! (Note: this looks good to me, anyway) + static const unsigned int DEFAULT_TESSELLATION_STEPS = 4; + + /*! + * VertexInfo is a private class which is used by the decomposition and + * tessellation routines to store the vertices and other data of the glyph's + * outline. Because of the "impedance mismatch" between the crazy + * 26.6 fixed point format of the FreeType library (well, don't + * blame them; look at what they have to work with) and OpenGL's preference + * for double precision, this simple vector has two constructors: one + * for 26.6 format and one for direct floating point. + * + * VertexInfo also contains (optional) pointers to objects which + * implement the ColorTess and TextureTess interfaces. + */ + struct VertexInfo { + double v_[3]; //!< Why is this double precision? Because the second + //!< argument to the routine gluTessVertex is a pointer + //!< to an array of doubles. Otherwise, we could use + //!< single precision everywhere. + + //! The user can provide a ColorTess object which computes a color + //! for each tesselated vertex. + ColorTess* color_tess_; + + //! The user can provide a TextureTess object which computes texture + //! coordinates for each tesselated vertex. + TextureTess* texture_tess_; + + //! Default constructor just initializes Vertex to zero. + //! \param color_tess optional color tesselation object. + //! \param texture_tess optional texture tesselation object. + VertexInfo ( ColorTess* color_tess = 0, TextureTess* texture_tess = 0 ) + : color_tess_( color_tess ), texture_tess_( texture_tess ) + { + v_[X] = v_[Y] = v_[Z] = 0.; + } + + /*! + * Construct a Vertex from a point in a FreeType contour. + * \param ft_v a FreeType FT_Vector, normally passed into the + * the decomposition callbacks. + * \param color_tess optional color tesselation object. + * \param texture_tess optional texture tesselation object. + */ + VertexInfo ( FT_Vector* ft_v, ColorTess* color_tess = 0, + TextureTess* texture_tess = 0 ) + : color_tess_( color_tess ), texture_tess_( texture_tess ) + { + v_[X] = (double)( ft_v->x / 64 ) + (double)( ft_v->x % 64 ) / 64.; + v_[Y] = (double)( ft_v->y / 64 ) + (double)( ft_v->y % 64 ) / 64.; + v_[Z] = 0.; + } + + /*! + * Construct a Vertex from a 2D point. + * \param p 2D array of doubles. + * \param color_tess optional color tesselation object. + * \param texture_tess optional texture tesselation object. + */ + VertexInfo ( double p[2], ColorTess* color_tess = 0, + TextureTess* texture_tess = 0 ) + : color_tess_( color_tess ), texture_tess_( texture_tess ) + { + v_[X] = p[X]; + v_[Y] = p[Y]; + v_[Z] = 0.; + } + + /*! + * Construct a Vertex from a 2D point. + * \param x the X coordinate. + * \param y the Y coordinate. + * \param color_tess optional color tesselation object. + * \param texture_tess optional texture tesselation object. + */ + VertexInfo ( double x, double y, ColorTess* color_tess = 0, + TextureTess* texture_tess = 0 ) + : color_tess_( color_tess ), texture_tess_( texture_tess ) + { + v_[X] = x; + v_[Y] = y; + v_[Z] = 0.; + } + + //! Treat the Vertex like a vector: Normalize its length in the + //! usual way. + void normalize ( void ) + { + double length = sqrt( v_[X] * v_[X] + v_[Y] * v_[Y] + v_[Z] * v_[Z] ); + v_[X] /= length; + v_[Y] /= length; + v_[Z] /= length; + } + }; + + /*! + * Buffers the last control point as the outline of a glyph is + * decomposed. + */ + VertexInfo last_vertex_; + + //! Normally, we will consider a list of vertices. + typedef std::list< VertexInfo* > VertexInfoList; + + //! A convenience definition of the iterator over the list of vertices. + typedef VertexInfoList::const_iterator VILCI; + + //! A convenience definition of the iterator over the list of vertices. + typedef VertexInfoList::iterator VILI; + + /*! + * As curves are decomposed out of the glyph, their vertices are passed + * along to the GLU tessellation functions. These vertices have to + * hang around until gluTessContourEnd is called. + */ + VertexInfoList vertices_; + + //! As GLU tessellation proceeds, new contours open with every call + //! to moveTo. + bool contour_open_; + + //! The user can provide a ColorTess object which computes a color + //! for each tesselated vertex. + ColorTess* color_tess_; + + //! The user can provide a TextureTess object which computes texture + //! coordinates for each tesselated vertex. + TextureTess* texture_tess_; + + public: + /*! + * \param filename the filename which contains the font face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Polygonal ( const char* filename, float point_size = 12, + FT_UInt resolution = 100 ); + + /*! + * \param data_base the memory location (base pointer) which contains the font face. + * \param data_size the size (in bytes) of the font data found at \ref data_base. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Polygonal ( const FT_Byte* data_base, const FT_Long data_size, + float point_size = 12, FT_UInt resolution = 100); + + /*! + * \param face open Freetype FT_Face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Polygonal ( FT_Face face, float point_size = 12, FT_UInt resolution = 100 ); + + /*! + * The Polygonal destructor doesn't do anything in particular. + */ + OGLFT_API virtual ~Polygonal ( void ); + + /*! + * TrueType and Type1 files describe the boundaries of glyphs with + * quadratic and cubic curves, respectively. Since OpenGL can only really + * draw straight lines, these curves have to be tessellated. The + * number of steps used is fixed for all glyphs in the face, + * but can be changed through this method. Other notes: This value is + * only applicable for Outline, Filled and Solid styles. Changing this value + * invalidates any cached display lists for glyphs in this face. + * + * \param tessellation_steps the number of steps to tessellate each curved + * segment of a glyph outline. + */ + OGLFT_API void setTessellationSteps ( unsigned int tessellation_steps ); + + /*! + * \return the number of steps used to tessellate curves in the + * polygonal font types. + */ + OGLFT_API unsigned int tessellationSteps ( void ) const { return tessellation_steps_; } + + /*! + * Set the individual character rotation in the X direction. + * \param character_rotation_x angle in degrees of the X rotation. + */ + OGLFT_API void setCharacterRotationX ( GLfloat character_rotation_x ); + + /*! + * Set the individual character rotation in the Y direction. + * \param character_rotation_y angle in degrees of the Y rotation. + */ + OGLFT_API void setCharacterRotationY ( GLfloat character_rotation_y ); + + /*! + * Set the individual character rotation in the Z direction. + * \param character_rotation_z angle in degrees of the Z rotation. + */ + OGLFT_API void setCharacterRotationZ ( GLfloat character_rotation_z ); + + /*! + * \return the character rotation in the X direction. + */ + OGLFT_API GLfloat characterRotationX ( void ) const { return character_rotation_.x_; } + + /*! + * \return the character rotation in the Y direction. + */ + OGLFT_API GLfloat characterRotationY ( void ) const { return character_rotation_.y_; } + + /*! + * \return the character rotation in the Z direction. + */ + OGLFT_API GLfloat characterRotationZ ( void ) const { return character_rotation_.z_; } + + /*! + * Set an optional color tesselation object. Each tesselated vertex + * is passed to this object, which returns a color for that position + * in space. + * \param color_tess the color tesselation object. + */ + OGLFT_API void setColorTess ( ColorTess* color_tess ); + /*! + * \return the color tesselation object. + */ + OGLFT_API ColorTess* colorTess ( void ) const { return color_tess_; } + /*! + * Set an optional texture coordinate tesselation object. Each + * tessellated vertex is passed to this object, which returns + * texture coordinates for that position in space. + * \param texture_tess the texture coordinate tesselation object. + */ + OGLFT_API void setTextureTess ( TextureTess* texture_tess ); + /*! + * \return the texture coordinate tesselation object. + */ + OGLFT_API TextureTess* textureTess ( void ) const { return texture_tess_; } + + /*! + * \return the height (i.e., line spacing) at the current character size. + */ + OGLFT_API double height ( void ) const; + + /*! + * Implement measuring a character in a polygonal face. + * \param c the (latin1) character to measure + * \return the bounding box of c. + */ + OGLFT_API BBox measure ( unsigned char c ); +#ifndef OGLFT_NO_QT + /*! + * Implement measuring a character in a polygonal face. + * \param c the (UNICODE) character to measure + * \return the bounding box of c. + */ + OGLFT_API BBox measure ( const QChar c ); +#endif /* OGLFT_NO_QT */ + /*! + * Measure a string of characters. Note: currently, this merely + * calls Face's measure routine. + * \param s string of (latin1) characters to measure + * \return the bounding box of s. + */ + OGLFT_API BBox measure ( const char* s ) { return Face::measure( s ); } +#ifndef OGLFT_NO_QT + /*! + * Implement measuring a formatted number + * \param format the format string + * \param number to value to format + * \return the bounding box of the formatted number + */ + OGLFT_API BBox measure ( const QString& format, double number ) + { return Face::measure( format, number ); } +#endif /* OGLFT_NO_QT */ + + private: + void init ( void ); + void setCharSize ( void ); + void setRotationOffset ( void ); + GLuint compileGlyph ( FT_Face face, FT_UInt glyph_index ); + protected: + void clearCaches ( void ); + }; + + //! Render text as a polygon outline. + /*! + * \image html outline_class.png + * Text is drawn as an outline of each glyph. The contours are extracted + * from the font file through FreeType. FreeType is used to scale the + * contours to a given size. Usually the outline is drawn in the foreground + * color, however, you can specify a ColorTess object to provide a color + * for each vertex individually. You can also use + * the per-glyph display list functionality to alter the attributes + * of each glyph. + * + * The only complexity to this style is selecting the point size. Since + * the outlines are drawn as a polygon, they are subject to the MODELVIEW + * transformation. The point size is nominally chosen to be the same as a + * raster image generated at the given resolution. Some experimentation + * with point size and resolution may be necessary to achieve the desired + * results. + */ + class Outline : public Polygonal { + public: + /*! + * \param filename the filename which contains the font face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Outline ( const char* filename, float point_size = 12, + FT_UInt resolution = 100 ); + + /*! + * \param data_base the memory location (base pointer) which contains the font face. + * \param data_size the size (in bytes) of the font data found at \ref data_base. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Outline ( const FT_Byte* data_base, const FT_Long data_size, + float point_size = 12, FT_UInt resolution = 100 ); + + /*! + * \param face open FreeType FT_Face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Outline ( FT_Face face, float point_size = 12, FT_UInt resolution = 100 ); + + /*! + * The destructor doesn't do anything in particular. + */ + ~Outline ( void ); + private: + void init ( void ); + void renderGlyph ( FT_Face face, FT_UInt glyph_index ); + static int moveToCallback ( FT_Vector* to, Outline* outline ); + static int lineToCallback ( FT_Vector* to, Outline* outline ); + static int conicToCallback ( FT_Vector* control, FT_Vector* to, Outline* outline ); + static int cubicToCallback ( FT_Vector* control1, FT_Vector* control2, + FT_Vector* to, Outline* outline ); + }; + + //! Render text as a filled polygons. + /*! + * \image html filled_class.png + * Each glyph is drawn as a filled polygon. The contours are extracted + * from the font file through FreeType. FreeType is used to scale the + * contours to the given size. Then the GLU tessellation routines are used + * to tessellate the contours into polygons (well, triangles). By default, + * these are drawn in GL_FILL polygon mode, but any other polygon mode + * can be specified. + * + * Usually, the polygons are drawn only in the + * foreground color, however, you may supply ColorTess and TextureTess + * objects which can alter the color or texture coordinates of each + * vertex individually. You can also use + * the per-glyph display list functionality to alter the attributes + * of each glyph. + * + * The only complexity to this style is selecting the point size. Since + * the glyphs are drawn as polygons, they are subject to the viewing and + * modeling transformations. The point size is nominally chosen to be the same + * as a raster image generated at the given resolution. Some experimentation + * with point size and resolution may be necessary to achieve the desired + * results. + */ + class Filled : public Polygonal { + //! 3D tessellation of glyphs is accomplished through the standard GLU + //! routines + GLUtesselator* tess_obj_; + + //! A place to store any extra vertices generated by the Combine callback + VertexInfoList extra_vertices_; + + protected: + //! Offset the glyph in the Z direction. Solely for the Solid subclass. + //! Until I can figure out how to shift the glyph outside the context + //! of this class, I guess this has got to stay (but it is redundant + //! to extrusion_.depth_) + GLfloat depth_offset_; + + public: + /*! + * \param filename the filename which contains the font face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Filled ( const char* filename, float point_size = 12, + FT_UInt resolution = 100 ); + + /*! + * \param data_base the memory location (base pointer) which contains the font face. + * \param data_size the size (in bytes) of the font data found at \ref data_base. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Filled ( const FT_Byte* data_base, const FT_Long data_size, + float point_size = 12, FT_UInt resolution = 100 ); + + /*! + * \param face open FreeType FT_Face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Filled ( FT_Face face, float point_size = 12, FT_UInt resolution = 100 ); + /*! + * The destructor deletes the GLU tessellation object allocated in + * in the constructor. + */ + OGLFT_API virtual ~Filled ( void ); + + /*! + * \return the list of extra vertices created by the GLU tessellation + * combine callback. + */ + OGLFT_API VertexInfoList& extraVertices ( void ) { return extra_vertices_; } + + protected: + void renderGlyph ( FT_Face face, FT_UInt glyph_index ); + private: + void init ( void ); + static int moveToCallback ( FT_Vector* to, Filled* filled ); + static int lineToCallback ( FT_Vector* to, Filled* filled ); + static int conicToCallback ( FT_Vector* control, FT_Vector* to, Filled* filled); + static int cubicToCallback ( FT_Vector* control1, FT_Vector* control2, + FT_Vector* to, Filled* filled ); + static void vertexCallback ( VertexInfo* vertex ); + static void beginCallback ( GLenum which ); + static void endCallback ( void ); + static void combineCallback ( GLdouble coords[3], void* vertex_data[4], + GLfloat weight[4], void** out_data, + Filled* filled ); + static void errorCallback ( GLenum error_code ); + }; + +#ifndef OGLFT_NO_SOLID + //! Render text as solid letters. + /*! + * \image html solid_class.png + * Each glyph is drawn as a closed solid. The contours are extracted + * from the font file through FreeType. FreeType is used to scale the + * contours to the given size. The contours are passed to the GLE + * tubing and extrusion library to create the sides of the solid. + * Then the GLU tessellation routines are used + * to tessellate the contours into polygons which are used to cap the sides. + * + * Currently, the solids are drawn only in the foreground color. However, + * proper surface normals are computed so that the solids may be lighted. + * Eventually, you'll be able to supply a color/texture + * coordinate function to make glyphs more interesting. Note that you can use + * the per-glyph display list functionality to alter each glyph individually. + * + * Another TODO item is to improve the interaction with GLE. Currently, + * you can only create block solids. Eventually, we'll have the capability + * add bevels and rounds to the edges of the solids and maybe even more + * general extrusions (like, for example, the swooshing letters in the title + * sequence of the Salkind's 1978 "Superman" movie). + * + * The only complexity to this style is selecting the point size. Since + * the glyphs are drawn as a collection of polygons, they are subject to the + * viewing and modeling transformations. The point size is nominally chosen + * to be the same as a raster image generated at the given resolution. + * Some experimentation with point size and resolution may be necessary to + * achieve the desired results. + */ + class Solid : public Filled { + private: + + //! Callbacks for FreeType glyph decomposition into outlines (note: this + //! has the same name as the variable in Polygonal, but it is distinct since + //! the routines for the GLE contouring are different from the Filled + //! GLU tessellation routines. This may be too confusing?) + FT_Outline_Funcs interface_; + + //! For now, you can only get block extruded solids + static const unsigned int N_POLYLINE_PTS = 4; + + //! Data for the gleExtrusion routine + struct glePoint2D { + double p_[2]; + glePoint2D ( double p[2] ) { p_[X] = p[X]; p_[Y] = p[Y]; } + glePoint2D ( double x, double y ) { p_[X] = x; p_[Y] = y; } + glePoint2D ( const VertexInfo& v ) { p_[X] = v.v_[X]; p_[Y] = v.v_[Y]; } + }; + + //! Collect all the output from GLE in one of these structures. + struct { + double depth_; + struct { + int x_, y_; + } normal_sign_; + std::vector< glePoint2D > contour_; + std::vector< glePoint2D > contour_normals_; + gleDouble up_[3]; + int n_polyline_pts_; + gleDouble point_array_[N_POLYLINE_PTS][3]; + } extrusion_; + + public: + /*! + * \param filename the filename which contains the font face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Solid ( const char* filename, float point_size = 12, FT_UInt resolution = 100 ); + + /*! + * \param data_base the memory location (base pointer) which contains the font face. + * \param data_size the size (in bytes) of the font data found at \ref data_base. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Solid ( const FT_Byte* data_base, const FT_Long data_size, + float point_size = 12, FT_UInt resolution = 100 ); + + /*! + * \param face open FreeType FT_Face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Solid ( FT_Face face, float point_size = 12, FT_UInt resolution = 100 ); + + /*! + * The destructor doesn't do anything in particular. + */ + OGLFT_API ~Solid ( void ); + /*! + * Set the thickness of the solid + * \param depth thickness of the solid in model units. + */ + OGLFT_API void setDepth ( double depth ); + + /*! + * \return the solid extrusion depth. + */ + OGLFT_API double depth ( void ) const { return extrusion_.depth_; } + + private: + // It would be nice if C/C++ had real matrix notation (like Perl!) + void assign ( gleDouble a[3], double x, double y, double z ) + { + a[X] = x; + a[Y] = y; + a[Z] = z; + } + + void init ( void ); + void renderGlyph ( FT_Face face, FT_UInt glyph_index ); + static int moveToCallback ( FT_Vector* to, Solid* solid ); + static int lineToCallback ( FT_Vector* to, Solid* solid ); + static int conicToCallback ( FT_Vector* control, FT_Vector* to, Solid* solid ); + static int cubicToCallback ( FT_Vector* control1, FT_Vector* control2, + FT_Vector* to, Solid* solid ); + }; +#endif /* OGLFT_NO_SOLID */ + //! This is the base class of the raster styles: bitmap, grayscale and + //! translucent. + /*! + * In the raster styles, FreeType's rasterizer is used to generate raster + * images of each glyph. + */ + class Raster : public Face { + protected: + //! Raster glyph can be rotated in the Z plane (in addition to the string + //! rotation). + GLfloat character_rotation_z_; + public: + /*! + * \param filename the filename which contains the font face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Raster ( const char* filename, float point_size = 12, FT_UInt resolution = 100 ); + + /*! + * \param data_base the memory location (base pointer) which contains the font face. + * \param data_size the size (in bytes) of the font data found at \ref data_base. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Raster ( const FT_Byte* data_base, const FT_Long data_size, + float point_size = 12, FT_UInt resolution = 100 ); + + /*! + * \param face open FreeType FT_Face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Raster ( FT_Face face, float point_size = 12, FT_UInt resolution = 100 ); + /*! + * The destructor doesn't do anything in particular. + */ + OGLFT_API virtual ~Raster ( void ); + /*! + * Set the individual character rotation in the Z direction. + * \param character_rotation_z angle in degrees of Z rotation. + */ + OGLFT_API void setCharacterRotationZ ( GLfloat character_rotation_z ); + /*! + * \return the character rotation in the Z direction. + */ + OGLFT_API GLfloat characterRotationZ ( void ) const { return character_rotation_z_; } + + /*! + * \return the height (i.e., line spacing) at the current character size. + */ + OGLFT_API double height ( void ) const; + + /*! + * Implement measuring a character in a raster face. + * \param c the (latin1) character to measure + * \return the bounding box of c. + */ + OGLFT_API BBox measure ( unsigned char c ); +#ifndef OGLFT_NO_QT + /*! + * Implement measuring a character in a raster face. + * \param c the (UNICODE) character to measure + * \return the bounding box of c. + */ + OGLFT_API BBox measure ( const QChar c ); +#endif /* OGLFT_NO_QT */ + /*! + * Measure a string of characters. Note: currently, this merely + * calls Face's measure routine. + * \param s string of (latin1) characters to measure + * \return the bounding box of s. + */ + OGLFT_API BBox measure ( const char* s ) { return Face::measure( s ); } +#ifndef OGLFT_NO_QT + /*! + * Implement measuring a formatted number + * \param format the format string + * \param number to value to format + * \return the bounding box of the formatted number + */ + OGLFT_API BBox measure ( const QString& format, double number ); +#endif /* OGLFT_NO_QT */ + + private: + void init ( void ); + GLuint compileGlyph ( FT_Face face, FT_UInt glyph_index ); + void setCharSize ( void ); + void setRotationOffset ( void ); + void clearCaches ( void ); + }; + + //! Render text as a monochrome raster image. + /*! + * \image html monochrome_class.png + * This is more or less the standard way in which text is intended to + * be rendered in OpenGL. It uses the \c glBitmap call to draw a sequence + * of monochrome bitmaps. Since FreeType is capable of rotating glyphs + * created from faces based on vector outlines, you can rotate (in the Z plane) + * both the text string as well as the individual characters in the string. + * + * Note: you \em must call + * \code + * glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + * \endcode + * before drawing in order for monochrome glyphs to be rendered properly. + * + * Another note: It is helpful to have the option + * \c GL_RASTER_POSITION_UNCLIPPED_IBM available if you intend to draw text + * at MODELVIEW based positions, otherwise if the initial text position is off + * the screen, the entire image is clipped. + */ + class Monochrome : public Raster { + public: + /*! + * \param filename the filename which contains the font face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Monochrome ( const char* filename, float point_size = 12, + FT_UInt resolution = 100 ); + + /*! + * \param data_base the memory location (base pointer) which contains the font face. + * \param data_size the size (in bytes) of the font data found at \ref data_base. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Monochrome ( const FT_Byte* data_base, const FT_Long data_size, + float point_size = 12, FT_UInt resolution = 100 ); + + /*! + * \param font open FreeType FT_Face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Monochrome ( FT_Face face, float point_size = 12, FT_UInt resolution = 100 ); + /*! + * The destructor doesn't do anything in particular. + */ + OGLFT_API ~Monochrome ( void ); + private: + GLubyte* invertBitmap ( const FT_Bitmap& bitmap ); + void renderGlyph ( FT_Face face, FT_UInt glyph_index ); + }; + + //! Render text as a grayscale raster image. + /*! + * \image html grayscale_class.png + * The Grayscale style is similar to the Monochrome style. FreeType is used + * to rasterize a glyph and this is then drawn on the screen using + * \c glDrawPixels. The FreeType rasterization is done in anti-aliased mode. + * When Grayscale draws the glyph image, the resulting text is blended + * smoothly from the foreground color to the background color. The background + * of the glyph is opaque, so this style works best over a solid background. + * + * Note: you \em must call + * \code + * glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + * \endcode + * before drawing in order for grayscale glyphs to be rendered properly. + * + * Another note: It is helpful to have the option + * \c GL_RASTER_POSITION_UNCLIPPED_IBM available if you intend to draw text + * at MODELVIEW based positions, otherwise if the initial text position is off + * the screen, the entire image is clipped. + */ + class Grayscale : public Raster { + public: + /*! + * \param filename the filename which contains the font face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Grayscale ( const char* filename, float point_size = 12, + FT_UInt resolution = 100 ); + + /*! + * \param data_base the memory location (base pointer) which contains the font face. + * \param data_size the size (in bytes) of the font data found at \ref data_base. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Grayscale ( const FT_Byte* data_base, const FT_Long data_size, + float point_size = 12, FT_UInt resolution = 100 ); + + /*! + * \param face open FreeType FT_Face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Grayscale ( FT_Face face, float point_size = 12, FT_UInt resolution = 100 ); + /*! + * The destructor doesn't do anything in particular. + */ + OGLFT_API ~Grayscale ( void ); + private: + GLubyte* invertPixmap ( const FT_Bitmap& bitmap ); + void renderGlyph ( FT_Face face, FT_UInt glyph_index ); + }; + + //! Render text as a translucent raster image. + /*! + * \image html translucent_class.png + * The Translucent style is similar to the Grayscale style. FreeType is used + * to rasterize a glyph and this is then drawn on the screen using + * \c glDrawPixels. The FreeType rasterization is done in anti-aliased mode. + * When Translucent draws the glyph image, the grayscale levels provided + * by FreeType are used as Alpha values in the raster image. This allows + * the glyphs to be smoothly blended into complicated backgrounds. + * + * Note: you \em must call + * \code + * glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + * \endcode + * before drawing in order for translucent glyphs to be rendered properly. + * Additionally, you need to activate blending in order to achieve the + * translucent effect: + * \code + * glEnable( GL_BLEND ); + * glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + * \endcode + * + * Another note: It is helpful to have the option + * \c GL_RASTER_POSITION_UNCLIPPED_IBM available if you intend to draw text + * at MODELVIEW based positions, otherwise if the initial text position is off + * the screen, the entire image is clipped. + */ + class Translucent : public Raster { + public: + /*! + * \param filename the filename which contains the font face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Translucent ( const char* filename, float point_size = 12, + FT_UInt resolution = 100 ); + + /*! + * \param data_base the memory location (base pointer) which contains the font face. + * \param data_size the size (in bytes) of the font data found at \ref data_base. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Translucent ( const FT_Byte* data_base, const FT_Long data_size, + float point_size = 12, FT_UInt resolution = 100 ); + + /*! + * \param face open FreeType FT_Face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Translucent ( FT_Face face, float point_size = 12, FT_UInt resolution = 100 ); + + /*! + * The destructor doesn't do anything in particular. + */ + OGLFT_API ~Translucent ( void ); + + private: + GLubyte* invertPixmapWithAlpha ( const FT_Bitmap& bitmap ); + void renderGlyph ( FT_Face face, FT_UInt glyph_index ); + }; + + //! This is the base class of the texture style. + class Texture : public Face { + protected: + //! Angle of rotation of characters relative to text orientation. + struct { + bool active_; //!< Is character rotation non-zero? (faster than checking all + //!< the other values.) + GLfloat x_, //!< Angle of rotation in the X direction. + y_, //!< Angle of rotation in the Y direction. + z_; //!< Angle of rotation in the Z direction. + } character_rotation_; + + /*! + * The textured glyphs need a little bit more infrastructure to draw + * since we have to remember the size of the texture object itself + * (at least implicitly). Also, we don't want to create any more + * texture objects than we have to, so they are always cached. + */ + struct TextureInfo { + GLuint texture_name_; //!< A bound texture name is an integer in OpenGL. + FT_Int left_bearing_, //!< The left bearing of the transformed glyph. + bottom_bearing_; //!< The bottom bearing of the transformed glyph. + int width_, //!< The 2**l width of the texture. + height_; //!< The 2**m height of the texture. + GLfloat texture_s_, //!< The fraction of the texture width occupied + //!< by the glyph. + texture_t_; //!< The fraction of the texture height occupied + //!< by the glyph. + FT_Vector advance_; //!< The advance vector of the transformed glyph. + }; + + //! Type of the cache of defined glyph to texture objects mapping. + typedef std::map< FT_UInt, TextureInfo > GlyphTexObjs; + + //! A convenience definition of the iterator over the glyph to texture + //! object map. + typedef GlyphTexObjs::const_iterator GTOCI; + + //! A convenience definition of the iterator over the glyph to texture + //! object map. + typedef GlyphTexObjs::iterator GTOI; + + //! Cache of defined glyph texture objects. + GlyphTexObjs glyph_texobjs_; + + public: + /*! + * \param filename the filename which contains the font face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Texture ( const char* filename, float point_size = 12, + FT_UInt resolution = 100 ); + + /*! + * \param data_base the memory location (base pointer) which contains the font face. + * \param data_size the size (in bytes) of the font data found at \ref data_base. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Texture ( const FT_Byte* data_base, const FT_Long data_size, + float point_size = 12, FT_UInt resolution = 100 ); + + /*! + * \param face open FreeType FT_Face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API Texture ( FT_Face face, float point_size = 12, FT_UInt resolution = 100 ); + + /*! + * The texture destructor doesn't really do anything. + */ + OGLFT_API virtual ~Texture ( void ); + /*! + * Set the individual character rotation in the X direction. + * \param character_rotation_x angle in degrees of X rotation. + */ + OGLFT_API void setCharacterRotationX ( GLfloat character_rotation_x ); + + /*! + * Set the individual character rotation in the Y direction. + * \param character_rotation_y angle in degrees of Y rotation. + */ + OGLFT_API void setCharacterRotationY ( GLfloat character_rotation_y ); + + /*! + * Set the individual character rotation in the Z direction. + * \param character_rotation_z angle in degrees of Z rotation. + */ + OGLFT_API void setCharacterRotationZ ( GLfloat character_rotation_z ); + + /*! + * \return the character rotation in the X direction. + */ + OGLFT_API GLfloat characterRotationX ( void ) const { return character_rotation_.x_; } + + /*! + * \return the character rotation in the Y direction. + */ + OGLFT_API GLfloat characterRotationY ( void ) const { return character_rotation_.y_; } + + /*! + * \return the character rotation in the Z direction. + */ + OGLFT_API GLfloat characterRotationZ ( void ) const { return character_rotation_.z_; } + + /*! + * \return the height (i.e., line spacing) at the current character size. + */ + OGLFT_API double height ( void ) const; + + /*! + * Implement measuring a character in a texture face. + * \param c the (latin1) character to measure + * \return the bounding box of c. + */ + OGLFT_API BBox measure ( unsigned char c ); +#ifndef OGLFT_NO_QT + /*! + * Implement measuring a character in a texture face. + * \param c the (UNICODE) character to measure + * \return the bounding box of c. + */ + OGLFT_API BBox measure ( const QChar c ); +#endif /* OGLFT_NO_QT */ + /*! + * Measure a string of characters. Note: currently, this merely + * calls Face's measure routine. + * \param s string of (latin1) characters to measure + * \return the bounding box of s. + */ + OGLFT_API BBox measure ( const char* s ) { return Face::measure( s ); } +#ifndef OGLFT_NO_QT + OGLFT_API BBox measure ( const QString& s ) + { return Face::measure( s ); } + + /*! + * Implement measuring a formatted number + * \param format the format string + * \param number to value to format + * \return the bounding box of the formatted number + */ + OGLFT_API BBox measure ( const QString& format, double number ) + { return Face::measure( format, number ); } +#endif /* OGLFT_NO_QT */ + + protected: + /*! + * OpenGL texture maps have to be a power of 2 in width and height (including + * apparently 1 = 2**0 ). This function returns the next higher power of + * 2 of the argument. If the argument is already a power of 2, you just + * get that back. + * \param a width or height of an image. + * \return value of a rounded to nearest, higher power of 2. + */ + unsigned int nearestPowerCeil ( unsigned int a ); + /*! + * This is all that distinguishes the various texture styles. Each subclass + * defines this method as appropriate. Once the texture is bound, it + * is rendered the same in all cases. + * \param face FT_Face containing the glyph to render. + * \param glyph_index index of glyph in face. + */ + virtual void bindTexture ( FT_Face face, FT_UInt glyph_index ) = 0; + + private: + void init ( void ); + void setCharSize ( void ); + void setRotationOffset ( void ); + GLuint compileGlyph ( FT_Face face, FT_UInt glyph_index ); + void renderGlyph ( FT_Face face, FT_UInt glyph_index ); + void clearCaches ( void ); + }; + + //! Render text as texture mapped monochrome quads. + /*! + * \image html texture_monochrome_class.png + * This style is similar to the Monochrome raster style, except instead + * of using \c glBitmap to draw the raster image, the image is used + * as a texture map on a quad. If drawing is confined to the Z plane, + * then you will see no difference between this style and Monochrome. + * However, because the quad is a 3D object, it can be transformed + * by the usual modeling operations; so, texture mapped glyphs can be + * rotated in the X and Y directions as well as Z direction. Also, + * if the viewing (or modeling) transformation has a non-unity scale or + * shear, the glyphs will also be scaled or sheared (unlike the raster + * styles). Also, there is no problem with clipping glyphs which lie + * off the screen; texture mapped quads are properly clipped to the + * screen boundary. + * + * If this is not convincing enough, the performance of texture mapped + * glyphs is generally as good as or better than the equivalent + * raster style (especially with hardware texture acceleration). However, + * they do take up more memory space. + * + * Note: you \em must call + * \code + * glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + * \endcode + * before drawing in order for textured glyphs to be rendered properly. + */ + class MonochromeTexture : public Texture { + public: + /*! + * \param filename the filename which contains the font face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API MonochromeTexture ( const char* filename, float point_size = 12, + FT_UInt resolution = 100 ); + + /*! + * \param data_base the memory location (base pointer) which contains the font face. + * \param data_size the size (in bytes) of the font data found at \ref data_base. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API MonochromeTexture ( const FT_Byte* data_base, const FT_Long data_size, + float point_size = 12, FT_UInt resolution = 100 ); + + /*! + * \param face open FreeType FT_Face + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API MonochromeTexture ( FT_Face face, float point_size = 12, + FT_UInt resolution = 100 ); + /*! + * The monochrome texture destructor doesn't really do anything. + */ + OGLFT_API ~MonochromeTexture ( void ); + private: + GLubyte* invertBitmap ( const FT_Bitmap& bitmap, int* width, int* height ); + void bindTexture ( FT_Face face, FT_UInt glyph_index ); + }; + + //! Render text as texture mapped grayscale quads. + /*! + * \image html texture_grayscale_class.png + * This style is similar to the Grayscale raster style, except instead + * of using \c glDrawPixels to draw the raster image, the image is used + * as a texture map on a quad. If drawing is confined to the Z plane, + * then you will see no difference between this style and Grayscale. + * However, because the quad is a 3D object, it can be transformed + * by the usual modeling operations; so, texture mapped glyphs can be + * rotated in the X and Y directions as well as Z direction. Also, + * if the viewing (or modeling) transformation has a non-unity scale or + * shear, the glyphs will also be scaled or sheared (unlike the raster + * styles). Also, there is no problem with clipping glyphs which lie + * off the screen; texture mapped quads are properly clipped to the + * screen boundary. + * + * If this is not convincing enough, the performance of texture mapped + * glyphs is generally as good as or better than the equivalent + * raster style (especially with hardware texture acceleration). However, + * they do consume more memory space. + * + * Note: you \em must call + * \code + * glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + * \endcode + * before drawing in order for textured glyphs to be rendered properly. + */ + class GrayscaleTexture : public Texture { + public: + /*! + * \param filename the filename which contains the font face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API GrayscaleTexture ( const char* filename, float point_size = 12, + FT_UInt resolution = 100 ); + + /*! + * \param data_base the memory location (base pointer) which contains the font face. + * \param data_size the size (in bytes) of the font data found at \ref data_base. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API GrayscaleTexture ( const FT_Byte* data_base, const FT_Long data_size, + float point_size = 12, FT_UInt resolution = 100 ); + + /*! + * \param face open FreeType FT_Face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API GrayscaleTexture ( FT_Face face, float point_size = 12, + FT_UInt resolution = 100 ); + /*! + * The grayscale texture destructor doesn't really do anything. + */ + OGLFT_API ~GrayscaleTexture ( void ); + private: + GLubyte* invertPixmap ( const FT_Bitmap& bitmap, int* width, int* height ); + void bindTexture ( FT_Face face, FT_UInt glyph_index ); + }; + + //! Render text as texture mapped translucent quads. + /*! + * \image html texture_translucent_class.png + * This style is similar to the Translucent raster style, except instead + * of using \c glDrawPixels to draw the raster image, the image is used + * as a texture map on a quad. If drawing is confined to the Z plane, + * then you will see no difference between this style and Translucent. + * However, because the quad is a 3D object, it can be transformed + * by the usual modeling operations; so, texture mapped glyphs can be + * rotated in the X and Y directions as well as Z direction. Also, + * if the viewing (or modeling) transformation has a non-unity scale or + * shear, the glyphs will also be scaled or sheared (unlike the raster + * styles). Also, there is no problem with clipping glyphs which lie + * off the screen; texture mapped quads are properly clipped to the + * screen boundary. + * + * If this is not convincing enough, the performance of texture mapped + * glyphs is generally as good as or better than the equivalent + * raster style (especially with hardware texture acceleration). However, + * they do consume more memory space. + * + * Note: you \em must call + * \code + * glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + * \endcode + * before drawing in order for textured glyphs to be rendered properly. + * Additionally, you need to activate blending in order to achieve the + * translucent effect: + * \code + * glEnable( GL_BLEND ); + * glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + * \endcode + */ + class TranslucentTexture : public Texture { + public: + /*! + * \param filename the filename which contains the font face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API TranslucentTexture ( const char* filename, float point_size = 12, + FT_UInt resolution = 100 ); + + /*! + * \param data_base the memory location (base pointer) which contains the font face. + * \param data_size the size (in bytes) of the font data found at \ref data_base. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API TranslucentTexture ( const FT_Byte* data_base, const FT_Long data_size, + float point_size = 12, FT_UInt resolution = 100 ); + + /*! + * \param face open FreeType FT_Face. + * \param point_size the initial point size of the font to generate. A point + * is essentially 1/72th of an inch. Defaults to 12. + * \param resolution the pixel density of the display in dots per inch (DPI). + * Defaults to 100 DPI. + */ + OGLFT_API TranslucentTexture ( FT_Face face, float point_size = 12, + FT_UInt resolution = 100 ); + /*! + * The translucent texture destructor doesn't really do anything. + */ + OGLFT_API ~TranslucentTexture ( void ); + private: + GLubyte* invertPixmap ( const FT_Bitmap& bitmap, int* width, int* height ); + void bindTexture ( FT_Face face, FT_UInt glyph_index ); + }; +} // Close OGLFT namespace +#endif /* OGLFT_H */ diff --git a/engine/libraries/oglft/tests/CMakeLists.txt b/engine/libraries/oglft/tests/CMakeLists.txt new file mode 100644 index 0000000..cc85304 --- /dev/null +++ b/engine/libraries/oglft/tests/CMakeLists.txt @@ -0,0 +1,117 @@ +FIND_PACKAGE( GLUT ) + +LINK_LIBRARIES( oglft ${FREETYPE2_LIBRARIES}) + +INCLUDE_DIRECTORIES( ${PROJECT_BINARY_DIR} ) +INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR} ) +INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_BINARY_DIR} ) +INCLUDE_DIRECTORIES( ${FREETYPE2_INCLUDE_DIR} + ${FREETYPE2_INCLUDE_DIR}/freetype2 + ) +INCLUDE_DIRECTORIES( ${GLUT_INCLUDE_DIR} ) +IF( ENABLE_QT ) +IF( DESIRED_QT_VERSION EQUAL 3) + INCLUDE_DIRECTORIES( ${QT_INCLUDE_DIR} ) + LINK_LIBRARIES( oglft ${FREETYPE2_LIBRARIES} ${QT_LIBRARIES} ) +ELSEIF( DESIRED_QT_VERSION EQUAL 4 ) + INCLUDE_DIRECTORIES( ${QT_QTCORE_INCLUDE_DIR} ) + INCLUDE_DIRECTORIES( ${QT_QTGUI_INCLUDE_DIR} ) + INCLUDE_DIRECTORIES( ${QT_QTOPENGL_INCLUDE_DIR} ) + LINK_LIBRARIES( oglft ${FREETYPE2_LIBRARIES} ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY} ) +ENDIF( DESIRED_QT_VERSION EQUAL 3) +ENDIF( ENABLE_QT ) + +ADD_EXECUTABLE( + tutorial1 + tutorial1.cpp +) + +TARGET_LINK_LIBRARIES( + tutorial1 + ${GLUT_LIBRARIES} +) + +ADD_EXECUTABLE( + tutorial2 + tutorial2.cpp +) + +TARGET_LINK_LIBRARIES( + tutorial2 + ${GLUT_LIBRARIES} +) + +ADD_EXECUTABLE( + tutorial3 + tutorial3.cpp +) + +TARGET_LINK_LIBRARIES( + tutorial3 + ${GLUT_LIBRARIES} +) + +ADD_EXECUTABLE( + tutorial3 + tutorial3.cpp +) + +TARGET_LINK_LIBRARIES( + tutorial3 + ${GLUT_LIBRARIES} +) + +ADD_EXECUTABLE( + demo + demo.cpp +) + +TARGET_LINK_LIBRARIES( + demo + ${GLUT_LIBRARIES} +) + +ADD_EXECUTABLE( + demo2 + demo2.cpp +) + +TARGET_LINK_LIBRARIES( + demo2 + ${GLUT_LIBRARIES} +) + +IF( ENABLE_QT ) +IF( DESIRED_QT_VERSION EQUAL 3 ) +KDE3_AUTOMOC( demo3.cpp speedtest.cpp ) +ELSEIF( DESIRED_QT_VERSION EQUAL 4 ) +QT4_AUTOMOC( demo3.cpp speedtest.cpp ) +ENDIF( DESIRED_QT_VERSION EQUAL 3 ) +ADD_EXECUTABLE( + demo3 + demo3.cpp +) + +TARGET_LINK_LIBRARIES( + demo3 + ${OPENGL_LIBRARIES} +) + +ADD_EXECUTABLE( + speedtest + speedtest.cpp +) + +TARGET_LINK_LIBRARIES( + speedtest + ${OPENGL_LIBRARIES} +) +ENDIF( ENABLE_QT ) + +# Copy some files needed by demo3 into the tests directory. +FILE( GLOB images *.png ) +FOREACH( test_png ${images} ) + GET_FILENAME_COMPONENT( png_file ${test_png} NAME ) + CONFIGURE_FILE( ${test_png} + "${CMAKE_CURRENT_BINARY_DIR}/${png_file}" COPYONLY ) +ENDFOREACH( test_png ${images} ) diff --git a/engine/libraries/oglft/tests/Demo3UnicodeExample.h b/engine/libraries/oglft/tests/Demo3UnicodeExample.h new file mode 100644 index 0000000..a4277ea --- /dev/null +++ b/engine/libraries/oglft/tests/Demo3UnicodeExample.h @@ -0,0 +1,211 @@ +/* + * Demo3UnicodeExample.h: Sample font for demo3 example. + * Copyright (C) 2002 lignum Computing, Inc. + * $Id$ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +FT_Byte Demo3UnicodeExample_ttf[] = { +0,1,0,0,0,11,0,128,0,3,0,48,79,83,47,50, +83,120,128,90,0,0,0,188,0,0,0,86,99,109,97,112, +135,81,49,45,0,0,1,20,0,0,1,114,99,118,116,32, +5,238,9,153,0,0,2,136,0,0,0,36,103,108,121,102, +105,160,31,169,0,0,2,172,0,0,4,178,104,101,97,100, +212,135,107,129,0,0,7,96,0,0,0,54,104,104,101,97, +6,249,3,88,0,0,7,152,0,0,0,36,104,109,116,120, +20,76,1,64,0,0,7,188,0,0,0,40,108,111,99,97, +6,150,5,39,0,0,7,228,0,0,0,22,109,97,120,112, +0,78,0,250,0,0,7,252,0,0,0,32,110,97,109,101, +88,93,109,50,0,0,8,28,0,0,3,72,112,111,115,116, +11,139,233,133,0,0,11,100,0,0,0,100,0,1,3,232, +1,144,0,5,0,12,0,200,0,200,0,0,0,200,0,200, +0,200,0,0,0,200,0,49,1,2,0,0,2,0,5,3, +0,0,0,0,0,0,0,0,1,131,0,0,0,96,0,0, +0,0,0,0,0,0,80,102,69,100,0,64,0,0,34,43, +3,164,255,52,0,0,3,164,0,204,0,0,0,1,0,0, +0,0,0,0,0,0,0,2,0,3,0,1,0,0,0,20, +0,1,0,0,0,0,0,108,0,4,0,88,0,0,0,18, +0,16,0,3,0,2,3,169,3,195,3,200,33,38,33,146, +34,7,34,25,34,43,255,255,0,0,3,169,3,195,3,200, +33,38,33,146,34,7,34,25,34,43,255,255,252,90,252,65, +252,61,222,221,222,116,222,0,221,239,221,222,0,1,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,1,6,0,0,1,0,0,0,0,0,0,0,1,3, +0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,1,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,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,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,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,33,2,121,0,0,0,86, +0,83,1,30,0,100,0,12,0,74,2,25,0,92,2,222, +0,90,0,50,3,176,0,41,0,102,0,78,0,2,0,33, +0,0,1,110,2,154,0,3,0,7,0,46,177,1,0,47, +60,178,7,4,0,237,50,177,6,5,220,60,178,3,2,0, +237,50,0,177,3,0,47,60,178,5,4,0,237,50,178,7, +6,1,252,60,178,1,2,0,237,50,51,17,33,17,37,33, +17,33,33,1,77,254,212,1,11,254,245,2,154,253,102,33, +2,88,0,1,0,48,0,0,2,206,2,234,0,38,0,137, +0,177,20,2,63,176,21,47,176,37,51,176,38,51,177,0, +3,237,177,1,3,237,177,18,3,237,177,19,3,237,176,6, +47,176,28,51,176,29,51,176,30,51,177,10,4,237,1,176, +0,47,176,38,51,177,36,5,237,177,37,5,237,176,2,47, +176,3,51,176,4,51,176,5,51,177,6,6,237,177,33,6, +237,177,34,6,237,177,35,6,237,176,21,176,36,16,222,176, +22,50,177,19,5,237,177,20,5,237,176,23,176,34,16,222, +176,24,50,176,25,50,177,15,6,237,177,16,6,237,177,17, +6,237,177,40,16,16,204,48,49,55,51,38,53,39,54,55, +54,55,54,23,22,23,22,23,22,21,20,7,51,21,33,53, +54,61,1,54,39,38,7,38,7,49,6,23,20,23,21,33, +48,160,151,1,1,101,24,28,74,120,92,58,104,36,16,152, +160,254,226,178,3,71,63,95,110,66,53,2,177,254,226,86, +127,182,1,165,99,23,17,50,5,4,37,65,127,55,61,182, +128,86,81,83,216,2,127,82,72,1,2,96,78,104,220,85, +81,0,0,2,0,34,255,243,2,98,2,32,0,19,0,35, +0,98,0,177,8,7,63,176,9,47,176,10,51,177,27,8, +237,177,28,8,237,177,29,8,237,177,0,9,63,176,19,47, +177,1,8,237,177,2,8,237,1,176,13,47,176,14,51,176, +15,51,176,16,51,177,23,10,237,177,24,10,237,177,25,10, +237,176,31,176,24,16,222,176,32,50,176,33,50,177,3,10, +237,177,4,10,237,177,5,10,237,177,6,10,237,177,37,5, +16,204,48,49,1,21,35,22,21,23,20,7,6,35,34,47, +1,38,61,1,52,55,54,23,7,6,7,6,23,6,23,22, +23,54,55,54,39,52,39,38,2,98,145,66,1,73,68,108, +108,66,5,70,73,106,154,87,94,40,22,2,2,81,33,44, +102,38,19,1,75,35,2,19,73,62,130,1,143,67,67,65, +5,70,136,2,141,68,96,40,63,2,87,49,65,134,48,20, +1,1,95,47,61,130,50,23,0,1,0,61,255,52,2,157, +2,19,0,34,0,118,0,176,26,47,176,27,51,177,0,11, +237,177,1,11,237,177,9,11,237,177,10,11,237,177,17,11, +237,177,18,11,237,1,176,0,47,176,33,51,176,34,51,177, +1,12,237,177,2,12,237,177,3,12,237,176,8,176,1,16, +222,176,9,50,176,27,50,176,28,50,177,10,12,237,177,11, +12,237,177,25,12,237,177,26,12,237,176,14,176,10,16,222, +176,15,50,176,16,50,176,17,50,177,18,12,237,177,19,12, +237,177,20,12,237,177,36,18,16,204,48,49,19,51,17,22, +23,22,23,22,23,17,51,17,54,55,54,61,1,17,51,17, +6,7,6,7,6,7,21,35,53,38,39,38,39,38,39,61, +89,2,19,13,29,39,68,90,124,32,13,89,1,24,20,51, +59,103,90,138,63,43,9,5,1,2,19,254,250,97,34,25, +20,26,3,1,211,254,45,4,86,38,76,1,1,6,254,253, +122,47,39,35,38,3,192,192,5,68,48,55,32,76,0,1, +0,39,0,78,3,216,1,182,0,18,0,37,0,176,11,47, +176,12,51,177,13,13,237,177,14,13,237,1,176,12,47,176, +13,51,177,3,14,237,177,4,14,237,177,20,3,16,204,48, +49,1,22,31,1,21,6,7,6,7,35,54,55,33,53,33, +38,39,53,39,3,10,91,64,51,86,76,19,26,39,47,52, +252,227,3,29,51,20,26,1,182,98,41,27,23,44,78,20, +29,101,54,51,66,36,1,51,0,2,0,14,0,0,2,103, +2,182,0,2,0,5,0,23,0,176,3,47,176,5,51,177, +0,15,237,177,1,15,237,1,177,7,1,47,204,48,49,19, +33,1,3,27,1,14,2,88,254,198,168,200,220,2,182,253, +74,2,140,254,29,1,227,0,0,1,0,92,1,53,0,195, +1,156,0,3,0,37,0,176,0,47,176,3,51,177,1,16, +237,177,2,16,237,1,176,0,47,176,1,51,177,2,16,237, +177,3,16,237,177,5,2,16,204,48,49,19,53,51,21,92, +103,1,54,102,102,0,0,1,255,255,255,145,1,25,3,164, +0,46,0,29,176,0,47,176,26,51,176,38,51,176,45,51, +176,46,51,177,16,17,237,177,21,17,237,177,22,17,237,48, +49,27,1,54,55,54,55,54,23,22,21,22,7,6,35,34, +39,38,7,6,31,1,22,7,3,6,7,6,39,38,39,38, +55,54,55,54,23,22,23,22,63,1,54,39,38,39,38,17, +100,9,9,70,14,16,31,22,9,1,28,9,10,17,21,18, +15,8,1,4,20,1,8,4,41,25,43,25,17,21,6,3, +9,20,29,11,7,22,15,1,8,5,1,1,18,1,218,1, +10,155,29,6,2,1,28,13,14,35,13,4,18,19,15,9, +17,72,251,86,254,222,147,52,35,1,1,16,20,35,15,9, +22,11,5,8,24,14,1,15,48,12,8,158,1,1,0,0, +0,1,0,0,0,0,0,0,248,61,157,109,95,15,60,245, +0,19,3,232,0,0,0,0,184,197,19,226,0,0,0,0, +184,197,19,226,255,255,255,52,3,216,3,164,0,0,0,8, +0,2,0,0,0,0,0,0,0,1,0,0,3,32,255,56, +0,0,4,11,255,255,0,10,3,216,0,1,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,10,1,176,0,33, +0,0,0,0,1,77,0,0,2,248,0,48,2,142,0,34, +2,236,0,61,4,11,0,39,2,136,0,14,1,38,0,92, +1,35,255,255,0,0,0,43,0,43,0,43,0,171,1,22, +1,137,1,190,1,222,1,253,2,89,0,0,0,1,0,0, +0,10,0,47,0,2,0,0,0,0,0,2,0,0,0,64, +0,1,0,0,0,64,0,137,0,0,0,0,0,0,0,21, +1,2,0,1,0,0,0,0,0,0,0,65,0,0,0,1, +0,0,0,0,0,1,0,21,0,66,0,1,0,0,0,0, +0,2,0,7,0,88,0,1,0,0,0,0,0,3,0,47, +0,96,0,1,0,0,0,0,0,4,0,21,0,144,0,1, +0,0,0,0,0,5,0,7,0,166,0,1,0,0,0,0, +0,6,0,19,0,174,0,0,0,3,0,0,0,0,0,130, +0,194,0,0,0,3,0,0,0,1,0,42,1,70,0,0, +0,3,0,0,0,2,0,14,1,114,0,0,0,3,0,0, +0,3,0,94,1,130,0,0,0,3,0,0,0,4,0,42, +1,226,0,0,0,3,0,0,0,5,0,14,2,14,0,0, +0,3,0,0,0,6,0,38,2,30,0,3,0,1,4,9, +0,0,0,130,0,194,0,3,0,1,4,9,0,1,0,42, +1,70,0,3,0,1,4,9,0,2,0,14,1,114,0,3, +0,1,4,9,0,3,0,94,1,130,0,3,0,1,4,9, +0,4,0,42,1,226,0,3,0,1,4,9,0,5,0,14, +2,14,0,3,0,1,4,9,0,6,0,38,2,30,67,114, +101,97,116,101,100,32,98,121,32,65,108,108,101,110,32,66, +97,114,110,101,116,116,32,119,105,116,104,32,80,102,97,69, +100,105,116,32,49,46,48,32,40,104,116,116,112,58,47,47, +112,102,97,101,100,105,116,46,115,102,46,110,101,116,41,0, +68,101,109,111,51,32,85,110,105,99,111,100,101,32,69,120, +97,109,112,108,101,0,82,101,103,117,108,97,114,0,80,102, +97,69,100,105,116,32,49,46,48,32,58,32,68,101,109,111, +51,32,85,110,105,99,111,100,101,32,69,120,97,109,112,108, +101,32,58,32,50,53,45,50,45,50,48,55,50,0,68,101, +109,111,51,32,85,110,105,99,111,100,101,32,69,120,97,109, +112,108,101,0,48,48,49,46,48,48,48,0,68,101,109,111, +51,85,110,105,99,111,100,101,69,120,97,109,112,108,101,0, +0,67,0,114,0,101,0,97,0,116,0,101,0,100,0,32, +0,98,0,121,0,32,0,65,0,108,0,108,0,101,0,110, +0,32,0,66,0,97,0,114,0,110,0,101,0,116,0,116, +0,32,0,119,0,105,0,116,0,104,0,32,0,80,0,102, +0,97,0,69,0,100,0,105,0,116,0,32,0,49,0,46, +0,48,0,32,0,40,0,104,0,116,0,116,0,112,0,58, +0,47,0,47,0,112,0,102,0,97,0,101,0,100,0,105, +0,116,0,46,0,115,0,102,0,46,0,110,0,101,0,116, +0,41,0,0,0,68,0,101,0,109,0,111,0,51,0,32, +0,85,0,110,0,105,0,99,0,111,0,100,0,101,0,32, +0,69,0,120,0,97,0,109,0,112,0,108,0,101,0,0, +0,82,0,101,0,103,0,117,0,108,0,97,0,114,0,0, +0,80,0,102,0,97,0,69,0,100,0,105,0,116,0,32, +0,49,0,46,0,48,0,32,0,58,0,32,0,68,0,101, +0,109,0,111,0,51,0,32,0,85,0,110,0,105,0,99, +0,111,0,100,0,101,0,32,0,69,0,120,0,97,0,109, +0,112,0,108,0,101,0,32,0,58,0,32,0,50,0,53, +0,45,0,50,0,45,0,50,0,48,0,55,0,50,0,0, +0,68,0,101,0,109,0,111,0,51,0,32,0,85,0,110, +0,105,0,99,0,111,0,100,0,101,0,32,0,69,0,120, +0,97,0,109,0,112,0,108,0,101,0,0,0,48,0,48, +0,49,0,46,0,48,0,48,0,48,0,0,0,68,0,101, +0,109,0,111,0,51,0,85,0,110,0,105,0,99,0,111, +0,100,0,101,0,69,0,120,0,97,0,109,0,112,0,108, +0,101,0,0,0,2,0,0,0,0,0,0,255,156,0,50, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,10,0,0,0,1,0,2,1,2,1,3, +1,4,1,5,1,6,1,7,0,156,7,117,110,105,48,51, +65,57,5,115,105,103,109,97,3,112,115,105,10,97,114,114, +111,119,114,105,103,104,116,8,103,114,97,100,105,101,110,116, +7,117,110,105,50,50,49,57, +}; +const int Demo3UnicodeExample_ttf_size = sizeof( Demo3UnicodeExample_ttf ); diff --git a/engine/libraries/oglft/tests/Demo3UnicodeExample.sfd b/engine/libraries/oglft/tests/Demo3UnicodeExample.sfd new file mode 100644 index 0000000..57415c0 --- /dev/null +++ b/engine/libraries/oglft/tests/Demo3UnicodeExample.sfd @@ -0,0 +1,949 @@ +SplineFontDB: 1.0 +FontName: Demo3UnicodeExample +FullName: Demo3 Unicode Example +FamilyName: Demo3 Unicode Example +Weight: Medium +Copyright: Created by Allen Barnett with PfaEdit 1.0 (http://pfaedit.sf.net) +Version: 001.000 +ItalicAngle: 0 +UnderlinePosition: -100 +UnderlineWidth: 50 +Ascent: 800 +Descent: 200 +FSType: 12 +PfmFamily: 17 +TTFWeight: 400 +TTFWidth: 5 +Panose: 2 0 5 3 0 0 0 0 0 0 +Encoding: 26 +DisplaySize: -24 +AntiAlias: 1 +BeginChars: 65536 263 +StartChar: space +Encoding: 32 32 +Width: 1000 +EndChar +StartChar: exclam +Encoding: 33 33 +Width: 1000 +EndChar +StartChar: quotedbl +Encoding: 34 34 +Width: 1000 +EndChar +StartChar: numbersign +Encoding: 35 35 +Width: 1000 +EndChar +StartChar: dollar +Encoding: 36 36 +Width: 1000 +EndChar +StartChar: percent +Encoding: 37 37 +Width: 1000 +EndChar +StartChar: ampersand +Encoding: 38 38 +Width: 1000 +EndChar +StartChar: quotesingle +Encoding: 39 39 +Width: 1000 +EndChar +StartChar: parenleft +Encoding: 40 40 +Width: 1000 +EndChar +StartChar: parenright +Encoding: 41 41 +Width: 1000 +EndChar +StartChar: asterisk +Encoding: 42 42 +Width: 1000 +EndChar +StartChar: plus +Encoding: 43 43 +Width: 1000 +EndChar +StartChar: comma +Encoding: 44 44 +Width: 1000 +EndChar +StartChar: hyphen +Encoding: 45 45 +Width: 1000 +EndChar +StartChar: period +Encoding: 46 46 +Width: 1000 +EndChar +StartChar: slash +Encoding: 47 47 +Width: 1000 +EndChar +StartChar: zero +Encoding: 48 48 +Width: 1000 +EndChar +StartChar: one +Encoding: 49 49 +Width: 1000 +EndChar +StartChar: two +Encoding: 50 50 +Width: 1000 +EndChar +StartChar: three +Encoding: 51 51 +Width: 1000 +EndChar +StartChar: four +Encoding: 52 52 +Width: 1000 +EndChar +StartChar: five +Encoding: 53 53 +Width: 1000 +EndChar +StartChar: six +Encoding: 54 54 +Width: 1000 +EndChar +StartChar: seven +Encoding: 55 55 +Width: 1000 +EndChar +StartChar: eight +Encoding: 56 56 +Width: 1000 +EndChar +StartChar: nine +Encoding: 57 57 +Width: 1000 +EndChar +StartChar: colon +Encoding: 58 58 +Width: 1000 +EndChar +StartChar: semicolon +Encoding: 59 59 +Width: 1000 +EndChar +StartChar: less +Encoding: 60 60 +Width: 1000 +EndChar +StartChar: equal +Encoding: 61 61 +Width: 1000 +EndChar +StartChar: greater +Encoding: 62 62 +Width: 1000 +EndChar +StartChar: question +Encoding: 63 63 +Width: 1000 +EndChar +StartChar: at +Encoding: 64 64 +Width: 1000 +EndChar +StartChar: A +Encoding: 65 65 +Width: 1000 +EndChar +StartChar: B +Encoding: 66 66 +Width: 1000 +EndChar +StartChar: C +Encoding: 67 67 +Width: 1000 +EndChar +StartChar: D +Encoding: 68 68 +Width: 1000 +EndChar +StartChar: E +Encoding: 69 69 +Width: 1000 +EndChar +StartChar: F +Encoding: 70 70 +Width: 1000 +EndChar +StartChar: G +Encoding: 71 71 +Width: 1000 +EndChar +StartChar: H +Encoding: 72 72 +Width: 1000 +EndChar +StartChar: I +Encoding: 73 73 +Width: 1000 +EndChar +StartChar: J +Encoding: 74 74 +Width: 1000 +EndChar +StartChar: K +Encoding: 75 75 +Width: 1000 +EndChar +StartChar: L +Encoding: 76 76 +Width: 1000 +EndChar +StartChar: M +Encoding: 77 77 +Width: 1000 +EndChar +StartChar: N +Encoding: 78 78 +Width: 1000 +EndChar +StartChar: O +Encoding: 79 79 +Width: 1000 +EndChar +StartChar: P +Encoding: 80 80 +Width: 1000 +EndChar +StartChar: Q +Encoding: 81 81 +Width: 1000 +EndChar +StartChar: R +Encoding: 82 82 +Width: 1000 +EndChar +StartChar: S +Encoding: 83 83 +Width: 1000 +EndChar +StartChar: T +Encoding: 84 84 +Width: 1000 +EndChar +StartChar: U +Encoding: 85 85 +Width: 1000 +EndChar +StartChar: V +Encoding: 86 86 +Width: 1000 +EndChar +StartChar: W +Encoding: 87 87 +Width: 1000 +EndChar +StartChar: X +Encoding: 88 88 +Width: 1000 +EndChar +StartChar: Y +Encoding: 89 89 +Width: 1000 +EndChar +StartChar: Z +Encoding: 90 90 +Width: 1000 +EndChar +StartChar: bracketleft +Encoding: 91 91 +Width: 1000 +EndChar +StartChar: backslash +Encoding: 92 92 +Width: 1000 +EndChar +StartChar: bracketright +Encoding: 93 93 +Width: 1000 +EndChar +StartChar: asciicircum +Encoding: 94 94 +Width: 1000 +EndChar +StartChar: underscore +Encoding: 95 95 +Width: 1000 +EndChar +StartChar: grave +Encoding: 96 96 +Width: 1000 +EndChar +StartChar: a +Encoding: 97 97 +Width: 1000 +EndChar +StartChar: b +Encoding: 98 98 +Width: 1000 +EndChar +StartChar: c +Encoding: 99 99 +Width: 1000 +EndChar +StartChar: d +Encoding: 100 100 +Width: 1000 +EndChar +StartChar: e +Encoding: 101 101 +Width: 1000 +EndChar +StartChar: f +Encoding: 102 102 +Width: 1000 +EndChar +StartChar: g +Encoding: 103 103 +Width: 1000 +EndChar +StartChar: h +Encoding: 104 104 +Width: 1000 +EndChar +StartChar: i +Encoding: 105 105 +Width: 1000 +EndChar +StartChar: j +Encoding: 106 106 +Width: 1000 +EndChar +StartChar: k +Encoding: 107 107 +Width: 1000 +EndChar +StartChar: l +Encoding: 108 108 +Width: 1000 +EndChar +StartChar: m +Encoding: 109 109 +Width: 1000 +EndChar +StartChar: n +Encoding: 110 110 +Width: 1000 +EndChar +StartChar: o +Encoding: 111 111 +Width: 1000 +EndChar +StartChar: p +Encoding: 112 112 +Width: 1000 +EndChar +StartChar: q +Encoding: 113 113 +Width: 1000 +EndChar +StartChar: r +Encoding: 114 114 +Width: 1000 +EndChar +StartChar: s +Encoding: 115 115 +Width: 1000 +EndChar +StartChar: t +Encoding: 116 116 +Width: 1000 +EndChar +StartChar: u +Encoding: 117 117 +Width: 1000 +EndChar +StartChar: v +Encoding: 118 118 +Width: 1000 +EndChar +StartChar: w +Encoding: 119 119 +Width: 1000 +EndChar +StartChar: x +Encoding: 120 120 +Width: 1000 +EndChar +StartChar: y +Encoding: 121 121 +Width: 1000 +EndChar +StartChar: z +Encoding: 122 122 +Width: 1000 +EndChar +StartChar: braceleft +Encoding: 123 123 +Width: 1000 +EndChar +StartChar: bar +Encoding: 124 124 +Width: 1000 +EndChar +StartChar: braceright +Encoding: 125 125 +Width: 1000 +EndChar +StartChar: asciitilde +Encoding: 126 126 +Width: 1000 +EndChar +StartChar: nonbreakingspace +Encoding: 160 160 +Width: 1000 +EndChar +StartChar: exclamdown +Encoding: 161 161 +Width: 1000 +EndChar +StartChar: cent +Encoding: 162 162 +Width: 1000 +EndChar +StartChar: sterling +Encoding: 163 163 +Width: 1000 +EndChar +StartChar: currency +Encoding: 164 164 +Width: 1000 +EndChar +StartChar: yen +Encoding: 165 165 +Width: 1000 +EndChar +StartChar: brokenbar +Encoding: 166 166 +Width: 1000 +EndChar +StartChar: section +Encoding: 167 167 +Width: 1000 +EndChar +StartChar: dieresis +Encoding: 168 168 +Width: 1000 +EndChar +StartChar: copyright +Encoding: 169 169 +Width: 1000 +EndChar +StartChar: ordfeminine +Encoding: 170 170 +Width: 1000 +EndChar +StartChar: guillemotleft +Encoding: 171 171 +Width: 1000 +EndChar +StartChar: logicalnot +Encoding: 172 172 +Width: 1000 +EndChar +StartChar: softhyphen +Encoding: 173 173 +Width: 1000 +EndChar +StartChar: registered +Encoding: 174 174 +Width: 1000 +EndChar +StartChar: macron +Encoding: 175 175 +Width: 1000 +EndChar +StartChar: degree +Encoding: 176 176 +Width: 1000 +EndChar +StartChar: plusminus +Encoding: 177 177 +Width: 1000 +EndChar +StartChar: twosuperior +Encoding: 178 178 +Width: 1000 +EndChar +StartChar: threesuperior +Encoding: 179 179 +Width: 1000 +EndChar +StartChar: acute +Encoding: 180 180 +Width: 1000 +EndChar +StartChar: mu +Encoding: 181 181 +Width: 1000 +EndChar +StartChar: paragraph +Encoding: 182 182 +Width: 1000 +EndChar +StartChar: periodcentered +Encoding: 183 183 +Width: 1000 +EndChar +StartChar: cedilla +Encoding: 184 184 +Width: 1000 +EndChar +StartChar: onesuperior +Encoding: 185 185 +Width: 1000 +EndChar +StartChar: ordmasculine +Encoding: 186 186 +Width: 1000 +EndChar +StartChar: guillemotright +Encoding: 187 187 +Width: 1000 +EndChar +StartChar: onequarter +Encoding: 188 188 +Width: 1000 +EndChar +StartChar: onehalf +Encoding: 189 189 +Width: 1000 +EndChar +StartChar: threequarters +Encoding: 190 190 +Width: 1000 +EndChar +StartChar: questiondown +Encoding: 191 191 +Width: 1000 +EndChar +StartChar: Agrave +Encoding: 192 192 +Width: 1000 +EndChar +StartChar: Aacute +Encoding: 193 193 +Width: 1000 +EndChar +StartChar: Acircumflex +Encoding: 194 194 +Width: 1000 +EndChar +StartChar: Atilde +Encoding: 195 195 +Width: 1000 +EndChar +StartChar: Adieresis +Encoding: 196 196 +Width: 1000 +EndChar +StartChar: Aring +Encoding: 197 197 +Width: 1000 +EndChar +StartChar: AE +Encoding: 198 198 +Width: 1000 +EndChar +StartChar: Ccedilla +Encoding: 199 199 +Width: 1000 +EndChar +StartChar: Egrave +Encoding: 200 200 +Width: 1000 +EndChar +StartChar: Eacute +Encoding: 201 201 +Width: 1000 +EndChar +StartChar: Ecircumflex +Encoding: 202 202 +Width: 1000 +EndChar +StartChar: Edieresis +Encoding: 203 203 +Width: 1000 +EndChar +StartChar: Igrave +Encoding: 204 204 +Width: 1000 +EndChar +StartChar: Iacute +Encoding: 205 205 +Width: 1000 +EndChar +StartChar: Icircumflex +Encoding: 206 206 +Width: 1000 +EndChar +StartChar: Idieresis +Encoding: 207 207 +Width: 1000 +EndChar +StartChar: Eth +Encoding: 208 208 +Width: 1000 +EndChar +StartChar: Ntilde +Encoding: 209 209 +Width: 1000 +EndChar +StartChar: Ograve +Encoding: 210 210 +Width: 1000 +EndChar +StartChar: Oacute +Encoding: 211 211 +Width: 1000 +EndChar +StartChar: Ocircumflex +Encoding: 212 212 +Width: 1000 +EndChar +StartChar: Otilde +Encoding: 213 213 +Width: 1000 +EndChar +StartChar: Odieresis +Encoding: 214 214 +Width: 1000 +EndChar +StartChar: multiply +Encoding: 215 215 +Width: 1000 +EndChar +StartChar: Oslash +Encoding: 216 216 +Width: 1000 +EndChar +StartChar: Ugrave +Encoding: 217 217 +Width: 1000 +EndChar +StartChar: Uacute +Encoding: 218 218 +Width: 1000 +EndChar +StartChar: Ucircumflex +Encoding: 219 219 +Width: 1000 +EndChar +StartChar: Udieresis +Encoding: 220 220 +Width: 1000 +EndChar +StartChar: Yacute +Encoding: 221 221 +Width: 1000 +EndChar +StartChar: Thorn +Encoding: 222 222 +Width: 1000 +EndChar +StartChar: germandbls +Encoding: 223 223 +Width: 1000 +EndChar +StartChar: agrave +Encoding: 224 224 +Width: 1000 +EndChar +StartChar: aacute +Encoding: 225 225 +Width: 1000 +EndChar +StartChar: acircumflex +Encoding: 226 226 +Width: 1000 +EndChar +StartChar: atilde +Encoding: 227 227 +Width: 1000 +EndChar +StartChar: adieresis +Encoding: 228 228 +Width: 1000 +EndChar +StartChar: aring +Encoding: 229 229 +Width: 1000 +EndChar +StartChar: ae +Encoding: 230 230 +Width: 1000 +EndChar +StartChar: ccedilla +Encoding: 231 231 +Width: 1000 +EndChar +StartChar: egrave +Encoding: 232 232 +Width: 1000 +EndChar +StartChar: eacute +Encoding: 233 233 +Width: 1000 +EndChar +StartChar: ecircumflex +Encoding: 234 234 +Width: 1000 +EndChar +StartChar: edieresis +Encoding: 235 235 +Width: 1000 +EndChar +StartChar: igrave +Encoding: 236 236 +Width: 1000 +EndChar +StartChar: iacute +Encoding: 237 237 +Width: 1000 +EndChar +StartChar: icircumflex +Encoding: 238 238 +Width: 1000 +EndChar +StartChar: idieresis +Encoding: 239 239 +Width: 1000 +EndChar +StartChar: eth +Encoding: 240 240 +Width: 1000 +EndChar +StartChar: ntilde +Encoding: 241 241 +Width: 1000 +EndChar +StartChar: ograve +Encoding: 242 242 +Width: 1000 +EndChar +StartChar: oacute +Encoding: 243 243 +Width: 1000 +EndChar +StartChar: ocircumflex +Encoding: 244 244 +Width: 1000 +EndChar +StartChar: otilde +Encoding: 245 245 +Width: 1000 +EndChar +StartChar: odieresis +Encoding: 246 246 +Width: 1000 +EndChar +StartChar: divide +Encoding: 247 247 +Width: 1000 +EndChar +StartChar: oslash +Encoding: 248 248 +Width: 1000 +EndChar +StartChar: ugrave +Encoding: 249 249 +Width: 1000 +EndChar +StartChar: uacute +Encoding: 250 250 +Width: 1000 +EndChar +StartChar: ucircumflex +Encoding: 251 251 +Width: 1000 +EndChar +StartChar: udieresis +Encoding: 252 252 +Width: 1000 +EndChar +StartChar: yacute +Encoding: 253 253 +Width: 1000 +EndChar +StartChar: thorn +Encoding: 254 254 +Width: 1000 +EndChar +StartChar: ydieresis +Encoding: 255 255 +Width: 1000 +EndChar +StartChar: uni03A9 +Encoding: 937 937 +Width: 760 +Flags: W +HStem: 0 86.5<55 192 560 711> 662.5 82.6191<368 400> +VStem: 48.5 285.5<1 81> 56.5 100.5<368 416> 432.5 285<1 81> 610.5 99<368 416> +Fore +48.5 86.5 m 1 + 208.5 86.5 l 1 + 107.167 171.167 56.5 274.5 56.5 396.5 c 1 + 56.7101 536.75 118.58 644.249 209.5 699.75 c 1 + 297.509 759.342 465.523 760.85 554.25 703.5 c 1 + 642.338 648.475 711.166 540.692 709.5 396.5 c 1 + 709.5 274.5 658.833 171.167 557.5 86.5 c 1 + 717.5 86.5 l 1 + 717.5 0 l 1 + 432.5 0 l 1 + 432.5 81 l 1 + 551.167 136.333 610.5 236.5 610.5 381.5 c 1 + 613.683 531.395 525.526 663.23 384.5 662.5 c 1 + 242.961 665.005 154.417 531.689 157 386.5 c 1 + 157 239.5 216 137.667 334 81 c 1 + 334 0 l 1 + 48.5 0 l 1 + 48.5 86.5 l 1 +EndSplineSet +MinimumDistance: x5,-1 +EndChar +StartChar: sigma +Encoding: 963 963 +Width: 654 +Flags: W +HStem: -11.9998 73.9998<282 299.31> 458 73<466 594> 468.5 74.5483<280.625 302.587> +VStem: 34 92.5<243 265 265.575 298.829> 440 91.5<243 291> +Fore +609.5 531 m 1 + 609.5 458 l 1 + 465 458 l 1 + 509.333 416.667 531.5 352.333 531.5 265 c 0 + 531.5 170 507.25 100.083 458.75 55.25 c 1 + 367.585 -35.1072 191 -34.6289 104 58 c 1 + 57.333 104.667 34 173.833 34 265.5 c 0 + 34 359.833 58.25 429.667 106.75 475 c 1 + 176.138 537.884 259.745 558.789 367 531 c 1 + 609.5 531 l 1 +280.5 468.5 m 1 + 180.415 465.592 122.839 382.568 126.5 265 c 1 + 124.033 145.734 177.982 64.8142 282 62 c 1 + 387.637 63.1845 442.118 149.124 440 265.5 c 1 + 439.941 377.605 391.876 470.041 280.5 468.5 c 1 +EndSplineSet +MinimumDistance: x3,-1 +EndChar +StartChar: psi +Encoding: 968 968 +Width: 748 +Flags: W +HStem: -203.5 734.5<320 410> +VStem: 61 89.5<244 530> 320 90<-188 -12 65 530> 579 89.5<244 530> +Fore +61 531 m 1 + 150.5 531 l 1 + 150.5 269 l 1 + 151.992 173.54 157.865 130.981 213.25 92.75 c 1 + 239.083 75.583 274.667 65.833 320 63.5 c 1 + 320 531 l 1 + 410 531 l 1 + 410 63.5 l 1 + 489.519 66.7047 544.824 98.0574 565.75 154.25 c 1 + 574.583 179.75 579 218 579 269 c 2 + 579 531 l 1 + 668.5 531 l 1 + 668.5 272 l 1 + 666.618 133.548 653.5 84.7044 571.5 29.25 c 1 + 532.5 4.08301 478.667 -9.66699 410 -12 c 1 + 410 -203.5 l 1 + 320 -203.5 l 1 + 320 -12 l 1 + 229.595 -8.79161 164.247 11.9517 118.75 61.25 c 1 + 67.0712 119.571 62.4383 155.542 61 272 c 1 + 61 531 l 1 +EndSplineSet +MinimumDistance: x11,-1 +EndChar +StartChar: arrowright +Encoding: 8594 8594 +Width: 1035 +Flags: W +HStem: 233 50.5<55 836> +VStem: 39.5 944.5<249 271> +Fore +777.5 438 m 1 + 848.958 360.788 901.645 310.333 984 271.5 c 1 + 984 249 l 1 + 897.46 204.953 850.008 159.158 777 78.5 c 1 + 738.5 78.5 l 1 + 769.833 145.5 802.667 197 837 233 c 1 + 39.5 233 l 1 + 39.5 283.5 l 1 + 837 283.5 l 1 + 782.642 353.947 781.915 354.515 739.5 438 c 1 + 777.5 438 l 1 +EndSplineSet +MinimumDistance: x1,-1 +EndChar +StartChar: gradient +Encoding: 8711 8711 +Width: 648 +Flags: W +HStem: 652.5 41<134 551> +DStem: 14.5 693.5 132.5 652.5 300.5 0 332 169 552 652.5 614.5 693.5 332 169 300.5 0 +Fore +14.5 693.5 m 1 + 614.5 693.5 l 1 + 300.5 0 l 1 + 14.5 693.5 l 1 +132.5 652.5 m 1 + 332 169 l 1 + 552 652.5 l 1 + 132.5 652.5 l 1 +EndSplineSet +MinimumDistance: x1,-1 +EndChar +StartChar: uni2219 +Encoding: 8729 8729 +Width: 294 +Flags: W +HStem: 309.5 102.5<108 194> +VStem: 92.5 102.5<325 411> +Fore +92.5 309.5 m 1 + 92.5 412 l 1 + 195 412 l 1 + 195 309.5 l 1 + 92.5 309.5 l 1 +EndSplineSet +MinimumDistance: x2,-1 +EndChar +StartChar: integral +Encoding: 8747 8747 +Width: 291 +Flags: W +VStem: 100.5 77.8252<327.709 474> +Fore +100.5 474 m 1 + 109 739.5 l 1 + 114.542 833.138 134.534 923.622 217.5 931.5 c 1 + 247.522 933.096 280.447 905.565 280 878 c 1 + 280.849 848.918 261.127 825.941 234 826 c 1 + 222.333 826 209.667 832 196 844 c 1 + 176.357 863.99 153.522 851.038 156 822 c 1 + 156 808 157.5 783.833 160.5 749.5 c 1 + 184.169 438.285 182.123 484.679 171 123 c 1 + 168.333 24.667 153.333 -41.833 126 -76.5 c 1 + 101.122 -110.714 47.8807 -123.362 16.5 -93 c 1 + -3.17021 -74.8996 -6.32757 -33.0899 12.75 -13.5 c 1 + 30.257 5.46361 62.8625 4.28698 79.5 -16 c 1 + 93.653 -31.3569 104.804 -38.4598 118 -25 c 1 + 129.244 -4.11259 121.392 22.5244 119 58.5 c 1 + 106.667 163.5 100.5 302 100.5 474 c 1 +EndSplineSet +EndChar +EndChars +EndSplineFont diff --git a/engine/libraries/oglft/tests/Demo3UnicodeExample.ttf b/engine/libraries/oglft/tests/Demo3UnicodeExample.ttf new file mode 100644 index 0000000..e861494 Binary files /dev/null and b/engine/libraries/oglft/tests/Demo3UnicodeExample.ttf differ diff --git a/engine/libraries/oglft/tests/Demo3UnicodeExample2.h b/engine/libraries/oglft/tests/Demo3UnicodeExample2.h new file mode 100644 index 0000000..384ef0c --- /dev/null +++ b/engine/libraries/oglft/tests/Demo3UnicodeExample2.h @@ -0,0 +1,470 @@ +/* + * Demo3UnicodeExample2.h: Additional symbols for demo3 example. + * Copyright (C) 2002 lignum Computing, Inc. + * $Id$ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +FT_Byte lCSymbols_ttf[] = { +0,1,0,0,0,12,0,128,0,3,0,64,79,83,47,50, +81,97,77,142,0,0,0,204,0,0,0,86,99,109,97,112, +224,46,4,241,0,0,1,36,0,0,1,82,99,118,116,32, +4,48,5,221,0,0,2,120,0,0,0,34,103,108,121,102, +109,46,45,11,0,0,2,156,0,0,18,254,104,101,97,100, +211,187,118,221,0,0,21,156,0,0,0,54,104,104,101,97, +6,115,1,120,0,0,21,212,0,0,0,36,104,109,116,120, +28,67,1,223,0,0,21,248,0,0,0,100,107,101,114,110, +251,84,252,187,0,0,22,92,0,0,0,162,108,111,99,97, +51,201,56,104,0,0,23,0,0,0,0,52,109,97,120,112, +0,94,1,24,0,0,23,52,0,0,0,32,110,97,109,101, +31,28,131,163,0,0,23,84,0,0,3,167,112,111,115,116, +142,120,183,88,0,0,26,252,0,0,1,4,0,1,3,232, +1,144,0,5,0,12,0,200,0,200,0,0,0,200,0,200, +0,200,0,0,0,200,0,49,1,2,0,0,2,0,5,3, +0,0,0,0,0,0,0,0,0,3,16,0,0,8,0,0, +0,0,0,0,0,0,80,102,69,100,0,64,0,32,224,25, +2,234,255,52,0,90,2,234,0,204,0,0,0,1,0,0, +0,0,0,0,0,0,0,3,0,0,0,3,0,0,0,28, +0,1,0,0,0,0,0,76,0,3,0,1,0,0,0,28, +0,4,0,48,0,0,0,8,0,8,0,2,0,0,33,4, +224,10,224,25,255,255,0,0,33,4,224,0,224,16,255,255, +222,255,32,4,31,255,0,1,0,0,0,0,0,0,0,0, +0,0,1,6,0,0,1,0,0,0,0,0,0,0,1,3, +0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,1,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,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,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,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,33,2,121,0,86,0,83, +0,100,0,97,1,202,0,37,0,46,1,112,0,43,0,242, +0,48,0,41,0,237,0,0,0,21,0,0,0,2,0,33, +0,0,1,110,2,154,0,3,0,7,0,46,177,1,0,47, +60,178,7,4,0,237,50,177,6,5,220,60,178,3,2,0, +237,50,0,177,3,0,47,60,178,5,4,0,237,50,178,7, +6,1,252,60,178,1,2,0,237,50,51,17,33,17,37,33, +17,33,33,1,77,254,212,1,11,254,245,2,154,253,102,33, +2,88,0,1,0,7,255,52,2,246,2,234,0,47,0,110, +0,176,11,47,176,12,51,177,9,2,237,177,10,2,237,176, +32,47,176,33,51,176,34,51,177,24,3,237,177,25,3,237, +177,26,3,237,1,176,17,47,176,18,51,176,19,51,176,20, +51,177,39,4,237,177,40,4,237,177,41,4,237,177,42,4, +237,176,12,47,176,13,51,176,45,51,176,46,51,177,0,5, +237,177,7,5,237,177,8,5,237,177,9,5,237,177,47,5, +237,176,12,16,177,10,6,237,177,11,6,237,48,49,37,54, +55,23,6,7,6,15,1,21,33,21,33,53,38,47,1,38, +61,1,52,55,54,55,54,51,50,23,22,23,7,38,47,1, +34,7,6,15,1,6,29,1,20,23,22,23,17,51,1,141, +130,31,97,46,161,24,26,1,1,105,254,54,183,70,1,39, +122,22,25,80,97,176,81,20,13,96,45,128,23,135,63,12, +8,3,23,112,39,42,97,76,31,150,24,185,47,7,3,1, +108,86,194,19,166,1,91,104,1,198,100,18,14,44,136,35, +43,22,141,11,1,100,19,22,8,68,72,1,201,67,23,7, +1,198,0,2,0,21,1,97,1,5,2,216,0,12,0,30, +0,109,0,176,7,47,176,8,51,176,9,51,177,10,7,237, +177,17,7,237,177,18,7,237,177,19,7,237,176,26,176,10, +16,222,176,27,50,176,28,50,177,1,7,237,177,2,7,237, +177,3,7,237,1,176,0,47,176,1,51,176,11,51,176,12, +51,177,13,8,237,177,14,8,237,177,30,8,237,176,21,176, +13,16,222,176,22,50,176,23,50,176,24,50,177,5,8,237, +177,6,8,237,177,7,8,237,177,32,6,16,204,48,49,19, +52,51,50,23,22,21,20,35,34,39,38,53,55,20,23,51, +22,51,50,55,54,61,1,52,39,38,35,34,7,6,21,120, +85,26,8,119,53,30,37,47,20,1,21,31,31,21,21,21, +21,32,31,18,23,2,29,187,101,32,54,187,38,45,102,2, +90,30,30,30,30,88,2,90,30,30,27,33,0,1,0,55, +1,104,0,191,2,216,0,13,0,41,0,176,0,47,176,1, +51,177,11,9,237,177,12,9,237,177,13,9,237,1,176,1, +47,176,2,51,177,0,8,237,177,13,8,237,177,15,0,16, +204,48,49,19,35,17,6,7,6,7,53,54,55,54,63,1, +51,191,45,17,26,26,21,37,29,27,12,1,29,1,104,1, +31,16,15,16,8,44,18,25,25,23,1,0,0,1,0,15, +1,104,1,2,2,216,0,37,0,96,0,176,1,47,176,2, +51,177,0,10,237,177,37,10,237,176,15,176,0,16,222,176, +16,50,176,17,50,177,23,7,237,177,24,7,237,177,25,7, +237,1,176,2,47,176,3,51,177,0,11,237,177,1,11,237, +177,28,11,237,177,29,11,237,177,30,11,237,176,29,16,177, +10,8,237,177,11,8,237,177,12,8,237,177,13,8,237,177, +25,8,237,177,39,0,16,204,48,49,1,21,35,38,55,53, +54,55,54,55,54,61,1,52,39,38,35,34,7,21,7,39, +54,63,1,50,23,22,31,1,20,15,1,6,7,6,15,1, +1,2,242,2,36,21,38,60,21,21,41,13,15,56,13,3, +46,8,82,29,70,31,10,2,1,38,1,21,48,52,13,6, +1,147,43,37,42,1,24,32,49,28,28,25,1,43,16,5, +50,1,23,5,89,15,2,50,17,20,15,44,43,1,23,41, +44,17,10,0,0,1,0,21,1,97,1,6,2,216,0,52, +0,133,0,176,48,47,176,49,51,176,50,51,177,2,7,237, +177,3,7,237,177,4,7,237,176,13,176,2,16,222,176,14, +50,176,15,50,177,17,0,237,177,18,0,237,177,19,0,237, +176,26,176,17,16,222,176,27,50,176,28,50,177,33,7,237, +177,34,7,237,177,35,7,237,1,176,21,47,176,22,51,176, +23,51,176,24,51,176,35,51,177,38,8,237,177,39,8,237, +177,40,8,237,177,41,8,237,176,8,47,176,9,51,176,10, +51,176,11,51,177,44,12,237,177,45,12,237,177,46,12,237, +177,54,45,16,204,48,49,19,55,22,51,50,55,61,1,54, +61,1,52,39,38,35,34,7,55,23,50,55,54,61,1,52, +39,38,35,34,7,39,54,55,54,51,50,23,49,22,21,23, +20,7,22,31,1,20,7,6,35,34,39,38,22,44,15,57, +47,21,8,40,15,17,13,19,5,7,55,16,4,34,12,14, +56,10,45,13,63,16,18,59,31,17,1,51,59,8,2,57, +30,37,71,32,11,1,201,6,72,41,1,1,15,17,1,47, +18,6,4,39,1,38,11,12,1,38,14,5,66,8,73,17, +5,42,23,29,1,51,24,13,60,17,65,32,17,57,20,0, +0,2,0,6,1,104,1,4,2,215,0,10,0,13,0,114, +0,176,1,47,176,2,51,176,8,51,176,9,51,177,3,13, +237,177,6,13,237,177,7,13,237,177,11,13,237,177,13,13, +237,177,4,6,16,204,177,5,6,16,204,177,10,1,16,204, +177,0,1,16,204,1,176,0,47,176,1,51,176,11,51,176, +12,51,177,5,8,237,177,6,8,237,177,9,8,237,177,10, +8,237,177,4,6,16,204,177,8,6,16,204,177,7,6,16, +204,177,3,1,16,204,177,2,1,16,204,177,15,7,16,204, +48,49,19,53,35,53,55,51,21,51,21,35,21,39,53,7, +166,160,168,36,50,50,44,115,1,104,88,41,237,237,41,88, +129,165,165,0,0,1,0,21,1,97,1,9,2,210,0,36, +0,102,0,176,32,47,176,33,51,176,34,51,177,3,7,237, +177,4,7,237,177,5,7,237,176,12,176,3,16,222,176,13, +50,176,14,50,177,22,13,237,177,23,13,237,177,24,13,237, +176,19,176,22,16,222,176,20,50,177,17,10,237,177,18,10, +237,1,176,7,47,176,8,51,176,9,51,176,10,51,176,24, +51,177,27,12,237,177,28,12,237,177,29,12,237,177,30,12, +237,177,38,29,16,204,48,49,19,55,22,31,1,50,55,54, +61,1,52,39,38,35,34,7,39,55,51,21,35,7,54,51, +50,23,21,22,29,1,20,7,6,35,34,39,38,21,47,9, +47,15,51,19,7,46,15,17,44,22,43,36,182,146,20,33, +36,66,32,16,57,30,38,76,30,9,1,200,4,58,10,1, +49,18,21,1,59,19,5,35,5,189,44,98,23,55,1,27, +35,1,75,36,19,60,19,0,0,2,0,19,1,97,1,6, +2,216,0,29,0,47,0,136,0,176,17,47,176,18,51,176, +19,51,177,33,7,237,177,34,7,237,177,35,7,237,176,42, +176,33,16,222,176,43,50,176,44,50,176,45,50,177,8,13, +237,177,9,13,237,177,10,13,237,176,2,176,8,16,222,176, +3,50,176,4,50,177,25,7,237,177,26,7,237,177,27,7, +237,177,28,7,237,1,176,21,47,176,22,51,176,23,51,176, +24,51,177,6,10,237,177,7,10,237,176,10,47,176,37,51, +176,38,51,176,39,51,176,40,51,177,12,8,237,177,13,8, +237,177,14,8,237,177,15,8,237,177,49,14,16,204,48,49, +1,7,38,35,34,7,6,21,54,51,50,23,22,29,1,20, +7,6,35,34,39,38,39,53,52,55,54,51,50,23,7,20, +23,22,51,50,55,54,61,1,52,39,38,39,35,34,7,6, +0,255,45,13,49,48,23,13,32,57,64,30,14,52,27,36, +56,35,35,1,117,7,8,73,24,177,36,18,21,46,17,6, +41,11,14,5,47,19,7,2,125,4,58,57,30,55,49,57, +28,33,1,73,34,18,41,41,94,2,184,11,1,63,186,52, +25,11,49,16,19,1,56,19,6,1,44,16,0,1,0,24, +1,104,1,6,2,210,0,16,0,43,0,176,0,47,176,16, +51,177,1,10,237,177,2,10,237,176,9,47,176,10,47,1, +176,0,47,176,1,51,177,2,14,237,177,3,14,237,177,18, +2,16,204,48,49,19,53,51,21,6,7,6,7,6,7,35, +54,55,52,55,54,55,24,238,71,42,6,4,14,3,46,5, +86,1,17,19,2,166,44,35,75,120,16,15,46,55,140,129, +2,1,25,21,0,3,0,20,1,97,1,7,2,216,0,32, +0,51,0,69,0,207,0,176,24,47,176,25,51,176,26,51, +177,55,7,237,177,56,7,237,177,57,7,237,176,17,176,55, +16,222,176,31,50,176,64,50,176,65,50,176,66,50,177,36, +7,237,177,37,7,237,177,38,7,237,176,3,176,36,16,222, +176,45,50,176,46,50,176,47,50,177,5,7,237,177,6,7, +237,177,7,7,237,1,176,28,47,176,29,51,176,30,51,176, +31,51,177,52,8,237,177,53,8,237,177,68,8,237,177,69, +8,237,176,1,47,176,2,51,176,3,51,176,27,51,177,33, +8,237,177,34,8,237,177,50,8,237,177,51,8,237,176,40, +47,176,41,51,176,42,51,176,43,51,177,12,8,237,177,13, +8,237,177,14,8,237,177,15,8,237,176,59,47,176,60,51, +176,61,51,176,62,51,177,17,8,237,177,18,8,237,177,19, +8,237,177,20,8,237,177,21,8,237,177,22,8,237,177,71, +21,16,204,48,49,19,38,53,52,55,54,51,50,31,1,21, +23,22,29,1,20,7,22,23,20,29,1,20,7,6,35,34, +39,38,61,1,52,55,39,20,23,22,51,50,55,54,61,1, +52,39,38,35,34,15,1,6,21,7,20,23,22,51,50,55, +54,61,1,52,39,38,35,34,7,6,21,90,54,54,23,28, +63,29,1,1,12,54,67,2,58,28,34,72,33,16,67,6, +37,11,12,40,14,5,35,12,13,38,16,1,4,15,40,16, +19,48,19,7,41,16,19,47,19,7,2,47,20,55,59,25, +10,46,1,1,1,21,25,1,53,20,22,69,1,1,1,67, +30,14,52,26,33,1,73,19,77,42,13,4,34,10,11,1, +39,15,5,32,1,11,11,170,48,19,8,40,15,17,1,48, +20,7,40,15,17,0,0,2,0,21,1,97,1,7,2,216, +0,35,0,58,0,133,0,176,32,47,176,33,51,176,34,51, +177,2,7,237,177,3,7,237,177,4,7,237,176,11,176,2, +16,222,176,12,50,176,13,50,177,7,13,237,177,8,13,237, +177,53,13,237,177,54,13,237,177,55,13,237,176,41,176,8, +16,222,176,42,50,176,43,50,177,21,7,237,177,22,7,237, +177,23,7,237,1,176,16,47,176,17,51,176,18,51,176,19, +51,177,13,8,237,177,49,8,237,177,50,8,237,177,51,8, +237,176,6,47,176,7,51,176,8,51,176,9,51,177,28,10, +237,177,29,10,237,177,30,10,237,48,49,19,55,22,51,50, +55,54,53,55,39,6,15,1,34,39,53,38,61,1,52,55, +54,51,50,31,1,21,23,22,21,20,7,6,35,34,39,55, +52,39,34,49,38,35,34,15,1,21,7,21,6,21,20,23, +22,51,50,55,54,53,28,43,11,50,61,19,5,1,1,27, +52,8,65,30,14,54,27,34,72,34,1,4,15,60,30,41, +76,22,179,37,1,14,18,45,20,1,1,7,39,15,19,52, +15,4,1,189,4,58,79,24,28,2,8,43,4,1,58,1, +27,33,1,73,34,17,64,1,1,8,36,67,132,43,22,67, +185,54,22,9,45,1,1,1,1,18,21,50,21,8,52,14, +15,0,0,1,0,0,255,243,1,2,2,234,0,3,0,20, +176,0,47,176,1,47,176,2,47,176,3,47,1,177,5,2, +47,204,48,49,21,19,51,3,212,46,214,12,2,246,253,10, +0,2,0,21,255,249,1,5,1,112,0,12,0,31,0,113, +0,176,7,47,176,8,51,176,9,51,177,10,7,237,177,17, +7,237,177,18,7,237,177,19,7,237,176,26,176,10,16,222, +176,27,50,176,28,50,177,1,7,237,177,2,7,237,177,3, +7,237,1,176,0,47,176,1,51,176,11,51,176,12,51,177, +13,8,237,177,14,8,237,177,30,8,237,177,31,8,237,176, +21,176,13,16,222,176,22,50,176,23,50,176,24,50,177,5, +8,237,177,6,8,237,177,7,8,237,177,33,6,16,204,48, +49,55,52,51,50,23,22,21,20,35,34,39,38,53,55,20, +23,51,22,51,50,55,54,61,1,52,39,38,35,34,7,6, +21,21,120,85,26,8,119,53,30,37,47,20,1,21,31,31, +21,21,21,21,32,31,18,23,181,187,101,32,54,187,38,45, +102,2,90,30,30,30,30,88,2,90,30,30,27,33,89,0, +0,1,0,55,0,0,0,191,1,112,0,13,0,42,0,177, +0,15,63,176,1,51,177,11,9,237,177,12,9,237,177,13, +9,237,1,176,1,47,176,2,51,177,0,8,237,177,13,8, +237,177,15,0,16,204,48,49,51,35,17,6,7,6,7,53, +54,55,54,63,1,51,191,45,17,26,26,21,37,29,27,12, +1,29,1,31,16,15,16,8,44,18,25,25,23,1,0,1, +0,15,0,0,1,2,1,112,0,38,0,101,0,177,1,15, +63,176,2,51,177,0,10,237,177,38,10,237,176,14,176,0, +16,222,176,15,50,176,16,50,177,23,7,237,177,24,7,237, +177,25,7,237,1,176,2,47,176,3,51,177,0,11,237,177, +1,11,237,177,28,11,237,177,29,11,237,177,30,11,237,177, +31,11,237,176,28,16,177,9,8,237,177,10,8,237,177,11, +8,237,177,12,8,237,177,25,8,237,177,40,0,16,204,48, +49,37,21,35,38,55,54,55,54,55,54,61,1,52,39,38, +35,34,7,29,1,7,39,54,63,1,50,23,21,22,29,1, +20,15,1,6,7,6,15,1,1,2,242,2,36,21,38,60, +21,21,41,13,15,56,13,3,46,8,82,29,70,31,13,38, +1,21,48,52,13,6,43,43,38,42,24,32,49,28,28,25, +1,43,16,5,50,1,1,22,5,89,15,2,50,1,22,28, +1,44,43,1,23,41,44,17,10,0,0,1,0,21,255,249, +1,6,1,112,0,51,0,133,0,176,47,47,176,48,51,176, +49,51,177,2,7,237,177,3,7,237,177,4,7,237,176,11, +176,2,16,222,176,12,50,176,13,50,177,15,0,237,177,16, +0,237,177,17,0,237,176,24,176,15,16,222,176,25,50,176, +26,50,177,31,7,237,177,32,7,237,177,33,7,237,1,176, +19,47,176,20,51,176,21,51,176,22,51,176,33,51,177,37, +8,237,177,38,8,237,177,39,8,237,177,40,8,237,176,6, +47,176,7,51,176,8,51,176,9,51,177,43,12,237,177,44, +12,237,177,45,12,237,177,53,44,16,204,48,49,63,1,22, +51,50,55,54,61,1,52,39,38,35,34,7,55,23,50,55, +54,61,1,52,39,38,35,34,7,39,54,55,54,51,50,31, +1,49,22,21,23,20,7,22,31,1,20,7,6,39,34,39, +38,22,44,15,57,47,21,8,40,15,17,13,19,5,7,55, +16,4,34,12,14,56,10,45,13,63,16,18,61,30,1,15, +1,51,59,8,2,57,30,37,71,32,11,97,6,72,41,16, +18,1,47,18,6,4,39,1,38,11,12,1,38,14,5,66, +8,73,17,5,43,1,22,28,1,51,24,13,60,17,65,32, +18,1,57,20,0,2,0,6,0,0,1,4,1,111,0,10, +0,13,0,111,0,177,0,15,63,176,10,51,176,1,222,176, +2,50,176,8,50,176,9,50,177,3,13,237,177,6,13,237, +177,7,13,237,177,11,13,237,177,13,13,237,177,4,6,16, +204,177,5,6,16,204,1,176,0,47,176,1,51,176,11,51, +176,12,51,177,5,8,237,177,6,8,237,177,9,8,237,177, +10,8,237,177,4,6,16,204,177,8,6,16,204,177,7,6, +16,204,177,3,1,16,204,177,2,1,16,204,177,15,7,16, +204,48,49,51,53,35,53,55,51,21,51,21,35,21,39,53, +7,166,160,168,36,50,50,44,115,88,41,237,237,41,88,129, +165,165,0,1,0,21,255,249,1,9,1,106,0,36,0,106, +0,176,32,47,176,33,51,176,34,51,177,3,7,237,177,4, +7,237,177,5,7,237,177,6,7,237,176,13,176,3,16,222, +176,14,50,176,15,50,177,23,13,237,177,24,13,237,177,25, +13,237,176,20,176,23,16,222,176,21,50,177,18,10,237,177, +19,10,237,1,176,8,47,176,9,51,176,10,51,176,11,51, +176,25,51,177,27,12,237,177,28,12,237,177,29,12,237,177, +30,12,237,177,38,29,16,204,48,49,63,1,22,23,22,51, +50,55,54,61,1,52,39,38,35,34,7,39,55,51,21,35, +7,54,51,50,23,22,29,1,20,7,6,35,34,39,38,21, +47,9,47,7,8,51,19,7,46,15,17,44,22,43,36,182, +146,20,33,36,66,32,16,57,30,38,76,30,9,96,4,58, +10,1,49,18,21,1,59,19,5,35,5,189,44,98,23,55, +28,35,1,75,36,19,60,19,0,2,0,12,255,249,0,255, +1,112,0,31,0,49,0,129,0,176,17,47,176,18,51,176, +19,51,177,35,7,237,177,36,7,237,177,37,7,237,176,44, +176,35,16,222,176,45,50,176,46,50,177,8,13,237,177,9, +13,237,177,10,13,237,176,2,176,8,16,222,176,3,50,176, +4,50,177,25,7,237,177,26,7,237,177,27,7,237,1,176, +21,47,176,22,51,176,23,51,176,24,51,177,6,10,237,177, +7,10,237,176,10,47,176,39,51,176,40,51,176,41,51,176, +42,51,177,12,8,237,177,13,8,237,177,14,8,237,177,15, +8,237,177,51,14,16,204,48,49,19,7,38,35,34,7,6, +21,54,51,50,23,22,29,1,20,7,6,35,34,39,38,39, +53,52,63,1,50,23,21,22,23,7,20,23,22,51,50,55, +54,61,1,52,39,38,35,34,7,6,21,248,45,13,49,48, +23,13,32,57,64,30,14,52,27,35,57,35,35,1,117,15, +73,24,5,2,184,36,18,21,46,17,6,41,13,17,47,19, +7,1,21,4,58,57,30,55,49,57,28,33,1,73,34,18, +41,41,94,2,184,11,1,63,1,12,14,159,52,25,11,49, +16,19,1,56,19,7,44,16,18,0,0,1,0,24,0,0, +1,6,1,106,0,14,0,44,0,177,9,15,63,176,10,51, +176,0,222,176,14,50,177,1,10,237,177,2,10,237,1,176, +0,47,176,1,51,177,2,14,237,177,3,14,237,177,16,2, +16,204,48,49,19,53,51,21,6,7,6,7,6,7,35,54, +55,54,55,24,238,71,42,6,4,14,3,46,5,86,18,19, +1,62,44,35,75,120,16,15,46,55,140,129,27,22,0,3, +0,20,255,249,1,7,1,112,0,29,0,47,0,65,0,203, +0,176,20,47,176,21,51,176,22,51,177,51,7,237,177,52, +7,237,177,53,7,237,176,14,176,51,16,222,176,27,50,176, +60,50,176,61,50,176,62,50,177,33,7,237,177,34,7,237, +177,35,7,237,176,3,176,33,16,222,176,42,50,176,43,50, +176,44,50,177,5,7,237,177,6,7,237,177,7,7,237,1, +176,24,47,176,25,51,176,26,51,176,27,51,177,48,8,237, +177,49,8,237,177,64,8,237,177,65,8,237,176,1,47,176, +2,51,176,3,51,176,23,51,177,30,8,237,177,31,8,237, +177,46,8,237,177,47,8,237,176,37,47,176,38,51,176,39, +51,176,40,51,177,9,8,237,177,10,8,237,177,11,8,237, +177,12,8,237,176,55,47,176,56,51,176,57,51,176,58,51, +177,14,8,237,177,15,8,237,177,16,8,237,177,17,8,237, +177,18,8,237,177,67,17,16,204,48,49,55,38,53,52,55, +54,51,50,23,22,29,1,20,7,22,23,20,49,20,7,6, +35,34,39,38,61,1,52,55,50,39,20,23,22,51,50,55, +54,61,1,52,39,38,35,34,7,6,21,7,20,23,22,51, +50,55,54,61,1,52,39,38,35,34,7,6,21,90,54,54, +23,28,63,29,14,54,67,2,58,28,34,72,33,16,65,2, +6,37,11,12,40,14,5,35,12,13,38,16,5,15,40,16, +19,48,19,7,41,16,19,47,19,7,199,20,55,59,25,10, +46,22,27,1,53,20,22,69,3,67,30,14,52,26,33,1, +72,20,77,42,13,4,34,10,11,1,39,15,5,32,11,12, +170,48,19,8,40,15,17,1,48,20,7,40,15,17,0,2, +0,21,255,249,1,7,1,112,0,33,0,52,0,133,0,176, +29,47,176,30,51,176,31,51,177,2,7,237,177,3,7,237, +177,4,7,237,176,11,176,2,16,222,176,12,50,176,13,50, +177,7,13,237,177,8,13,237,177,47,13,237,177,48,13,237, +177,49,13,237,176,39,176,8,16,222,176,40,50,176,41,50, +177,20,7,237,177,21,7,237,177,22,7,237,1,176,15,47, +176,16,51,176,17,51,176,18,51,177,13,8,237,177,43,8, +237,177,44,8,237,177,45,8,237,176,6,47,176,7,51,176, +8,51,176,9,51,177,25,10,237,177,26,10,237,177,27,10, +237,48,49,63,1,22,51,50,55,54,53,55,39,6,15,1, +34,39,38,61,1,52,55,54,51,50,31,1,22,21,20,7, +6,35,34,39,38,55,52,39,34,49,38,35,34,7,6,21, +20,23,22,51,50,55,54,53,28,43,11,50,40,22,23,1, +1,27,52,8,65,30,14,54,27,34,72,34,5,15,60,30, +41,76,22,3,182,37,1,14,18,46,20,8,39,15,19,52, +15,4,85,4,58,35,36,60,2,8,43,4,1,58,27,34, +1,73,34,17,64,10,36,67,132,43,22,67,11,174,54,22, +9,46,20,22,50,21,8,52,14,15,0,0,0,1,0,0, +0,0,0,0,233,217,10,104,95,15,60,245,0,19,3,232, +0,0,0,0,184,207,153,237,0,0,0,0,184,207,153,237, +0,0,255,52,2,246,2,234,0,0,0,8,0,2,0,0, +0,0,0,0,0,1,0,0,3,32,255,56,0,90,2,142, +0,0,255,152,2,246,0,1,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,25,1,176,0,33,0,0,0,0, +1,77,0,0,2,142,0,7,1,15,0,21,0,235,0,55, +1,31,0,15,1,23,0,21,1,23,0,6,1,30,0,21, +1,28,0,19,1,25,0,24,1,28,0,20,1,23,0,21, +1,27,0,0,1,15,0,21,0,235,0,55,1,31,0,15, +1,25,0,21,1,22,0,6,1,31,0,21,1,21,0,12, +1,25,0,24,1,28,0,20,1,31,0,21,0,0,0,1, +0,0,0,158,0,1,0,24,0,96,0,4,255,208,0,4, +0,4,0,0,0,4,0,14,255,122,0,5,0,5,0,0, +0,5,0,14,255,152,0,6,0,6,0,0,0,6,0,14, +255,150,0,7,0,14,255,122,0,8,0,14,255,122,0,9, +0,14,255,115,0,10,0,14,255,115,0,11,0,14,255,92, +0,12,0,14,255,115,0,13,0,14,255,115,0,14,0,14, +255,211,0,14,0,15,255,107,0,14,0,16,255,85,0,14, +0,17,255,107,0,14,0,18,255,115,0,14,0,19,255,92, +0,14,0,20,255,100,0,14,0,21,255,107,0,14,0,22, +255,130,0,14,0,23,255,100,0,14,0,24,255,107,0,0, +0,0,0,43,0,43,0,43,0,171,1,16,1,64,1,172, +2,58,2,140,2,246,3,128,3,180,4,125,5,19,5,42, +5,146,5,193,6,47,6,188,7,11,7,118,7,255,8,49, +8,241,9,127,0,1,0,0,0,25,0,70,0,3,0,0, +0,0,0,2,0,0,0,1,0,1,0,0,0,64,0,207, +0,0,0,0,0,0,0,26,1,62,0,1,0,0,0,0, +0,0,0,65,0,0,0,1,0,0,0,0,0,1,0,9, +0,66,0,1,0,0,0,0,0,2,0,7,0,76,0,1, +0,0,0,0,0,3,0,34,0,84,0,1,0,0,0,0, +0,4,0,9,0,119,0,1,0,0,0,0,0,5,0,7, +0,129,0,1,0,0,0,0,0,6,0,9,0,137,0,0, +0,3,0,0,0,0,0,130,0,147,0,0,0,3,0,0, +0,1,0,18,1,23,0,0,0,3,0,0,0,2,0,14, +1,43,0,0,0,3,0,0,0,3,0,68,1,59,0,0, +0,3,0,0,0,4,0,18,1,129,0,0,0,3,0,0, +0,5,0,14,1,149,0,0,0,3,0,0,0,6,0,18, +1,165,0,3,0,1,4,9,0,0,0,130,0,147,0,3, +0,1,4,9,0,1,0,18,1,23,0,3,0,1,4,9, +0,2,0,14,1,43,0,3,0,1,4,9,0,3,0,68, +1,59,0,3,0,1,4,9,0,4,0,18,1,129,0,3, +0,1,4,9,0,5,0,14,1,149,0,3,0,1,4,9, +0,6,0,18,1,165,0,3,0,1,0,0,0,4,0,34, +1,185,0,3,0,1,0,0,0,8,0,44,1,221,0,3, +0,1,0,0,0,11,0,46,2,11,0,3,0,1,0,0, +0,13,0,6,2,59,0,3,0,1,0,0,0,14,0,36, +2,67,67,114,101,97,116,101,100,32,98,121,32,65,108,108, +101,110,32,66,97,114,110,101,116,116,32,119,105,116,104,32, +80,102,97,69,100,105,116,32,49,46,48,32,40,104,116,116, +112,58,47,47,112,102,97,101,100,105,116,46,115,102,46,110, +101,116,41,0,108,67,83,121,109,98,111,108,115,0,82,101, +103,117,108,97,114,0,80,102,97,69,100,105,116,32,49,46, +48,32,58,32,108,67,83,121,109,98,111,108,115,32,58,32, +50,45,51,45,50,48,55,50,0,108,67,83,121,109,98,111, +108,115,0,48,48,49,46,48,48,48,0,108,67,83,121,109, +98,111,108,115,0,0,67,0,114,0,101,0,97,0,116,0, +101,0,100,0,32,0,98,0,121,0,32,0,65,0,108,0, +108,0,101,0,110,0,32,0,66,0,97,0,114,0,110,0, +101,0,116,0,116,0,32,0,119,0,105,0,116,0,104,0, +32,0,80,0,102,0,97,0,69,0,100,0,105,0,116,0, +32,0,49,0,46,0,48,0,32,0,40,0,104,0,116,0, +116,0,112,0,58,0,47,0,47,0,112,0,102,0,97,0, +101,0,100,0,105,0,116,0,46,0,115,0,102,0,46,0, +110,0,101,0,116,0,41,0,0,0,108,0,67,0,83,0, +121,0,109,0,98,0,111,0,108,0,115,0,0,0,82,0, +101,0,103,0,117,0,108,0,97,0,114,0,0,0,80,0, +102,0,97,0,69,0,100,0,105,0,116,0,32,0,49,0, +46,0,48,0,32,0,58,0,32,0,108,0,67,0,83,0, +121,0,109,0,98,0,111,0,108,0,115,0,32,0,58,0, +32,0,50,0,45,0,51,0,45,0,50,0,48,0,55,0, +50,0,0,0,108,0,67,0,83,0,121,0,109,0,98,0, +111,0,108,0,115,0,0,0,48,0,48,0,49,0,46,0, +48,0,48,0,48,0,0,0,108,0,67,0,83,0,121,0, +109,0,98,0,111,0,108,0,115,0,0,0,108,0,105,0, +103,0,110,0,117,0,109,0,67,0,65,0,68,0,32,0, +83,0,121,0,109,0,98,0,111,0,108,0,115,0,0,0, +108,0,105,0,103,0,110,0,117,0,109,0,32,0,67,0, +111,0,109,0,112,0,117,0,116,0,105,0,110,0,103,0, +44,0,32,0,73,0,110,0,99,0,46,0,0,0,119,0, +119,0,119,0,46,0,108,0,105,0,103,0,110,0,117,0, +109,0,99,0,111,0,109,0,112,0,117,0,116,0,105,0, +110,0,103,0,46,0,99,0,111,0,109,0,0,0,71,0, +80,0,76,0,0,0,104,0,116,0,116,0,112,0,58,0, +47,0,47,0,119,0,119,0,119,0,46,0,103,0,110,0, +117,0,46,0,111,0,114,0,103,0,0,0,0,2,0,0, +0,0,0,0,255,156,0,50,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,25,0,0, +0,1,0,2,1,2,1,3,1,4,1,5,1,6,1,7, +1,8,1,9,1,10,1,11,1,12,1,13,1,14,1,15, +1,16,1,17,1,18,1,19,1,20,1,21,1,22,1,23, +7,117,110,105,50,49,48,52,7,117,110,105,69,48,48,48, +7,117,110,105,69,48,48,49,7,117,110,105,69,48,48,50, +7,117,110,105,69,48,48,51,7,117,110,105,69,48,48,52, +7,117,110,105,69,48,48,53,7,117,110,105,69,48,48,54, +7,117,110,105,69,48,48,55,7,117,110,105,69,48,48,56, +7,117,110,105,69,48,48,57,7,117,110,105,69,48,48,65, +7,117,110,105,69,48,49,48,7,117,110,105,69,48,49,49, +7,117,110,105,69,48,49,50,7,117,110,105,69,48,49,51, +7,117,110,105,69,48,49,52,7,117,110,105,69,48,49,53, +7,117,110,105,69,48,49,54,7,117,110,105,69,48,49,55, +7,117,110,105,69,48,49,56,7,117,110,105,69,48,49,57, +}; +const int lCSymbols_ttf_size = sizeof( lCSymbols_ttf ); diff --git a/engine/libraries/oglft/tests/background.png b/engine/libraries/oglft/tests/background.png new file mode 100644 index 0000000..3686224 Binary files /dev/null and b/engine/libraries/oglft/tests/background.png differ diff --git a/engine/libraries/oglft/tests/background00.png b/engine/libraries/oglft/tests/background00.png new file mode 100644 index 0000000..f262168 Binary files /dev/null and b/engine/libraries/oglft/tests/background00.png differ diff --git a/engine/libraries/oglft/tests/background00a.png b/engine/libraries/oglft/tests/background00a.png new file mode 100644 index 0000000..e077d2a Binary files /dev/null and b/engine/libraries/oglft/tests/background00a.png differ diff --git a/engine/libraries/oglft/tests/background00b.png b/engine/libraries/oglft/tests/background00b.png new file mode 100644 index 0000000..365ef86 Binary files /dev/null and b/engine/libraries/oglft/tests/background00b.png differ diff --git a/engine/libraries/oglft/tests/background01.png b/engine/libraries/oglft/tests/background01.png new file mode 100644 index 0000000..6c47289 Binary files /dev/null and b/engine/libraries/oglft/tests/background01.png differ diff --git a/engine/libraries/oglft/tests/background10.png b/engine/libraries/oglft/tests/background10.png new file mode 100644 index 0000000..1f6c86a Binary files /dev/null and b/engine/libraries/oglft/tests/background10.png differ diff --git a/engine/libraries/oglft/tests/background10a.png b/engine/libraries/oglft/tests/background10a.png new file mode 100644 index 0000000..3d52456 Binary files /dev/null and b/engine/libraries/oglft/tests/background10a.png differ diff --git a/engine/libraries/oglft/tests/background10b.png b/engine/libraries/oglft/tests/background10b.png new file mode 100644 index 0000000..b673cc7 Binary files /dev/null and b/engine/libraries/oglft/tests/background10b.png differ diff --git a/engine/libraries/oglft/tests/background11.png b/engine/libraries/oglft/tests/background11.png new file mode 100644 index 0000000..bdcb806 Binary files /dev/null and b/engine/libraries/oglft/tests/background11.png differ diff --git a/engine/libraries/oglft/tests/demo.cpp b/engine/libraries/oglft/tests/demo.cpp new file mode 100644 index 0000000..ed71f59 --- /dev/null +++ b/engine/libraries/oglft/tests/demo.cpp @@ -0,0 +1,378 @@ +/* + * demo.cpp: Demo of the OGLFT library + * Copyright (C) 2002 lignum Computing, Inc. + * $Id$ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include + +#include + +static const char* USAGE = " fontfile"; + +static const char* commands = "a/A: char rotate X, s/S: char rotate Y, d/D: char rotate Z, f/F: string rotate, r: reset all"; +static const char* text = "The quick brown fox jumps over a lazy dog."; +static const float point_size = 16; + +static OGLFT::Monochrome* commands_face; +static OGLFT::Monochrome* monochrome_face; +static OGLFT::Grayscale* grayscale_face; +static OGLFT::Translucent* translucent_face; +static OGLFT::Outline* outline_face; +static OGLFT::Filled* filled_face; +#ifndef OGLFT_NO_SOLID +static OGLFT::Solid* solid_face; +#else +static OGLFT::Monochrome* solid_face; +#endif +static OGLFT::MonochromeTexture* monochrome_texture_face; +static OGLFT::GrayscaleTexture* grayscale_texture_face; +static OGLFT::TranslucentTexture* translucent_texture_face; + + +static float dy; +static int viewport_width; +static int viewport_height; + +static void init ( int /*argc*/, char* argv[] ) +{ + std::cout << glGetString( GL_VENDOR ) << " " << glGetString( GL_RENDERER ) << " " + << glGetString( GL_VERSION ) << std::endl; + + commands_face = new OGLFT::Monochrome( argv[1], point_size / 2. ); + commands_face->setHorizontalJustification( OGLFT::Face::CENTER ); + + monochrome_face = new OGLFT::Monochrome( argv[1], point_size ); + monochrome_face->setHorizontalJustification( OGLFT::Face::CENTER ); + monochrome_face->setForegroundColor( 1., 0., 0., 1. ); + + if ( !monochrome_face->isValid() ) { + std::cerr << "failed to open face. exiting." << std::endl; + exit( 1 ); + } + + grayscale_face = new OGLFT::Grayscale( argv[1], point_size ); + grayscale_face->setHorizontalJustification( OGLFT::Face::CENTER ); + grayscale_face->setForegroundColor( 0., 0., .5, 1. ); + grayscale_face->setBackgroundColor( 0., 1., 1., 1. ); + + translucent_face = new OGLFT::Translucent( argv[1], point_size ); + translucent_face->setHorizontalJustification( OGLFT::Face::CENTER ); + translucent_face->setForegroundColor( 0., .5, 0., 1. ); + + outline_face = new OGLFT::Outline( argv[1], point_size ); + outline_face->setHorizontalJustification( OGLFT::Face::CENTER ); + outline_face->setForegroundColor( 1., 1., 0., 1. ); + + filled_face = new OGLFT::Filled( argv[1], point_size ); + filled_face->setHorizontalJustification( OGLFT::Face::CENTER ); + filled_face->setForegroundColor( .5, 0., 1., 1. ); + +#ifndef OGLFT_NO_SOLID + solid_face = new OGLFT::Solid( argv[1], point_size ); + solid_face->setDepth( 10. ); + solid_face->setCharacterRotationX( 25. ); + solid_face->setCharacterRotationY( 25. ); + solid_face->setTessellationSteps( 3 ); +#else + solid_face = new OGLFT::Monochrome( argv[1], point_size ); +#endif + solid_face->setHorizontalJustification( OGLFT::Face::CENTER ); + solid_face->setForegroundColor( 1., .5, 0., 1. ); + + monochrome_texture_face = new OGLFT::MonochromeTexture( argv[1], point_size ); + monochrome_texture_face->setHorizontalJustification( OGLFT::Face::CENTER ); + monochrome_texture_face->setForegroundColor( 0., .5, .75, 1. ); + + grayscale_texture_face = new OGLFT::GrayscaleTexture( argv[1], point_size ); + grayscale_texture_face->setHorizontalJustification( OGLFT::Face::CENTER ); + grayscale_texture_face->setForegroundColor( 0.9, .65, .9, 1. ); + grayscale_texture_face->setBackgroundColor( 0.5, .5, .75, 0.3 ); + + translucent_texture_face = new OGLFT::TranslucentTexture( argv[1], point_size ); + translucent_texture_face->setHorizontalJustification( OGLFT::Face::CENTER ); + translucent_texture_face->setForegroundColor( 0.75, 1., .75, 1. ); + + // Set various general parameters which don't affect performance (yet). + glClearColor( .75, .75, .75, 1. ); + glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); +#if defined(GL_RASTER_POSITION_UNCLIPPED_IBM) + glEnable( GL_RASTER_POSITION_UNCLIPPED_IBM ); +#endif + glEnable( GL_LIGHT0 ); + glDisable( GL_DITHER ); + glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); +} + +static void reshape ( int width, int height ) +{ + viewport_width = width; + viewport_height = height; + + glViewport( 0, 0, viewport_width, viewport_height ); + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glOrtho( 0, viewport_width, 0, viewport_height, -100, 100 ); + + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + + dy = viewport_height / ( 9 + 1 ); +} + +static void reset ( void ) +{ + monochrome_face->setCharacterRotationZ( 0 ); + monochrome_face->setStringRotation( 0 ); + + grayscale_face->setCharacterRotationZ( 0 ); + grayscale_face->setStringRotation( 0 ); + + translucent_face->setCharacterRotationZ( 0 ); + translucent_face->setStringRotation( 0 ); + + outline_face->setCharacterRotationX( 0 ); + outline_face->setCharacterRotationY( 0 ); + outline_face->setCharacterRotationZ( 0 ); + outline_face->setStringRotation( 0 ); + + filled_face->setCharacterRotationX( 0 ); + filled_face->setCharacterRotationY( 0 ); + filled_face->setCharacterRotationZ( 0 ); + filled_face->setStringRotation( 0 ); + +#ifndef OGLFT_NO_SOLID + solid_face->setCharacterRotationX( 25. ); + solid_face->setCharacterRotationY( 25. ); +#endif + solid_face->setCharacterRotationZ( 0 ); + solid_face->setStringRotation( 0 ); + + monochrome_texture_face->setCharacterRotationX( 0 ); + monochrome_texture_face->setCharacterRotationY( 0 ); + monochrome_texture_face->setCharacterRotationZ( 0 ); + monochrome_texture_face->setStringRotation( 0 ); + + grayscale_texture_face->setCharacterRotationX( 0 ); + grayscale_texture_face->setCharacterRotationY( 0 ); + grayscale_texture_face->setCharacterRotationZ( 0 ); + grayscale_texture_face->setStringRotation( 0 ); + + translucent_texture_face->setCharacterRotationX( 0 ); + translucent_texture_face->setCharacterRotationY( 0 ); + translucent_texture_face->setCharacterRotationZ( 0 ); + translucent_texture_face->setStringRotation( 0 ); + + glViewport( 0, 0, viewport_width, viewport_height ); + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glOrtho( 0, viewport_width, 0, viewport_height, -100, 100 ); + + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); +} + +static void char_rotate_x ( float dx ) +{ + outline_face->setCharacterRotationX( outline_face->characterRotationX()+dx ); + filled_face->setCharacterRotationX( filled_face->characterRotationX()+dx ); +#ifndef OGLFT_NO_SOLID + solid_face->setCharacterRotationX( solid_face->characterRotationX()+dx ); +#endif + monochrome_texture_face->setCharacterRotationX( + monochrome_texture_face->characterRotationX()+dx ); + grayscale_texture_face->setCharacterRotationX( + grayscale_texture_face->characterRotationX()+dx ); + translucent_texture_face->setCharacterRotationX( + translucent_texture_face->characterRotationX()+dx ); +} + +static void char_rotate_y ( float dy ) +{ + outline_face->setCharacterRotationY( outline_face->characterRotationY()+dy ); + filled_face->setCharacterRotationY( filled_face->characterRotationY()+dy ); +#ifndef OGLFT_NO_SOLID + solid_face->setCharacterRotationY( solid_face->characterRotationY()+dy ); +#endif + monochrome_texture_face->setCharacterRotationY( + monochrome_texture_face->characterRotationY()+dy ); + grayscale_texture_face->setCharacterRotationY( + grayscale_texture_face->characterRotationY()+dy ); + translucent_texture_face->setCharacterRotationY( + translucent_texture_face->characterRotationY()+dy ); +} + +static void char_rotate_z ( float dz ) +{ + monochrome_face->setCharacterRotationZ( monochrome_face->characterRotationZ()+dz ); + grayscale_face->setCharacterRotationZ( grayscale_face->characterRotationZ()+dz ); + translucent_face->setCharacterRotationZ( translucent_face->characterRotationZ()+dz ); + + outline_face->setCharacterRotationZ( outline_face->characterRotationZ()+dz ); + filled_face->setCharacterRotationZ( filled_face->characterRotationZ()+dz ); + solid_face->setCharacterRotationZ( solid_face->characterRotationZ()+dz ); + + monochrome_texture_face->setCharacterRotationZ( monochrome_texture_face->characterRotationZ()+dz ); + grayscale_texture_face->setCharacterRotationZ( grayscale_texture_face->characterRotationZ()+dz ); + translucent_texture_face->setCharacterRotationZ( translucent_texture_face->characterRotationZ()+dz ); +} + +static void string_rotate ( float dz ) +{ + + monochrome_face->setStringRotation( monochrome_face->stringRotation()+dz ); + grayscale_face->setStringRotation( grayscale_face->stringRotation()+dz ); + translucent_face->setStringRotation( translucent_face->stringRotation()+dz ); + + outline_face->setStringRotation( outline_face->stringRotation()+dz ); + filled_face->setStringRotation( filled_face->stringRotation()+dz ); + solid_face->setStringRotation( solid_face->stringRotation()+dz ); + + monochrome_texture_face->setStringRotation( monochrome_texture_face->stringRotation()+dz ); + grayscale_texture_face->setStringRotation( grayscale_texture_face->stringRotation()+dz ); + translucent_texture_face->setStringRotation( translucent_texture_face->stringRotation()+dz ); +} + +static void display ( void ) +{ + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + glPushMatrix(); + + // Draw everything centered + glTranslatef( viewport_width/2., 0., 0. ); + + commands_face->draw( 0., 0., commands ); + + glTranslatef( 0., dy, 0. ); + monochrome_face->draw( 0., 0., text ); + + glTranslatef( 0., dy, 0. ); + grayscale_face->draw( 0., 0., text ); + + glEnable( GL_BLEND ); + glTranslatef( 0., dy, 0. ); + translucent_face->draw( 0., 0., text ); + glDisable( GL_BLEND ); + + glTranslatef( 0., dy, 0. ); + outline_face->draw( 0., 0., text ); + + glTranslatef( 0., dy, 0. ); + filled_face->draw( 0., 0., text ); + + glTranslatef( 0., dy, 0. ); +#ifndef OGLFT_NO_SOLID + glEnable( GL_LIGHTING ); + glEnable( GL_DEPTH_TEST ); + glEnable( GL_COLOR_MATERIAL ); + solid_face->draw( 0., 0., text ); + glDisable( GL_COLOR_MATERIAL ); + glDisable( GL_DEPTH_TEST ); + glDisable( GL_LIGHTING ); +#else + solid_face->draw( 0., 0., "" ); +#endif + + glEnable( GL_TEXTURE_2D ); + glEnable( GL_BLEND ); + glEnable( GL_DEPTH_TEST ); + glTranslatef( 0., dy, 0. ); + monochrome_texture_face->draw( 0., 0., text ); + + glTranslatef( 0., dy, 0. ); + grayscale_texture_face->draw( 0., 0., text ); + + glTranslatef( 0., dy, 0. ); + translucent_texture_face->draw( 0., 0., text ); + glDisable( GL_DEPTH_TEST ); + glDisable( GL_BLEND ); + glDisable( GL_TEXTURE_2D ); + + glPopMatrix(); + glutSwapBuffers(); +} + +static void key ( unsigned char key, int /*x*/, int /*y*/ ) +{ + switch ( key ) { + case 'a': + char_rotate_x( -4 ); break; + case 'A': + char_rotate_x( 4 ); break; + case 's': + char_rotate_y( -4 ); break; + case 'S': + char_rotate_y( 4 ); break; + case 'd': + char_rotate_z( -4 ); break; + case 'D': + char_rotate_z( 4 ); break; + case 'f': + string_rotate( -4 ); break; + case 'F': + string_rotate( 4 ); break; + case 'r': case 'R': + reset(); break; + case 27: + exit( 0 ); + default: + return; + } + + glutPostRedisplay(); +} + +static void done ( void ) +{ + delete monochrome_face; + delete grayscale_face; + delete translucent_face; + delete outline_face; + delete filled_face; + delete solid_face; + delete monochrome_texture_face; + delete grayscale_texture_face; + delete translucent_texture_face; +} + +int main ( int argc, char* argv[] ) +{ + if ( argc != 2 ) { + std::cerr << argv[0] << USAGE << std::endl; + return 1; + } + + glutInit( &argc, argv ); + glutInitWindowSize( 500, 500 ); + glutInitDisplayMode( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH ); + glutCreateWindow( argv[0] ); + + init( argc, argv ); + + atexit( done ); + + glutReshapeFunc( reshape ); + glutDisplayFunc( display ); + glutKeyboardFunc( key ); + + glutMainLoop(); + + return 0; +} diff --git a/engine/libraries/oglft/tests/demo2.cpp b/engine/libraries/oglft/tests/demo2.cpp new file mode 100644 index 0000000..6b48259 --- /dev/null +++ b/engine/libraries/oglft/tests/demo2.cpp @@ -0,0 +1,183 @@ +/* + * demo2.cpp: Second Demo of the OGLFT library + * Copyright (C) 2002 lignum Computing, Inc. + * $Id$ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include + +#include + +#include + +#include FT_MULTIPLE_MASTERS_H + +static const char* USAGE = " fontfile"; + +static FT_Library library; +static FT_Face ft_face; +static FT_Multi_Master master_info; +static FT_Long axis_averages[T1_MAX_MM_AXIS]; +#if 0 +static OGLFT::Filled* oglft_face; +#else +static OGLFT::Monochrome* oglft_face; +#endif +static int viewport_width; +static int viewport_height; + +static void init ( int /*argc*/, char* argv[] ) +{ + std::cout << glGetString( GL_VENDOR ) << " " << glGetString( GL_RENDERER ) << " " + << glGetString( GL_VERSION ) << std::endl; + + library = OGLFT::Library::instance(); + + FT_Error error; + + error = FT_New_Face( library, argv[1], 0, &ft_face ); + + if ( error != 0 ) { + std::cerr << "Could not create a font from file \"" << argv[1] << "\"" + << std::endl; + exit( 1 ); + } + + error = FT_Get_Multi_Master( ft_face, &master_info ); + + if ( error != 0 ) { + std::cerr << "Font file \"" << argv[1] + << "\" does not contain a multi master font" << std::endl; + exit( 1 ); + } + + for ( unsigned int i = 0; i < master_info.num_axis; i++ ) + axis_averages[i] = ( master_info.axis[i].minimum + + master_info.axis[i].maximum ) / 2; + + FT_Set_MM_Design_Coordinates( ft_face, master_info.num_axis, axis_averages ); + +#if 0 + oglft_face = new OGLFT::Filled( ft_face, 14 ); +#else + glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + glEnable( GL_RASTER_POSITION_UNCLIPPED_IBM ); + + oglft_face = new OGLFT::Monochrome( ft_face, 14 ); +#endif + oglft_face->setHorizontalJustification( OGLFT::Face::LEFT ); + oglft_face->setForegroundColor( 1., 0., 0. ); + oglft_face->setCompileMode( OGLFT::Face::IMMEDIATE ); +} + +static void reshape ( int width, int height ) +{ + viewport_width = width; + viewport_height = height; + + glViewport( 0, 0, viewport_width, viewport_height ); + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glOrtho( 0, viewport_width, 0, viewport_height, -100, 100 ); + + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); +} + +static void display ( void ) +{ + const int BUFSIZE = 128; + char buffer[BUFSIZE]; + + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + glPushMatrix(); + + snprintf( buffer, sizeof(buffer), "There are %d axes", master_info.num_axis ); + + GLfloat y = 480.; + + oglft_face->draw( 0., y, buffer ); + + for ( unsigned int i = 0; i < master_info.num_axis; i++ ) { + FT_Set_MM_Design_Coordinates( ft_face, master_info.num_axis, axis_averages ); + snprintf( buffer, sizeof(buffer), "%s: min: %ld max: %ld", + master_info.axis[i].name, + master_info.axis[i].minimum, master_info.axis[i].maximum ); + + y -= 20.; + oglft_face->draw( 0., y, buffer ); + + FT_Long axis_average = axis_averages[i]; + FT_Long d_axis = (master_info.axis[i].maximum - master_info.axis[i].minimum)/4; + axis_averages[i] = master_info.axis[i].minimum; + + for ( int j = 0; j <= 4; j++ ) { + FT_Set_MM_Design_Coordinates( ft_face, master_info.num_axis, axis_averages ); + snprintf( buffer, sizeof(buffer), " Style at axis = %ld\n", axis_averages[i] ); + y -= 20.; + oglft_face->draw( 0., y, buffer ); + + axis_averages[i] += d_axis; + } + + axis_averages[i] = axis_average; + } + + glPopMatrix(); + glutSwapBuffers(); +} + +static void key ( unsigned char key, int /*x*/, int /*y*/ ) +{ + switch ( key ) { + case 'q': + case 27: + exit( 0 ); + default: + return; + } + + glutPostRedisplay(); +} + +static void done ( void ) +{ +} + +int main ( int argc, char* argv[] ) +{ + if ( argc != 2 ) { + std::cerr << argv[0] << USAGE << std::endl; + return 1; + } + + glutInit( &argc, argv ); + glutInitWindowSize( 500, 500 ); + glutInitDisplayMode( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH ); + glutCreateWindow( argv[0] ); + + init( argc, argv ); + + atexit( done ); + + glutReshapeFunc( reshape ); + glutDisplayFunc( display ); + glutKeyboardFunc( key ); + + glutMainLoop(); + + return 0; +} diff --git a/engine/libraries/oglft/tests/demo3.cpp b/engine/libraries/oglft/tests/demo3.cpp new file mode 100644 index 0000000..a7bdb45 --- /dev/null +++ b/engine/libraries/oglft/tests/demo3.cpp @@ -0,0 +1,1582 @@ +/* + * demo3.cpp: Third Demo of the OGLFT library + * Copyright (C) 2002 lignum Computing, Inc. + * $Id$ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include + +#include +#include + +#include "OGLFT.h" +#include "vignette.h" +#include "vignette.moc" + +#include "Demo3UnicodeExample.h" +#include "Demo3UnicodeExample2.h" + +class Vignette0 : public Vignette { + + static const unsigned int FRAME_COUNT = 32; + static const unsigned int FRAME_RATE = 16; + + const char* text_; + OGLFT::Filled* face_; + +public: + Vignette0 ( const char* text, const char* fontfile ) : text_( text ) + { + face_ = new OGLFT::Filled( fontfile, 288, 75 ); + face_->setForegroundColor( 0., .5, .75 ); + face_->setHorizontalJustification( OGLFT::Face::CENTER ); + } + + ~Vignette0 ( void ) + { + // std::cout << "destructing Vignette 0" << std::endl; + delete face_; + } + + unsigned int frame_count ( void ) { return FRAME_COUNT; } + + unsigned int frame_rate ( void ) { return FRAME_RATE; } + + void init ( void ) + { + std::cout << "Vignette 0" << std::endl; + } + + void draw ( unsigned int frame_number ) + { + glPushMatrix(); + glTranslatef( 0., 0., + - 1. - ( FRAME_COUNT - frame_number ) ); + face_->draw( 0., 0., text_ ); + glPopMatrix(); + } + + void finish ( void ) + { + } +}; + +class Vignette1 : public Vignette { + + static const unsigned int FRAME_COUNT = 49; + static const unsigned int FRAME_RATE = 16; + + const char* text_; + OGLFT::Filled* face_; + +public: + Vignette1 ( const char* text, const char* fontfile ) : text_( text ) + { + face_ = new OGLFT::Filled( fontfile, 288, 75 ); + face_->setForegroundColor( 0., .5, .75 ); + face_->setHorizontalJustification( OGLFT::Face::CENTER ); + } + + ~Vignette1 ( void ) + { + // std::cout << "destructing Vignette 1" << std::endl; + delete face_; + } + + unsigned int frame_count ( void ) { return FRAME_COUNT; } + + unsigned int frame_rate ( void ) { return FRAME_RATE; } + + void init ( void ) + { + std::cout << "Vignette 1" << std::endl; + } + + void draw ( unsigned int frame_number ) + { + glPushMatrix(); + glTranslatef( 0., 0., + - 1. - ( FRAME_COUNT - frame_number ) ); + glRotatef( frame_number * 15., 0., 0., 1. ); + face_->draw( 0., 0., text_ ); + glPopMatrix(); + } + + void finish ( void ) + { + } +}; + +class Vignette2 : public Vignette { + + static const unsigned int FRAME_COUNT = 60; + static const unsigned int FRAME_RATE = 12; + + const char* text_; + OGLFT::Filled* face_; + + GLfloat x_; + +public: + Vignette2 ( const char* text, const char* fontfile ) : text_( text ) + { + face_ = new OGLFT::Filled( fontfile, 32, 75 ); + face_->setCompileMode( OGLFT::Face::IMMEDIATE ); + face_->setForegroundColor( 0., .5, .75 ); + face_->setHorizontalJustification( OGLFT::Face::RIGHT ); + face_->setCharacterRotationZ( 30. ); + + x_ = -250.; + } + + ~Vignette2 ( void ) + { + // std::cout << "destructing Vignette 2" << std::endl; + delete face_; + } + + unsigned int frame_count ( void ) { return FRAME_COUNT; } + + unsigned int frame_rate ( void ) { return FRAME_RATE; } + + void init ( void ) + { + std::cout << "Vignette 2" << std::endl; + } + + void draw ( unsigned int frame_number ) + { + glPushMatrix(); + + if ( frame_number <= 48 ) { + x_ += 10.; + face_->setCharacterRotationZ( face_->characterRotationZ() - 30. ); + } + else + x_ = 250.; + + glTranslatef( x_, 0., 0. ); + + face_->draw( 0., 0., text_ ); + + glPopMatrix(); + } + + void view ( GLdouble left, GLdouble right, GLdouble bottom, GLdouble top ) + { + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glOrtho( left, right, bottom, top, -250., 250. ); + glMatrixMode( GL_MODELVIEW ); + } + + void finish ( void ) + { + } +}; + +class Vignette3 : public Vignette { + + static const unsigned int FRAME_COUNT = 36; + static const unsigned int FRAME_RATE = 16; + + const char* text_; + OGLFT::Filled* face_; + +public: + Vignette3 ( const char* text, const char* fontfile ) : text_( text ) + { + face_ = new OGLFT::Filled( fontfile, 32, 75 ); + face_->setCompileMode( OGLFT::Face::IMMEDIATE ); + face_->setForegroundColor( 0., .5, .75 ); + face_->setHorizontalJustification( OGLFT::Face::CENTER ); + face_->setCharacterRotationY( 90. ); + } + + ~Vignette3 ( void ) + { + // std::cout << "destructing Vignette 3" << std::endl; + delete face_; + } + + unsigned int frame_count ( void ) { return FRAME_COUNT; } + + unsigned int frame_rate ( void ) { return FRAME_RATE; } + + void init ( void ) + { + std::cout << "Vignette 3" << std::endl; + } + + void view ( GLdouble left, GLdouble right, GLdouble bottom, GLdouble top ) + { + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glOrtho( left, right, bottom, top, -250., 250. ); + glMatrixMode( GL_MODELVIEW ); + } + + void draw ( unsigned int frame_number ) + { + glPushMatrix(); + glTranslatef( 0., 0., + - 1. - ( FRAME_COUNT - frame_number ) ); + face_->setCharacterRotationY( face_->characterRotationY() + 10. ); + face_->draw( 0., 0., text_ ); + glPopMatrix(); + } + + void finish ( void ) + { + } +}; + +class Vignette4 : public Vignette { + + static const unsigned int FRAME_COUNT = 82; + static const unsigned int FRAME_RATE = 10; + + const char* text_; +#ifndef OGLFT_NO_SOLID + OGLFT::Solid* face_; +#else + OGLFT::Filled* face_; +#endif + OGLFT::Translucent* annotation_; + + QImage* bg00b; + QImage* bg01; + QImage* bg10b; + QImage* bg11; + + GLuint bg_textures_[6]; + +public: + Vignette4 ( const char* text, const char* fontfile ) : text_( text ) + { +#ifndef OGLFT_NO_SOLID + face_ = new OGLFT::Solid( fontfile, 120, 75 ); + face_->setDepth( 24. ); +#else + face_ = new OGLFT::Filled( fontfile, 120, 75 ); +#endif + face_->setCompileMode( OGLFT::Face::IMMEDIATE ); + face_->setForegroundColor( 1., 1., 0. ); + face_->setHorizontalJustification( OGLFT::Face::CENTER ); + face_->setVerticalJustification( OGLFT::Face::MIDDLE ); + face_->setCharacterRotationY( 0. ); + face_->setCharacterRotationZ( 0. ); + + annotation_ = new OGLFT::Translucent( fontfile, 10, 75 ); + annotation_->setForegroundColor( 1., 1., 0., 0.5 ); + annotation_->setHorizontalJustification( OGLFT::Face::RIGHT ); + annotation_->setVerticalJustification( OGLFT::Face::BOTTOM ); + + bg00b = new QImage( "background00b.png" ); + bg01 = new QImage( "background01.png" ); + bg10b = new QImage( "background10b.png" ); + bg11 = new QImage( "background11.png" ); + } + + ~Vignette4 ( void ) + { + // std::cout << "destructing Vignette 4" << std::endl; + glDeleteTextures( 6, bg_textures_ ); + delete bg00b; + delete bg01; + delete bg10b; + delete bg11; + delete face_; + } + + unsigned int frame_count ( void ) { return FRAME_COUNT; } + + unsigned int frame_rate ( void ) { return FRAME_RATE; } + + void init ( void ) + { + std::cout << "Vignette 4" << std::endl; + + glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + + glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); + + glGenTextures( 6, bg_textures_ ); + + glBindTexture( GL_TEXTURE_2D, bg_textures_[1] ); + + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, bg01->width(), bg01->height(), + 0, GL_BGRA, GL_UNSIGNED_BYTE, bg01->bits() ); + + glBindTexture( GL_TEXTURE_2D, bg_textures_[3] ); + + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, bg11->width(), bg11->height(), + 0, GL_BGRA, GL_UNSIGNED_BYTE, bg11->bits() ); + + glBindTexture( GL_TEXTURE_2D, bg_textures_[4] ); + + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, bg00b->width(), bg00b->height(), + 0, GL_BGRA, GL_UNSIGNED_BYTE, bg00b->bits() ); + + glBindTexture( GL_TEXTURE_2D, bg_textures_[5] ); + + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, bg10b->width(), bg10b->height(), + 0, GL_BGRA, GL_UNSIGNED_BYTE, bg10b->bits() ); + + glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + + glEnable( GL_LIGHT0 ); + } + + void view ( GLdouble left, GLdouble right, GLdouble bottom, GLdouble top ) + { + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glOrtho( left, right, bottom, top, -250., 250. ); + glMatrixMode( GL_MODELVIEW ); + + GLint viewport[4]; + GLdouble matrix[16]; + + glGetIntegerv( GL_VIEWPORT, viewport ); + glGetDoublev( GL_PROJECTION_MATRIX, matrix ); + glGetDoublev( GL_MODELVIEW_MATRIX, matrix ); + } + + void draw ( unsigned int frame_number ) + { + glPushMatrix(); + + GLubyte alpha = 255; + + if ( frame_number < FRAME_COUNT ) + alpha = (GLubyte)( 255. * (GLfloat)frame_number/(FRAME_COUNT) ); + + glEnable( GL_BLEND ); + + glBegin( GL_QUADS ); + glColor4ub( 128, 164, 212, alpha ); + glVertex2f( -250., 0. ); + glVertex2f( 250., 0. ); + glColor4ub( 101, 142, 198, alpha ); + glVertex2f( 250., 250. ); + glVertex2f( -250., 250. ); + glEnd(); + + glDisable( GL_BLEND ); + + glEnable( GL_LIGHTING ); + glEnable( GL_DEPTH_TEST ); + glEnable( GL_COLOR_MATERIAL ); + + GLfloat y; + + if ( frame_number <= 72 ) { + face_->setCharacterRotationX( face_->characterRotationX() + 25. ); + face_->setCharacterRotationY( face_->characterRotationY() + 25. ); +#if 0 + face_->setCharacterRotationZ( face_->characterRotationZ() - 12.5 ); +#endif + y = 3.5*frame_number - 55.; + } + else + y = 3.5*72 - 55.; + + face_->draw( 0., y, text_ ); + + if ( frame_number < FRAME_COUNT ) { + // Fade in the background + GLdouble f = (GLfloat)frame_number/(FRAME_COUNT); + + glColor4f( f, f, f, 1. ); + } + else + glColor4f( 1., 1., 1., 1. ); + + glDisable( GL_LIGHTING ); + glDisable( GL_DEPTH_TEST ); + glDisable( GL_COLOR_MATERIAL ); + + glEnable( GL_TEXTURE_2D ); + glEnable( GL_BLEND ); + + glBindTexture( GL_TEXTURE_2D, bg_textures_[1] ); + + glBegin( GL_QUADS ); + glTexCoord2f( 0., 1. ); glVertex2f( -250., -250. ); + glTexCoord2f( 1., 1. ); glVertex2f( 0., -250. ); + glTexCoord2f( 1., 0. ); glVertex2f( 0., 0. ); + glTexCoord2f( 0., 0. ); glVertex2f( -250., 0. ); + glEnd(); + + glBindTexture( GL_TEXTURE_2D, bg_textures_[3] ); + + glBegin( GL_QUADS ); + glTexCoord2f( 0., 1. ); glVertex2f( 0., -250. ); + glTexCoord2f( 1., 1. ); glVertex2f( 250., -250. ); + glTexCoord2f( 1., 0. ); glVertex2f( 250., 0. ); + glTexCoord2f( 0., 0. ); glVertex2f( 0., 0. ); + glEnd(); + + glBindTexture( GL_TEXTURE_2D, bg_textures_[4] ); + + glBegin( GL_QUADS ); + glTexCoord2f( 0., 1. ); glVertex2f( -250., 0. ); + glTexCoord2f( 1., 1. ); glVertex2f( 0., 0. ); + glTexCoord2f( 1., 0. ); glVertex2f( 0., 250. ); + glTexCoord2f( 0., 0. ); glVertex2f( -250., 250. ); + glEnd(); + + glBindTexture( GL_TEXTURE_2D, bg_textures_[5] ); + + glBegin( GL_QUADS ); + glTexCoord2f( 0., 1. ); glVertex2f( 0., 0. ); + glTexCoord2f( 1., 1. ); glVertex2f( 250., 0. ); + glTexCoord2f( 1., 0. ); glVertex2f( 250., 250. ); + glTexCoord2f( 0., 0. ); glVertex2f( 0., 250. ); + glEnd(); + + glDisable( GL_TEXTURE_2D ); + glDisable( GL_BLEND ); + + glEnable( GL_BLEND ); + + annotation_->draw( 250., -250., "Hoodoos @ Bryce Canyon National Park, Utah, USA" ); + + glDisable( GL_BLEND ); + + glPopMatrix(); + } + + void finish ( void ) + { + } +}; + +class Vignette5 : public Vignette { + + static const unsigned int FRAME_COUNT = 124; + static const unsigned int FRAME_RATE = 8; + + const char* text_; + OGLFT::Monochrome* monochrome_; + OGLFT::Grayscale* grayscale_; + OGLFT::Translucent* translucent_; + + OGLFT::Outline* outline_; + OGLFT::Filled* filled_; +#ifndef OGLFT_NO_SOLID + OGLFT::Solid* solid_; +#else + OGLFT::Monochrome* solid_; +#endif + + OGLFT::MonochromeTexture* monochrome_texture_; + OGLFT::GrayscaleTexture* grayscale_texture_; + OGLFT::TranslucentTexture* translucent_texture_; + +public: + Vignette5 ( const char* text, const char* fontfile ) : text_( text ) + { + int point_size = 20; + + monochrome_ = new OGLFT::Monochrome( fontfile, point_size, 75 ); + monochrome_->setHorizontalJustification( OGLFT::Face::CENTER ); + monochrome_->setForegroundColor( 1., 0., 0., 1. ); + + grayscale_ = new OGLFT::Grayscale( fontfile, point_size, 75 ); + grayscale_->setHorizontalJustification( OGLFT::Face::CENTER ); + grayscale_->setForegroundColor( 0., 0., .5, 1. ); + grayscale_->setBackgroundColor( 0., 1., 1., 1. ); + + translucent_ = new OGLFT::Translucent( fontfile, point_size, 75 ); + translucent_->setHorizontalJustification( OGLFT::Face::CENTER ); + translucent_->setForegroundColor( 0., .5, 0., 1. ); + + outline_ = new OGLFT::Outline( fontfile, point_size, 75 ); + outline_->setForegroundColor( 1., 1., 0., 1. ); + outline_->setHorizontalJustification( OGLFT::Face::CENTER ); + + filled_ = new OGLFT::Filled( fontfile, point_size, 75 ); + filled_->setForegroundColor( .5, 0., 1., 1. ); + filled_->setHorizontalJustification( OGLFT::Face::CENTER ); +#ifndef OGLFT_NO_SOLID + solid_ = new OGLFT::Solid( fontfile, point_size, 75 ); + solid_->setDepth( 10. ); + solid_->setCharacterRotationX( 25. ); + solid_->setCharacterRotationY( 25. ); + solid_->setTessellationSteps( 3 ); +#else + solid_ = new OGLFT::Monochrome( fontfile, point_size, 75 ); +#endif + solid_->setHorizontalJustification( OGLFT::Face::CENTER ); + solid_->setForegroundColor( 1., .5, 0., 1. ); + + monochrome_texture_ = new OGLFT::MonochromeTexture( fontfile, point_size, 75 ); + monochrome_texture_->setHorizontalJustification( OGLFT::Face::CENTER ); + monochrome_texture_->setForegroundColor( 0., .5, .75, 1. ); + + grayscale_texture_ = new OGLFT::GrayscaleTexture( fontfile, point_size, 75 ); + grayscale_texture_->setHorizontalJustification( OGLFT::Face::CENTER ); + grayscale_texture_->setForegroundColor( 0.9, .65, .9, 1. ); + grayscale_texture_->setBackgroundColor( 0.5, .5, .75, 0.3 ); + + translucent_texture_ = new OGLFT::TranslucentTexture( fontfile, point_size, 75 ); + translucent_texture_->setHorizontalJustification( OGLFT::Face::CENTER ); + translucent_texture_->setForegroundColor( 0.75, 1., .75, 1. ); + } + + ~Vignette5 ( void ) + { + // std::cout << "destructing Vignette 5" << std::endl; + delete monochrome_; + delete grayscale_; + delete translucent_; + delete outline_; + delete filled_; + delete solid_; + delete monochrome_texture_; + delete grayscale_texture_; + delete translucent_texture_; + } + + unsigned int frame_count ( void ) { return FRAME_COUNT; } + + unsigned int frame_rate ( void ) { return FRAME_RATE; } + + void init ( void ) + { + std::cout << "Vignette 5" << std::endl; + + glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + glEnable( GL_RASTER_POSITION_UNCLIPPED_IBM ); + glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + glEnable( GL_LIGHT0 ); + } + + void view ( GLdouble left, GLdouble right, GLdouble bottom, GLdouble top ) + { + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glOrtho( left, right, bottom, top, -250., 250. ); + glMatrixMode( GL_MODELVIEW ); + } + + void draw ( unsigned int frame_number ) + { + GLdouble y; + + glPushMatrix(); + if ( frame_number <= 12 ) + y = -240. + 490. * ( 12 - frame_number ) / 12; + else + y = -240.; + monochrome_->draw( 0., y, "Draw text as monochrome bitmaps" ); + + if ( frame_number >= 12 ) { + if ( frame_number <= 24 ) + y = -190. + 440. * ( 24 - frame_number ) / 12; + else + y = -190.; + grayscale_->draw( 0., y, "Draw text as antialiased, grayscale pixmaps" ); + } + + if ( frame_number >= 24 ) { + if ( frame_number <= 36 ) + y = -140. + 390. * ( 36 - frame_number ) / 12; + else + y = -140.; + glEnable( GL_BLEND ); + translucent_->draw( 0., y, "Draw text as antialiased, blended pixmaps" ); + glDisable( GL_BLEND ); + } + + if ( frame_number >= 36 ) { + if ( frame_number <= 48 ) + y = -90. + 340. * ( 48 - frame_number ) / 12; + else + y = -90.; + outline_->draw( 0., y, "Draw text as line segments" ); + } + + if ( frame_number >= 48 ) { + if ( frame_number <= 60 ) + y = -40. + 290. * ( 60 - frame_number ) / 12; + else + y = -40.; + filled_->draw( 0., y, "Draw text as filled polygons" ); + } + + if ( frame_number >= 60 ) { + if ( frame_number <= 72 ) + y = 10. + 240. * ( 72 - frame_number ) / 12; + else + y = 10.; +#ifndef OGLFT_NO_SOLID + glEnable( GL_LIGHTING ); + glEnable( GL_DEPTH_TEST ); + glEnable( GL_COLOR_MATERIAL ); + solid_->draw( 0., y, "Draw text as solid with GLE" ); + glDisable( GL_COLOR_MATERIAL ); + glDisable( GL_DEPTH_TEST ); + glDisable( GL_LIGHTING ); +#else + solid_->draw( 0., y, "" ); +#endif + } + + glEnable( GL_TEXTURE_2D ); + glEnable( GL_BLEND ); + + if ( frame_number >= 72 ) { + if ( frame_number <= 84 ) + y = 60. + 190. * ( 84 - frame_number ) / 12; + else + y = 60.; + monochrome_texture_->draw( 0., y, "Draw text as monochrome texture maps" ); + } + + if ( frame_number >= 84 ) { + if ( frame_number <= 96 ) + y = 110. + 140. * ( 96 - frame_number ) / 12; + else + y = 110.; + grayscale_texture_->draw( 0., y, "Draw text as antialiased, grayscale texture maps" ); + } + + if ( frame_number >= 96 ) { + if ( frame_number <= 108 ) + y = 160. + 90. * ( 108 - frame_number ) / 12; + else + y = 160.; + translucent_texture_->draw( 0., y, "Draw text as antialiased, blended texture maps" ); + } + + glDisable( GL_BLEND ); + glDisable( GL_TEXTURE_2D ); + + glPopMatrix(); + } + + void finish ( void ) + { + } +}; + +class Vignette6 : public Vignette { + + static const unsigned int FRAME_COUNT = 48; + static const unsigned int FRAME_RATE = 12; + + const char* text_; + OGLFT::Filled* face_; + + class MyColorTess : public OGLFT::ColorTess { + public: + QColor hsv_; + GLfloat colors_[4]; + int phase_; + MyColorTess () + { + hsv_.setHsv( 0, 255, 255 ); + colors_[OGLFT::R] = hsv_.red() / 255.; + colors_[OGLFT::G] = hsv_.green() / 255.; + colors_[OGLFT::B] = hsv_.blue() / 255.; + colors_[OGLFT::A] = 1.; + phase_ = 0; + } + void setPhase ( int phase ) { phase_ = phase; } + int phase ( void ) const { return phase_; } + }; + + class MyColorTessVertical : public MyColorTess + { + public: + GLfloat* color ( GLdouble* p ) + { + int hue = (int)( 360. * p[OGLFT::Y] / 36. + phase_ ) % 360; + if ( hue < 0 ) hue += 360; + hsv_.setHsv( hue, 255, 255 ); + colors_[OGLFT::R] = hsv_.red() / 255.; + colors_[OGLFT::G] = hsv_.green() / 255.; + colors_[OGLFT::B] = hsv_.blue() / 255.; + return colors_; + } + }; + + class MyColorTessHorizontal : public MyColorTess + { + public: + GLfloat* color ( GLdouble* p ) + { + int hue = (int)( 360. * p[OGLFT::X] / 36. + phase_ ) % 360; + if ( hue < 0 ) hue += 360; + hsv_.setHsv( hue, 255, 255 ); + colors_[OGLFT::R] = hsv_.red() / 255.; + colors_[OGLFT::G] = hsv_.green() / 255.; + colors_[OGLFT::B] = hsv_.blue() / 255.; + return colors_; + } + }; + + MyColorTessVertical color_tess_v_; + MyColorTessHorizontal color_tess_h_; + +public: + Vignette6 ( const char* text, const char* fontfile ) : text_( text ) + { + face_ = new OGLFT::Filled( fontfile, 72, 75 ); + face_->setForegroundColor( 0., .5, .75 ); + face_->setHorizontalJustification( OGLFT::Face::CENTER ); + face_->setVerticalJustification( OGLFT::Face::MIDDLE ); + face_->setTessellationSteps( 3 ); + face_->setCompileMode( OGLFT::Face::IMMEDIATE ); + } + + ~Vignette6 ( void ) + { + // std::cout << "destructing Vignette 6" << std::endl; + delete face_; + } + + unsigned int frame_count ( void ) { return FRAME_COUNT; } + + unsigned int frame_rate ( void ) { return FRAME_RATE; } + + void init ( void ) + { + std::cout << "Vignette 6" << std::endl; + } + + void view ( GLdouble left, GLdouble right, GLdouble bottom, GLdouble top ) + { + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glOrtho( left, right, bottom, top, -250., 250. ); + glMatrixMode( GL_MODELVIEW ); + } + + void draw ( unsigned int /*frame_number*/ ) + { + glPushMatrix(); + face_->setColorTess( &color_tess_h_ ); + face_->draw( 0., 72., "Apply a per" ); + face_->draw( 0., 0., "vertex color" ); + face_->draw( 0., -72., "function" ); + glPopMatrix(); + + color_tess_h_.setPhase( color_tess_h_.phase() + 15 ); + color_tess_v_.setPhase( color_tess_v_.phase() + 15 ); + } + + void finish ( void ) + { + } +}; + +class Vignette7 : public Vignette { + + static const unsigned int FRAME_COUNT = 48; + static const unsigned int FRAME_RATE = 12; + + const char* text_; + OGLFT::Filled* face_; + QImage* image_; + GLuint texture_; + + class MyTextureTess : public OGLFT::TextureTess { + int phase_; + GLfloat texCoords_[2]; + public: + MyTextureTess () + { + texCoords_[0] = texCoords_[1] = 0.; + phase_ = 0; + } + GLfloat* texCoord ( GLdouble* p ) { + texCoords_[0] = ( p[OGLFT::X] + phase_ ) / 18.; + texCoords_[1] = ( p[OGLFT::Y] + phase_ ) / 18.; + return texCoords_; + } + void setPhase ( int phase ) { phase_ = phase; } + int phase ( void ) const { return phase_; } + }; + + MyTextureTess texture_tess_; + +public: + Vignette7 ( const char* text, const char* fontfile ) : text_( text ) + { + face_ = new OGLFT::Filled( fontfile, 72, 75 ); + face_->setForegroundColor( 0., .5, .75 ); + face_->setHorizontalJustification( OGLFT::Face::CENTER ); + face_->setVerticalJustification( OGLFT::Face::MIDDLE ); + face_->setTessellationSteps( 3 ); + face_->setCompileMode( OGLFT::Face::IMMEDIATE ); + face_->setTextureTess( &texture_tess_ ); + + image_ = new QImage( "texture.png" ); + } + + ~Vignette7 ( void ) + { + // std::cout << "destructing Vignette 7" << std::endl; + glDeleteTextures( 1, &texture_ ); + delete image_; + delete face_; + } + + unsigned int frame_count ( void ) { return FRAME_COUNT; } + + unsigned int frame_rate ( void ) { return FRAME_RATE; } + + void init ( void ) + { + std::cout << "Vignette 7" << std::endl; + glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + + glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); + + glGenTextures( 1, &texture_ ); + + glBindTexture( GL_TEXTURE_2D, texture_ ); + + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, image_->width(), image_->height(), + 0, GL_BGRA, GL_UNSIGNED_BYTE, image_->bits() ); + + glEnable( GL_TEXTURE_2D ); + } + + void view ( GLdouble left, GLdouble right, GLdouble bottom, GLdouble top ) + { + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glOrtho( left, right, bottom, top, -250., 250. ); + glMatrixMode( GL_MODELVIEW ); + } + + void draw ( unsigned int frame_number ) + { + glPushMatrix(); + face_->draw( 0., 144., "Apply a" ); + face_->draw( 0., 72., "per vertex" ); + face_->draw( 0., 0., "texture" ); + face_->draw( 0., -72., "function" ); + texture_tess_.setPhase( frame_number ); + glPopMatrix(); + } + + void finish ( void ) + { + glDisable( GL_TEXTURE_2D ); + } +}; + +class Vignette8 : public Vignette { + + static const unsigned int FRAME_COUNT = 48; + static const unsigned int FRAME_RATE = 3; + + char* text_; + unsigned int text_length_; + OGLFT::Monochrome* face_; + + static const int N_SIZES = 8; + static const int sizes_[N_SIZES]; +public: + Vignette8 ( const char* text, const char* fontfile ) + { + face_ = new OGLFT::Monochrome( fontfile, 36, 75 ); + face_->setForegroundColor( 1., 0., 0. ); + text_ = strdup( text ); + text_length_ = strlen( text_ ); + } + + ~Vignette8 ( void ) + { + // std::cout << "destructing Vignette 8" << std::endl; + delete face_; + free( text_ ); + } + + unsigned int frame_count ( void ) { return text_length_ + 3; } + + unsigned int frame_rate ( void ) { return FRAME_RATE; } + + void init ( void ) + { + std::cout << "Vignette 8" << std::endl; + glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + glEnable( GL_RASTER_POSITION_UNCLIPPED_IBM ); + glPushAttrib( GL_POLYGON_BIT ); + } + + void draw ( unsigned int frame_number ) + { + char save_char = '\0'; + if ( frame_number < text_length_ ) { + save_char = text_[frame_number]; + text_[frame_number] = '\0'; + } + + glPushMatrix(); + + glTranslatef( -225., 250., 0. ); + + for ( int i = 0; i < N_SIZES; i++ ) { + glTranslatef( 0., -2. * sizes_[i], 0. ); + + face_->setPointSize( sizes_[i] ); + + OGLFT::BBox size = face_->measure( text_ ); + + glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); + glColor3f( 1., 1., 1. ); + glRectf( size.x_min_, size.y_min_, size.x_max_, size.y_max_ ); + + face_->draw( 0., 0., text_ ); + + glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); + glColor3f( 0., 0., 1. ); + glRectf( size.x_min_, 0., size.x_max_, size.y_max_ ); + } + + if ( frame_number < text_length_ ) + text_[frame_number] = save_char; + + glPopMatrix(); + } + + void view ( GLdouble left, GLdouble right, GLdouble bottom, GLdouble top ) + { + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glOrtho( left, right, bottom, top, -250., 250. ); + glMatrixMode( GL_MODELVIEW ); + } + + void finish ( void ) + { + glPopAttrib(); + } +}; + +const int Vignette8::sizes_[8] = { 64, 48, 32, 24, 18, 12, 10, 6 }; + +class Vignette9 : public Vignette { + + static const unsigned int FRAME_COUNT = 32; + static const unsigned int FRAME_RATE = 8; + + const char* text_; + OGLFT::Filled* face_; + +public: + Vignette9 ( const char* text, const char* fontfile ) : text_( text ) + { + face_ = new OGLFT::Filled( fontfile, 288, 75 ); + face_->setForegroundColor( 0., .5, .75 ); + face_->setHorizontalJustification( OGLFT::Face::CENTER ); + face_->setCharacterRotationX( 12. ); + } + + ~Vignette9 ( void ) + { + // std::cout << "destructing Vignette 9" << std::endl; + delete face_; + } + + unsigned int frame_count ( void ) { return FRAME_COUNT; } + + unsigned int frame_rate ( void ) { return FRAME_RATE; } + + void init ( void ) + { + std::cout << "Vignette 9" << std::endl; + } + + void draw ( unsigned int /*frame_number*/ ) + { + glPushMatrix(); + glTranslatef( 0., 0., -8. ); + face_->draw( 0., 0., text_ ); + face_->setCharacterRotationX( face_->characterRotationX() - 0.5 ); + glPopMatrix(); + } + + void finish ( void ) + { + } +}; + +class Vignette10 : public Vignette { + + static const unsigned int FRAME_COUNT = 108; + static const unsigned int FRAME_RATE = 8; + + const char* text_; + unsigned int text_length_; + OGLFT::Filled* face_; + + GLuint glyph_dl_; + OGLFT::DisplayLists dlists; + GLfloat x_; + +public: + Vignette10 ( const char* text, const char* fontfile ) : text_( text ) + { + face_ = new OGLFT::Filled( fontfile, 24, 75 ); + face_->setForegroundColor( 0., .5, .75 ); + face_->setHorizontalJustification( OGLFT::Face::LEFT ); + + text_length_ = strlen( text_ ); + + x_ = 250.; + } + + ~Vignette10 ( void ) + { + // std::cout << "destructing Vignette 10" << std::endl; + delete face_; + } + + unsigned int frame_count ( void ) { return FRAME_COUNT; } + + unsigned int frame_rate ( void ) { return FRAME_RATE; } + + void init ( void ) + { + std::cout << "Vignette 10" << std::endl; + + glyph_dl_ = glGenLists( 2 * text_length_ ); + + dlists.push_back( 0 ); + + QColor hsv; + + for ( unsigned int i = 0; i < text_length_; i++ ) { + float dy = 50. * ( + sin( M_PI + (i+1) * 4 * M_PI / text_length_ ) + - sin( M_PI + i * 4 * M_PI / text_length_ ) ); + + hsv.setHsv( (int)(360. * i / text_length_), 255, 255 ); + + glNewList( glyph_dl_ + i, GL_COMPILE ); + glTranslatef( 0., dy, 0. ); + glColor3ub( hsv.red(), hsv.green(), hsv.blue() ); + glEndList(); + + dlists.push_back( glyph_dl_ + i ); + } + + for ( unsigned int i = 0; i < text_length_; i++ ) { + float y = 50. * sin( M_PI - i * 4 * M_PI / text_length_ ); + + hsv.setHsv( (int)(360. * (text_length_ - i) / text_length_), 255, 255 ); + + glNewList( glyph_dl_ + text_length_ + i, GL_COMPILE ); + glTranslatef( 0., y, 0. ); + glColor3ub( hsv.red(), hsv.green(), hsv.blue() ); + glEndList(); + + dlists.push_back( glyph_dl_ + text_length_ + i ); + } + + dlists[0] = dlists[text_length_]; + + face_->setCharacterDisplayLists( dlists ); + } + + void view ( GLdouble left, GLdouble right, GLdouble bottom, GLdouble top ) + { + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glOrtho( left, right, bottom, top, -250., 250. ); + glMatrixMode( GL_MODELVIEW ); + } + + void draw ( unsigned int /*frame_number*/ ) + { + OGLFT::DLI first = face_->characterDisplayLists().begin() + 1; + OGLFT::DLI next = first + text_length_ - 1; + OGLFT::DLI last = first + text_length_; + + rotate( first, next, last ); + + first = face_->characterDisplayLists().begin() + text_length_ + 1; + next = first + 1; + last = first + text_length_; + + rotate( first, next, last ); + + face_->characterDisplayLists()[0] = + face_->characterDisplayLists()[text_length_+1]; + + face_->draw( x_, 0., text_ ); + + x_ -= 10.; + } + + void finish ( void ) + { + glDeleteLists( glyph_dl_, 2 * text_length_ ); + } +}; + +class Vignette11 : public Vignette { + + static const unsigned int FRAME_COUNT = 10; + static const unsigned int FRAME_RATE = 1; + + const QString text_; + QString equation_; + OGLFT::Translucent* face_; + +public: + Vignette11 ( const char* text, const char* fontfile ) : text_( text ) + { + // First, open a face in the usual way. + face_ = new OGLFT::Translucent( fontfile, 18, 75 ); + + // Now, create a second face, in this case, using a built-in font. +#if 0 + FT_Library library = OGLFT::Library::instance(); + FT_Face ft_face; + FT_New_Memory_Face( library, Demo3UnicodeExample_ttf, + Demo3UnicodeExample_ttf_size, 0, &ft_face ); + + face_->addAuxiliaryFace( ft_face ); +#else + // Somewhat simpler with new interface. + face_->addAuxiliaryFace( Demo3UnicodeExample_ttf, + Demo3UnicodeExample_ttf_size ); +#endif + face_->setForegroundColor( 0., .5, .75 ); + + // Manually create the equation in UNICODE points + equation_ = QChar( 0x2207 );//Nabla + equation_ += QChar( 0x2219 );//Dot + equation_ += QChar( 0x03a9 );//Omega + equation_ += QChar( 0x03c8 );//psi + equation_ += QChar( ' ' ); + equation_ += QChar( '+' ); + equation_ += QChar( ' ' ); + equation_ += QChar( 0x03c3 );//sigma + equation_ += QChar( 0x03c8 );//psi + equation_ += QChar( ' ' ); + equation_ += QChar( '=' ); + equation_ += QChar( ' ' ); + equation_ += QChar( 0x222b );//integral + equation_ += QChar( 'd' ); + equation_ += QChar( 0x03a9 );//Omega + equation_ += QChar( '\'' ); + equation_ += QChar( 0x03c3 );//sigma + equation_ += QChar( '(' ); + equation_ += QChar( 0x03a9 );//Omega + equation_ += QChar( '\'' ); + equation_ += QChar( 0x2192 );//right arrow + equation_ += QChar( 0x03a9 );//Omega + equation_ += QChar( ')' ); + equation_ += QChar( 0x03c8 );//psi + equation_ += QChar( '(' ); + equation_ += QChar( 0x03a9 );//Omega + equation_ += QChar( '\'' ); + equation_ += QChar( ')' ); + } + + ~Vignette11 ( void ) + { + // std::cout << "destructing Vignette 11" << std::endl; + delete face_; + } + + unsigned int frame_count ( void ) { return FRAME_COUNT; } + + unsigned int frame_rate ( void ) { return FRAME_RATE; } + + void init ( void ) + { + std::cout << "Vignette 11" << std::endl; + + glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + glEnable( GL_RASTER_POSITION_UNCLIPPED_IBM ); + glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + glEnable( GL_BLEND ); + } + + void view ( GLdouble left, GLdouble right, GLdouble bottom, GLdouble top ) + { + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glOrtho( left, right, bottom, top, -250., 250. ); + glMatrixMode( GL_MODELVIEW ); + glTranslated( left, 0, 0 ); + } + + void draw ( unsigned int /*frame_number*/ ) + { + face_->setHorizontalJustification( OGLFT::Face::LEFT ); + face_->draw( 2., 128., "This frame demonstrates two...no three...features:" ); + face_->draw( 2., 96., "1. Drawing a UNICODE string (using Qt's QString)." ); + face_->draw( 2., 64., "2. Combining two fonts to cover more UNICODE points." ); + face_->draw( 2., 32., "3. Embedding a font in the program." ); + + face_->setHorizontalJustification( OGLFT::Face::CENTER ); + face_->draw( 250., 0., equation_ ); + + face_->setHorizontalJustification( OGLFT::Face::LEFT ); + face_->draw( 2., -32., "Thanks to Oliver Bock for the font-in-memory code!" ); + } + + void finish ( void ) + { + glDisable( GL_BLEND ); + } +}; + +class Vignette12 : public Vignette { + + static const unsigned int FRAME_COUNT = 10; + static const unsigned int FRAME_RATE = 1; + + const QString text_; + QString equation_; +#if 1 + OGLFT::Monochrome* face_; +#else + OGLFT::Filled* face_; +#endif +public: + Vignette12 ( const char* text, const char* fontfile ) : text_( text ) + { + // First, open a face in the usual way. +#if 1 + face_ = new OGLFT::Monochrome( fontfile, 18, 75 ); +#else + face_ = new OGLFT::Filled( fontfile, 18, 75 ); +#endif + face_->setForegroundColor( 0., .75, .75 ); + + // Now, create a second face, in this case, using a built-in font. + FT_Library library = OGLFT::Library::instance(); + FT_Face ft_face; + FT_New_Memory_Face( library, lCSymbols_ttf, + lCSymbols_ttf_size, 0, &ft_face ); + + face_->addAuxiliaryFace( ft_face ); + } + + ~Vignette12 ( void ) + { + // cout << "destructing Vignette 12" << endl; + delete face_; + } + + unsigned int frame_count ( void ) { return FRAME_COUNT; } + + unsigned int frame_rate ( void ) { return FRAME_RATE; } + + void init ( void ) + { + std::cout << "Vignette 12" << std::endl; + + glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + glEnable( GL_RASTER_POSITION_UNCLIPPED_IBM ); + glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + glEnable( GL_BLEND ); + } + + void view ( GLdouble left, GLdouble right, GLdouble bottom, GLdouble top ) + { + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glOrtho( left, right, bottom, top, -250., 250. ); + glMatrixMode( GL_MODELVIEW ); + glTranslated( left, 0, 0 ); + } + + void draw ( unsigned int /*frame_number*/ ) + { + face_->setVerticalJustification( OGLFT::Face::TOP ); + face_->setHorizontalJustification( OGLFT::Face::CENTER ); + face_->draw( 250., 250., "Example of numeric formatting" ); + + face_->setVerticalJustification( OGLFT::Face::BASELINE ); + face_->setHorizontalJustification( OGLFT::Face::ORIGIN ); + + face_->draw( 0., 128., "%.6f", 100.123456789 ); + face_->draw( 0., 96., "%12.6e", 100.123456789 ); + face_->draw( 0., 64., "%12.6g", 100.123456789 ); + for ( int i = 1; i <= 8; ++i ) { + for ( int j = 1; j <= i; j++ ) { + double a = i + (double)j / ( 1 << i ); + + OGLFT::BBox bbox = face_->measure( "%p\"", a ); + + glPushMatrix(); + glTranslated( j*42, -i * 18, 0 ); + + glColor3f( .25, .25, .25 ); + glRectd( bbox.x_min_, bbox.y_min_, bbox.x_max_, bbox.y_max_ ); + + face_->draw( 0, 0, "%p\"", a ); + glPopMatrix(); + } + } + + for ( int r = 0; r < 360; r += 45 ) { + double sinr = sin( (double)r/180 * M_PI ); + double cosr = cos( (double)r/180 * M_PI ); + face_->setStringRotation( r ); + OGLFT::BBox bbox = face_->measure( "%p\"", 1.015625 ); + + glPushMatrix(); + glTranslated( 250.+cosr*24, 96.+sinr*24, 0 ); + + glColor3f( .25, .25, .25 ); + glRectd( bbox.x_min_, bbox.y_min_, bbox.x_max_, bbox.y_max_ ); + + face_->draw( 0, 0, "%p\"", 1.015625 ); + glPopMatrix(); + } + + face_->setStringRotation( 0 ); + } + + void finish ( void ) + { + glDisable( GL_BLEND ); + } +}; + +CharacterView::CharacterView ( bool flank_speed, const char* fontfile, + QWidget* parent, const char* name ) + : QGLWidget( ), flank_speed_( flank_speed ) +{ + vignettes.enqueue( new Vignette0( "Welcome to OGLFT!", fontfile ) ); + vignettes.enqueue( new Vignette1( "The OpenGL/FreeType library", fontfile ) ); + vignettes.enqueue( new Vignette2( "Featuring a mind numbing", fontfile ) ); + vignettes.enqueue( new Vignette3( "collection of rendering options", fontfile ) ); + vignettes.enqueue( new Vignette9( "and other text effects.", fontfile ) ); + vignettes.enqueue( new Vignette5( "Sampler", fontfile ) ); + vignettes.enqueue( new Vignette8( "Measuring the text.", fontfile ) ); + vignettes.enqueue( new Vignette6( "Per vertex color", fontfile ) ); + vignettes.enqueue( new Vignette7( "Per vertex texture coord", fontfile ) ); + vignettes.enqueue( new Vignette10( "Each glyph can have it's own display list", fontfile ) ); + vignettes.enqueue( new Vignette11( "QString Example", fontfile ) ); + vignettes.enqueue( new Vignette12( "Formatting Numbers", fontfile ) ); + vignettes.enqueue( new Vignette4( "OGLFT", fontfile ) ); + + frame_counter_ = 0; + counter_snapshot_ = 0; + animation_frame_counter_ = 0; + + animation_frame_count_ = vignettes.current()->frame_count(); + animation_frame_rate_ = 1000 / vignettes.current()->frame_rate(); + + connect( &redraw_timer_, SIGNAL(timeout()), SLOT(redraw()) ); + connect( &performance_timer_, SIGNAL(timeout()), SLOT(measure_performance()) ); + + if ( flank_speed_ ) + redraw_timer_.start( 0 ); + else + redraw_timer_.start( animation_frame_rate_ ); + + performance_timer_.start( PERFORMANCE_SAMPLE_RATE_HZ * 1000 ); +} + +void CharacterView::redraw ( void ) +{ + updateGL(); + + frame_counter_++; + animation_frame_counter_++; + + if ( animation_frame_counter_ >= animation_frame_count_ ) { + redraw_timer_.stop(); + vignettes.current()->finish(); + delete vignettes.dequeue(); + + if ( !vignettes.isEmpty() ) { + counter_snapshot_ = frame_counter_; + animation_frame_counter_ = 0; + + vignettes.current()->init(); + resetView(); + animation_frame_count_ = vignettes.current()->frame_count(); + animation_frame_rate_ = 1000 / vignettes.current()->frame_rate(); + + if ( flank_speed_ ) + redraw_timer_.start( 0 ); + else + redraw_timer_.start( animation_frame_rate_ ); + } + else { + // Evidently, events may be processed during exiting, so... + redraw_timer_.stop(); + performance_timer_.stop(); + qApp->exit( 0 ); + } + } +} + +void CharacterView::measure_performance ( void ) +{ + int delta_count = frame_counter_ - counter_snapshot_; + + std::cout << delta_count << " FPS" << std::endl; + + counter_snapshot_ = frame_counter_; +} + +void CharacterView::initializeGL ( void ) +{ + std::cout << glGetString( GL_VENDOR ) << " " << glGetString( GL_RENDERER ) << " " + << glGetString( GL_VERSION ) << std::endl; + + glClearColor( 0., 0., 0., 1. ); + + // Let the first vignette do any initialization it wants (though this + // should probably be called by the animation controller). + + vignettes.current()->init(); +} + +void CharacterView::resizeGL ( int w, int h ) +{ + window_width_ = w; + window_height_ = h; + + glViewport( 0, 0, window_width_, window_height_ ); + + view_width_ = window_width_; + view_height_ = window_height_; + + view_left_ = -view_width_ / 2.; + view_right_ = view_width_ / 2.; + view_bottom_ = -view_height_ / 2.; + view_top_ = view_height_ / 2.; + + resetView(); +} + +void CharacterView::paintGL ( void ) +{ + + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + + vignettes.current()->draw( animation_frame_counter_ % animation_frame_count_ ); +} + +void CharacterView::keyPressEvent ( QKeyEvent* e ) +{ + switch ( e->key() ) { + case Key_Q: + case Key_Escape: + qApp->exit( 0 ); + case Key_Return: + // "Speed up the harvest." L, Skywalker + animation_frame_counter_ = animation_frame_count_; + updateGL(); + } +} + +void CharacterView::resetView ( void ) +{ + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + + glFrustum( view_left_, view_right_, view_bottom_, view_top_, + 1., 100. ); + + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + + vignettes.current()->view( view_left_, view_right_, view_bottom_, view_top_ ); +} + +int main ( int argc, char* argv[] ) +{ + + QApplication app( argc, argv ); + + if ( argc < 2 ) { + std::cerr << "usage: " << argv[0] << " [-f] fontfile" << std::endl; + std::cerr << " -f ignore the animation timings and run as fast as possible" + << std::endl; + return 1; + } + + int argn = 1; + bool flank_speed = false; + + if ( !strcmp( argv[argn], "-f" ) ) { + flank_speed = true; + argn++; + } + + if ( argc <= argn ) { + std::cerr << "usage: " << argv[0] << " [-f] fontfile" << std::endl; + std::cerr << " -f ignore the animation timings and run as fast as possible" + << std::endl; + return 1; + } + + // Test the supplied face to make sure it will work OK + + OGLFT::Monochrome* test_face = new OGLFT::Monochrome( argv[argn] ); + + if ( !test_face->isValid() ) { + std::cerr << "Freetype did not recognize \"" << argv[1] << "\" as a font file" + << std::endl; + return 1; + } + + delete test_face; + + CharacterView cv( flank_speed, argv[argn] ); + cv.resize( 500, 500 ); + + app.setMainWidget( &cv ); + cv.show(); + + return app.exec(); +} diff --git a/engine/libraries/oglft/tests/speedtest.cpp b/engine/libraries/oglft/tests/speedtest.cpp new file mode 100644 index 0000000..b24a7af --- /dev/null +++ b/engine/libraries/oglft/tests/speedtest.cpp @@ -0,0 +1,1298 @@ +/* + * speedtest.cpp: Performance test for the OGLFT library + * Copyright (C) 2002 lignum Computing, Inc. + * $Id$ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include + +#include + +#include + +#include "speedtest.h" +#include "speedtest.moc" + +class Vignette0 : public Vignette { + + static const unsigned int FRAME_COUNT = 128; // Not really + + const char* text_; + OGLFT::Face* face_; + +public: + Vignette0 ( const char* text, const char* /*fontfile*/ ) : text_( text ) + { + } + + unsigned int frame_count ( void ) { return FRAME_COUNT; } + + void init ( void ) + { + std::cout << "Vignette 0: Color and depth buffer clearing only" << std::endl; + } + + void draw ( int /*frame_number*/ ) + { + } + + void finish ( void ) + { + } +}; + +class Vignette1 : public Vignette { + + static const unsigned int FRAME_COUNT = 128; // Not really + + const char* text_; + OGLFT::Monochrome* face_; + +public: + Vignette1 ( const char* text, const char* fontfile ) : text_( text ) + { + face_ = new OGLFT::Monochrome( fontfile, 20, 75 ); + face_->setCompileMode( OGLFT::Face::IMMEDIATE ); + } + + unsigned int frame_count ( void ) { return FRAME_COUNT; } + + void init ( void ) + { + std::cout << "Vignette 1: MONOCHROME: immediate drawing: " << text_ << std::endl; + glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + glEnable( GL_RASTER_POSITION_UNCLIPPED_IBM ); + } + + void draw ( int /*frame_number*/ ) + { + face_->draw( 0., 0., text_ ); + } + + void finish ( void ) + { + } +}; + +class Vignette2 : public Vignette { + + static const unsigned int FRAME_COUNT = 128; // Not really + + const char* text_; + OGLFT::Monochrome* face_; + +public: + Vignette2 ( const char* text, const char* fontfile ) : text_( text ) + { + face_ = new OGLFT::Monochrome( fontfile, 20, 75 ); + // face_->setCompileMode( OGLFT::Face::IMMEDIATE ); + } + + unsigned int frame_count ( void ) { return FRAME_COUNT; } + + void init ( void ) + { + std::cout << "Vignette 2: MONOCHROME: cached glyphs: " << text_ << std::endl; + glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + glEnable( GL_RASTER_POSITION_UNCLIPPED_IBM ); + } + + void draw ( int /*frame_number*/ ) + { + face_->draw( 0., 0., text_ ); + } + + void finish ( void ) + { + } +}; + +class Vignette3 : public Vignette { + + static const unsigned int FRAME_COUNT = 128; // Not really + + const char* text_; + OGLFT::Monochrome* face_; + + GLuint dlist_; + +public: + Vignette3 ( const char* text, const char* fontfile ) : text_( text ) + { + face_ = new OGLFT::Monochrome( fontfile, 20, 75 ); + // face_->setCompileMode( OGLFT::Face::IMMEDIATE ); + face_->setAdvance( false ); + } + + unsigned int frame_count ( void ) { return FRAME_COUNT; } + + void init ( void ) + { + std::cout << "Vignette 3: MONOCHROME: display list: " << text_ << std::endl; + glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + glEnable( GL_RASTER_POSITION_UNCLIPPED_IBM ); + dlist_ = face_->compile( text_ ); + } + + void draw ( int /*frame_number*/ ) + { + glRasterPos2f( 0., 0. ); + glCallList( dlist_ ); + } + + void finish ( void ) + { + } +}; + +class Vignette4 : public Vignette { + + static const unsigned int FRAME_COUNT = 128; // Not really + + const char* text_; + OGLFT::Grayscale* face_; + + GLuint dlist_; + +public: + Vignette4 ( const char* text, const char* fontfile ) : text_( text ) + { + face_ = new OGLFT::Grayscale( fontfile, 20, 75 ); + face_->setCompileMode( OGLFT::Face::IMMEDIATE ); + } + + unsigned int frame_count ( void ) { return FRAME_COUNT; } + + void init ( void ) + { + std::cout << "Vignette 4: GRAYSCALE: immediate drawing: " << text_ << std::endl; + glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + glEnable( GL_RASTER_POSITION_UNCLIPPED_IBM ); + } + + void draw ( int /*frame_number*/ ) + { + face_->draw( 0., 0., text_ ); + } + + void finish ( void ) + { + } +}; + +class Vignette5 : public Vignette { + + static const unsigned int FRAME_COUNT = 128; // Not really + + const char* text_; + OGLFT::Grayscale* face_; + +public: + Vignette5 ( const char* text, const char* fontfile ) : text_( text ) + { + face_ = new OGLFT::Grayscale( fontfile, 20, 75 ); + // face_->setCompileMode( OGLFT::Face::IMMEDIATE ); + } + + unsigned int frame_count ( void ) { return FRAME_COUNT; } + + void init ( void ) + { + std::cout << "Vignette 5: GRAYSCALE: cached glyphs: " << text_ << std::endl; + glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + glEnable( GL_RASTER_POSITION_UNCLIPPED_IBM ); + } + + void draw ( int /*frame_number*/ ) + { + face_->draw( 0., 0., text_ ); + } + + void finish ( void ) + { + } +}; + +class Vignette6 : public Vignette { + + static const unsigned int FRAME_COUNT = 128; // Not really + + const char* text_; + OGLFT::Grayscale* face_; + + GLuint dlist_; + +public: + Vignette6 ( const char* text, const char* fontfile ) : text_( text ) + { + face_ = new OGLFT::Grayscale( fontfile, 20, 75 ); + // face_->setCompileMode( OGLFT::Face::IMMEDIATE ); + } + + unsigned int frame_count ( void ) { return FRAME_COUNT; } + + void init ( void ) + { + std::cout << "Vignette 6: GRAYSCALE: display list: " << text_ << std::endl; + glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + glEnable( GL_RASTER_POSITION_UNCLIPPED_IBM ); + dlist_ = face_->compile( text_ ); + } + + void draw ( int /*frame_number*/ ) + { + glPushMatrix(); + glRasterPos2f( 0., 0. ); + glCallList( dlist_ ); + glPopMatrix(); + } + + void finish ( void ) + { + } +}; + +class Vignette7 : public Vignette { + + static const unsigned int FRAME_COUNT = 128; // Not really + + const char* text_; + OGLFT::Translucent* face_; + + GLuint dlist_; + +public: + Vignette7 ( const char* text, const char* fontfile ) : text_( text ) + { + face_ = new OGLFT::Translucent( fontfile, 20, 75 ); + face_->setCompileMode( OGLFT::Face::IMMEDIATE ); + } + + unsigned int frame_count ( void ) { return FRAME_COUNT; } + + void init ( void ) + { + std::cout << "Vignette 7: TRANSLUCENT: immediate drawing: " << text_ << std::endl; + glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + glEnable( GL_BLEND ); + glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + glEnable( GL_RASTER_POSITION_UNCLIPPED_IBM ); + } + + void draw ( int /*frame_number*/ ) + { + face_->draw( 0., 0., text_ ); + } + + void finish ( void ) + { + glDisable( GL_BLEND ); + } +}; + +class Vignette8 : public Vignette { + + static const unsigned int FRAME_COUNT = 128; // Not really + + const char* text_; + OGLFT::Translucent* face_; + +public: + Vignette8 ( const char* text, const char* fontfile ) : text_( text ) + { + face_ = new OGLFT::Translucent( fontfile, 20, 75 ); + // face_->setCompileMode( OGLFT::Face::IMMEDIATE ); + } + + unsigned int frame_count ( void ) { return FRAME_COUNT; } + + void init ( void ) + { + std::cout << "Vignette 8: TRANSLUCENT: cached glyphs: " << text_ << std::endl; + glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + glEnable( GL_BLEND ); + glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + glEnable( GL_RASTER_POSITION_UNCLIPPED_IBM ); + } + + void draw ( int /*frame_number*/ ) + { + face_->draw( 0., 0., text_ ); + } + + void finish ( void ) + { + glDisable( GL_BLEND ); + } +}; + +class Vignette9 : public Vignette { + + static const unsigned int FRAME_COUNT = 128; // Not really + + const char* text_; + OGLFT::Translucent* face_; + + GLuint dlist_; + +public: + Vignette9 ( const char* text, const char* fontfile ) : text_( text ) + { + face_ = new OGLFT::Translucent( fontfile, 20, 75 ); + // face_->setCompileMode( OGLFT::Face::IMMEDIATE ); + } + + unsigned int frame_count ( void ) { return FRAME_COUNT; } + + void init ( void ) + { + std::cout << "Vignette 9: TRANSLUCENT: display list: " << text_ << std::endl; + glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + glEnable( GL_BLEND ); + glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + glEnable( GL_RASTER_POSITION_UNCLIPPED_IBM ); + dlist_ = face_->compile( text_ ); + } + + void draw ( int /*frame_number*/ ) + { + glPushMatrix(); + glRasterPos2f( 0., 0. ); + glCallList( dlist_ ); + glPopMatrix(); + } + + void finish ( void ) + { + glDisable( GL_BLEND ); + } +}; + +class Vignette10 : public Vignette { + + static const unsigned int FRAME_COUNT = 128; // Not really + + const char* text_; + OGLFT::Outline* face_; + + GLuint dlist_; + +public: + Vignette10 ( const char* text, const char* fontfile ) : text_( text ) + { + face_ = new OGLFT::Outline( fontfile, 20, 75 ); + face_->setCompileMode( OGLFT::Face::IMMEDIATE ); + } + + unsigned int frame_count ( void ) { return FRAME_COUNT; } + + void init ( void ) + { + std::cout << "Vignette 10: OUTLINE: immediate drawing: " << text_ << std::endl; + } + + void draw ( int /*frame_number*/ ) + { + face_->draw( 0., 0., text_ ); + } + + void finish ( void ) + { + } +}; + +class Vignette11 : public Vignette { + + static const unsigned int FRAME_COUNT = 128; // Not really + + const char* text_; + OGLFT::Outline* face_; + +public: + Vignette11 ( const char* text, const char* fontfile ) : text_( text ) + { + face_ = new OGLFT::Outline( fontfile, 20, 75 ); + // face_->setCompileMode( OGLFT::Face::IMMEDIATE ); + } + + unsigned int frame_count ( void ) { return FRAME_COUNT; } + + void init ( void ) + { + std::cout << "Vignette 11: OUTLINE: cached glyphs: " << text_ << std::endl; + } + + void draw ( int /*frame_number*/ ) + { + face_->draw( 0., 0., text_ ); + } + + void finish ( void ) + { + } +}; + +class Vignette12 : public Vignette { + + static const unsigned int FRAME_COUNT = 128; // Not really + + const char* text_; + OGLFT::Outline* face_; + + GLuint dlist_; + +public: + Vignette12 ( const char* text, const char* fontfile ) : text_( text ) + { + face_ = new OGLFT::Outline( fontfile, 20, 75 ); + // face_->setCompileMode( OGLFT::Face::IMMEDIATE ); + face_->setAdvance( false ); + } + + unsigned int frame_count ( void ) { return FRAME_COUNT; } + + void init ( void ) + { + std::cout << "Vignette 12: OUTLINE: display list: " << text_ << std::endl; + dlist_ = face_->compile( text_ ); + } + + void draw ( int /*frame_number*/ ) + { + // glPushMatrix(); + glCallList( dlist_ ); + // glPopMatrix(); + } + + void finish ( void ) + { + } +}; + +class Vignette13 : public Vignette { + + static const unsigned int FRAME_COUNT = 128; // Not really + + const char* text_; + OGLFT::Filled* face_; + + GLuint dlist_; + +public: + Vignette13 ( const char* text, const char* fontfile ) : text_( text ) + { + face_ = new OGLFT::Filled( fontfile, 20, 75 ); + face_->setCompileMode( OGLFT::Face::IMMEDIATE ); + } + + unsigned int frame_count ( void ) { return FRAME_COUNT; } + + void init ( void ) + { + std::cout << "Vignette 13: FILLED: immediate drawing: " << text_ << std::endl; + } + + void draw ( int /*frame_number*/ ) + { + face_->draw( 0., 0., text_ ); + } + + void finish ( void ) + { + } +}; + +class Vignette14 : public Vignette { + + static const unsigned int FRAME_COUNT = 128; // Not really + + const char* text_; + OGLFT::Filled* face_; + +public: + Vignette14 ( const char* text, const char* fontfile ) : text_( text ) + { + face_ = new OGLFT::Filled( fontfile, 20, 75 ); + // face_->setCompileMode( OGLFT::Face::IMMEDIATE ); + } + + unsigned int frame_count ( void ) { return FRAME_COUNT; } + + void init ( void ) + { + std::cout << "Vignette 14: FILLED: cached glyphs: " << text_ << std::endl; + } + + void draw ( int /*frame_number*/ ) + { + face_->draw( 0., 0., text_ ); + } + + void finish ( void ) + { + } +}; + +class Vignette15 : public Vignette { + + static const unsigned int FRAME_COUNT = 128; // Not really + + const char* text_; + OGLFT::Filled* face_; + + GLuint dlist_; + +public: + Vignette15 ( const char* text, const char* fontfile ) : text_( text ) + { + face_ = new OGLFT::Filled( fontfile, 20, 75 ); + // face_->setCompileMode( OGLFT::Face::IMMEDIATE ); + } + + unsigned int frame_count ( void ) { return FRAME_COUNT; } + + void init ( void ) + { + std::cout << "Vignette 15: FILLED: display list: " << text_ << std::endl; + dlist_ = face_->compile( text_ ); + } + + void draw ( int /*frame_number*/ ) + { + glPushMatrix(); + glCallList( dlist_ ); + glPopMatrix(); + } + + void finish ( void ) + { + } +}; +#ifndef OGLFT_NO_SOLID +class Vignette16 : public Vignette { + + static const unsigned int FRAME_COUNT = 128; // Not really + + const char* text_; + OGLFT::Solid* face_; + + GLuint dlist_; + +public: + Vignette16 ( const char* text, const char* fontfile ) : text_( text ) + { + face_ = new OGLFT::Solid( fontfile, 20, 75 ); + face_->setCompileMode( OGLFT::Face::IMMEDIATE ); + } + + unsigned int frame_count ( void ) { return FRAME_COUNT; } + + void init ( void ) + { + std::cout << "Vignette 16: SOLID: immediate drawing: " << text_ << std::endl; + glEnable( GL_LIGHTING ); + glEnable( GL_LIGHT0 ); + } + + void draw ( int /*frame_number*/ ) + { + face_->draw( 0., 0., text_ ); + } + + void finish ( void ) + { + glDisable( GL_LIGHTING ); + } +}; + +class Vignette17 : public Vignette { + + static const unsigned int FRAME_COUNT = 128; // Not really + + const char* text_; + OGLFT::Solid* face_; + +public: + Vignette17 ( const char* text, const char* fontfile ) : text_( text ) + { + face_ = new OGLFT::Solid( fontfile, 20, 75 ); + // face_->setCompileMode( OGLFT::Face::IMMEDIATE ); + } + + unsigned int frame_count ( void ) { return FRAME_COUNT; } + + void init ( void ) + { + std::cout << "Vignette 17: SOLID: cached glyphs: " << text_ << std::endl; + glEnable( GL_LIGHTING ); + glEnable( GL_LIGHT0 ); + } + + void draw ( int /*frame_number*/ ) + { + face_->draw( 0., 0., text_ ); + } + + void finish ( void ) + { + glDisable( GL_LIGHTING ); + } +}; + +class Vignette18 : public Vignette { + + static const unsigned int FRAME_COUNT = 128; // Not really + + const char* text_; + OGLFT::Solid* face_; + + GLuint dlist_; + +public: + Vignette18 ( const char* text, const char* fontfile ) : text_( text ) + { + face_ = new OGLFT::Solid( fontfile, 20, 75 ); + // face_->setCompileMode( OGLFT::Face::IMMEDIATE ); + } + + unsigned int frame_count ( void ) { return FRAME_COUNT; } + + void init ( void ) + { + std::cout << "Vignette 18: SOLID: display list: " << text_ << std::endl; + glEnable( GL_LIGHTING ); + glEnable( GL_LIGHT0 ); + dlist_ = face_->compile( text_ ); + } + + void draw ( int /*frame_number*/ ) + { + glPushMatrix(); + glCallList( dlist_ ); + glPopMatrix(); + } + + void finish ( void ) + { + glDisable( GL_LIGHTING ); + } +}; +#endif // OGLFT_NO_SOLID +class Vignette19 : public Vignette { + + static const unsigned int FRAME_COUNT = 128; // Not really + + const char* text_; + OGLFT::MonochromeTexture* face_; + + GLuint dlist_; + +public: + Vignette19 ( const char* text, const char* fontfile ) : text_( text ) + { + face_ = new OGLFT::MonochromeTexture( fontfile, 20, 75 ); + face_->setCompileMode( OGLFT::Face::IMMEDIATE ); + } + + unsigned int frame_count ( void ) { return FRAME_COUNT; } + + void init ( void ) + { + std::cout << "Vignette 19: MONOCHROME TEXTURE: immediate drawing: " << text_ << std::endl; + glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + glEnable( GL_TEXTURE_2D ); + glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); + glEnable( GL_BLEND ); + glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + } + + void draw ( int /*frame_number*/ ) + { + face_->draw( 0., 0., text_ ); + } + + void finish ( void ) + { + glDisable( GL_TEXTURE_2D ); + } +}; + +class Vignette20 : public Vignette { + + static const unsigned int FRAME_COUNT = 128; // Not really + + const char* text_; + OGLFT::MonochromeTexture* face_; + +public: + Vignette20 ( const char* text, const char* fontfile ) : text_( text ) + { + face_ = new OGLFT::MonochromeTexture( fontfile, 20, 75 ); + // face_->setCompileMode( OGLFT::Face::IMMEDIATE ); + } + + unsigned int frame_count ( void ) { return FRAME_COUNT; } + + void init ( void ) + { + std::cout << "Vignette 20: MONOCHROME TEXTURE: cached glyphs: " << text_ << std::endl; + glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + glEnable( GL_TEXTURE_2D ); + glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); + } + + void draw ( int /*frame_number*/ ) + { + face_->draw( 0., 0., text_ ); + } + + void finish ( void ) + { + glDisable( GL_TEXTURE_2D ); + } +}; + +class Vignette21 : public Vignette { + + static const unsigned int FRAME_COUNT = 128; // Not really + + const char* text_; + OGLFT::MonochromeTexture* face_; + + GLuint dlist_; + +public: + Vignette21 ( const char* text, const char* fontfile ) : text_( text ) + { + face_ = new OGLFT::MonochromeTexture( fontfile, 20, 75 ); + // face_->setCompileMode( OGLFT::Face::IMMEDIATE ); + } + + unsigned int frame_count ( void ) { return FRAME_COUNT; } + + void init ( void ) + { + std::cout << "Vignette 21: MONOCHROME TEXTURE: display list: " << text_ << std::endl; + glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + glEnable( GL_TEXTURE_2D ); + glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); + dlist_ = face_->compile( text_ ); + } + + void draw ( int /*frame_number*/ ) + { + glPushMatrix(); + glCallList( dlist_ ); + glPopMatrix(); + } + + void finish ( void ) + { + glDisable( GL_TEXTURE_2D ); + } +}; + +class Vignette22 : public Vignette { + + static const unsigned int FRAME_COUNT = 128; // Not really + + const char* text_; + OGLFT::GrayscaleTexture* face_; + + GLuint dlist_; + +public: + Vignette22 ( const char* text, const char* fontfile ) : text_( text ) + { + face_ = new OGLFT::GrayscaleTexture( fontfile, 20, 75 ); + face_->setCompileMode( OGLFT::Face::IMMEDIATE ); + } + + unsigned int frame_count ( void ) { return FRAME_COUNT; } + + void init ( void ) + { + std::cout << "Vignette 22: GRAYSCALE TEXTURE: immediate drawing: " << text_ << std::endl; + glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + glEnable( GL_TEXTURE_2D ); + glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); + } + + void draw ( int /*frame_number*/ ) + { + face_->draw( 0., 0., text_ ); + } + + void finish ( void ) + { + glDisable( GL_TEXTURE_2D ); + } +}; + +class Vignette23 : public Vignette { + + static const unsigned int FRAME_COUNT = 128; // Not really + + const char* text_; + OGLFT::GrayscaleTexture* face_; + +public: + Vignette23 ( const char* text, const char* fontfile ) : text_( text ) + { + face_ = new OGLFT::GrayscaleTexture( fontfile, 20, 75 ); + // face_->setCompileMode( OGLFT::Face::IMMEDIATE ); + } + + unsigned int frame_count ( void ) { return FRAME_COUNT; } + + void init ( void ) + { + std::cout << "Vignette 23: GRAYSCALE TEXTURE: cached glyphs: " << text_ << std::endl; + glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + glEnable( GL_TEXTURE_2D ); + glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); + } + + void draw ( int /*frame_number*/ ) + { + face_->draw( 0., 0., text_ ); + } + + void finish ( void ) + { + glDisable( GL_TEXTURE_2D ); + } +}; + +class Vignette24 : public Vignette { + + static const unsigned int FRAME_COUNT = 128; // Not really + + const char* text_; + OGLFT::GrayscaleTexture* face_; + + GLuint dlist_; + +public: + Vignette24 ( const char* text, const char* fontfile ) : text_( text ) + { + face_ = new OGLFT::GrayscaleTexture( fontfile, 20, 75 ); + // face_->setCompileMode( OGLFT::Face::IMMEDIATE ); + } + + unsigned int frame_count ( void ) { return FRAME_COUNT; } + + void init ( void ) + { + std::cout << "Vignette 24: GRAYSCALE TEXTURE: display list: " << text_ << std::endl; + glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + glEnable( GL_TEXTURE_2D ); + glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); + dlist_ = face_->compile( text_ ); + } + + void draw ( int /*frame_number*/ ) + { + glPushMatrix(); + glCallList( dlist_ ); + glPopMatrix(); + } + + void finish ( void ) + { + glDisable( GL_TEXTURE_2D ); + } +}; + +class Vignette25 : public Vignette { + + static const unsigned int FRAME_COUNT = 128; // Not really + + const char* text_; + OGLFT::TranslucentTexture* face_; + + GLuint dlist_; + +public: + Vignette25 ( const char* text, const char* fontfile ) : text_( text ) + { + face_ = new OGLFT::TranslucentTexture( fontfile, 20, 75 ); + face_->setCompileMode( OGLFT::Face::IMMEDIATE ); + } + + unsigned int frame_count ( void ) { return FRAME_COUNT; } + + void init ( void ) + { + std::cout << "Vignette 25: TRANSLUCENT TEXTURE: immediate drawing: " << text_ << std::endl; + glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + glEnable( GL_TEXTURE_2D ); + glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); + glEnable( GL_BLEND ); + glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + } + + void draw ( int /*frame_number*/ ) + { + face_->draw( 0., 0., text_ ); + } + + void finish ( void ) + { + glDisable( GL_TEXTURE_2D ); + glDisable( GL_BLEND ); + } +}; + +class Vignette26 : public Vignette { + + static const unsigned int FRAME_COUNT = 128; // Not really + + const char* text_; + OGLFT::TranslucentTexture* face_; + +public: + Vignette26 ( const char* text, const char* fontfile ) : text_( text ) + { + face_ = new OGLFT::TranslucentTexture( fontfile, 20, 75 ); + // face_->setCompileMode( OGLFT::Face::IMMEDIATE ); + } + + unsigned int frame_count ( void ) { return FRAME_COUNT; } + + void init ( void ) + { + std::cout << "Vignette 26: TRANSLUCENT TEXTURE: cached glyphs: " << text_ << std::endl; + glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + glEnable( GL_TEXTURE_2D ); + glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); + glEnable( GL_BLEND ); + glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + } + + void draw ( int /*frame_number*/ ) + { + face_->draw( 0., 0., text_ ); + } + + void finish ( void ) + { + glDisable( GL_TEXTURE_2D ); + glDisable( GL_BLEND ); + } +}; + +class Vignette27 : public Vignette { + + static const unsigned int FRAME_COUNT = 128; // Not really + + const char* text_; + OGLFT::TranslucentTexture* face_; + + GLuint dlist_; + +public: + Vignette27 ( const char* text, const char* fontfile ) : text_( text ) + { + face_ = new OGLFT::TranslucentTexture( fontfile, 20, 75 ); + // face_->setCompileMode( OGLFT::Face::IMMEDIATE ); + } + + unsigned int frame_count ( void ) { return FRAME_COUNT; } + + void init ( void ) + { + std::cout << "Vignette 27: TRANSLUCENT TEXTURE: display list: " << text_ << std::endl; + glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + glEnable( GL_TEXTURE_2D ); + glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); + glEnable( GL_BLEND ); + glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + dlist_ = face_->compile( text_ ); + } + + void draw ( int /*frame_number*/ ) + { + glPushMatrix(); + glCallList( dlist_ ); + glPopMatrix(); + } + + void finish ( void ) + { + glDisable( GL_TEXTURE_2D ); + glDisable( GL_BLEND ); + } +}; + +CharacterView::CharacterView ( const char* text, const char* fontfile, + QWidget* parent, const char* name ) + : QGLWidget( parent, name ) +{ + vignettes.enqueue( new Vignette0( text, fontfile ) ); + vignettes.enqueue( new Vignette1( text, fontfile ) ); + vignettes.enqueue( new Vignette2( text, fontfile ) ); + vignettes.enqueue( new Vignette3( text, fontfile ) ); + vignettes.enqueue( new Vignette4( text, fontfile ) ); + vignettes.enqueue( new Vignette5( text, fontfile ) ); + vignettes.enqueue( new Vignette6( text, fontfile ) ); + vignettes.enqueue( new Vignette7( text, fontfile ) ); + vignettes.enqueue( new Vignette8( text, fontfile ) ); + vignettes.enqueue( new Vignette9( text, fontfile ) ); + vignettes.enqueue( new Vignette10( text, fontfile ) ); + vignettes.enqueue( new Vignette11( text, fontfile ) ); + vignettes.enqueue( new Vignette12( text, fontfile ) ); + vignettes.enqueue( new Vignette13( text, fontfile ) ); + vignettes.enqueue( new Vignette14( text, fontfile ) ); + vignettes.enqueue( new Vignette15( text, fontfile ) ); +#ifndef OGLFT_NO_SOLID + vignettes.enqueue( new Vignette16( text, fontfile ) ); + vignettes.enqueue( new Vignette17( text, fontfile ) ); + vignettes.enqueue( new Vignette18( text, fontfile ) ); +#endif + vignettes.enqueue( new Vignette19( text, fontfile ) ); + vignettes.enqueue( new Vignette20( text, fontfile ) ); + vignettes.enqueue( new Vignette21( text, fontfile ) ); + vignettes.enqueue( new Vignette22( text, fontfile ) ); + vignettes.enqueue( new Vignette23( text, fontfile ) ); + vignettes.enqueue( new Vignette24( text, fontfile ) ); + vignettes.enqueue( new Vignette25( text, fontfile ) ); + vignettes.enqueue( new Vignette26( text, fontfile ) ); + vignettes.enqueue( new Vignette27( text, fontfile ) ); + + frame_counter_ = 0; + counter_snapshot_ = 0; + iteration_counter_ = MAXIMUM_ITERATIONS; + + animation_frame_count_ = vignettes.current()->frame_count(); + + connect( &redraw_timer_, SIGNAL(timeout()), SLOT(redraw()) ); + connect( &performance_timer_, SIGNAL(timeout()), SLOT(measure_performance()) ); + + redraw_timer_.start( 0 ); + performance_timer_.start( PERFORMANCE_SAMPLE_RATE_HZ * 1000 ); + +} + +void CharacterView::redraw ( void ) +{ + frame_counter_++; + updateGL(); +} + +void CharacterView::measure_performance ( void ) +{ + int delta_count = frame_counter_ - counter_snapshot_; + + std::cout << delta_count << " FPS" << std::endl; + + counter_snapshot_ = frame_counter_; + + iteration_counter_--; + + if ( iteration_counter_ == 0 ) { + vignettes.current()->finish(); + vignettes.dequeue(); + + if ( !vignettes.isEmpty() ) { + frame_counter_ = 0; + counter_snapshot_ = 0; + iteration_counter_ = MAXIMUM_ITERATIONS; + + vignettes.current()->init(); + animation_frame_count_ = vignettes.current()->frame_count(); + } + else { + // Evidently, events may be processed during exiting, so... + redraw_timer_.stop(); + performance_timer_.stop(); + qApp->exit( 0 ); + } + } +} + +void CharacterView::initializeGL ( void ) +{ + std::cout << glGetString( GL_VENDOR ) << " " << glGetString( GL_RENDERER ) << " " + << glGetString( GL_VERSION ) << std::endl; + + glClearColor( 0.75, 0.75, 0.75, 1. ); + + // Let the first vignette do any initialization it wants (though this + // should probably be called by the animation controller). + + vignettes.current()->init(); +} + +void CharacterView::resizeGL ( int w, int h ) +{ + window_width_ = w; + window_height_ = h; + + glViewport( 0, 0, window_width_, window_height_ ); + + view_width_ = window_width_; + view_height_ = window_height_; + + view_left_ = -view_width_ / 2.; + view_right_ = view_width_ / 2.; + view_bottom_ = -view_height_ / 2.; + view_top_ = view_height_ / 2.; + + rot_x_ = rot_y_ = rot_z_ = 0.; + + resetView(); +} + +void CharacterView::paintGL ( void ) +{ + + glClear( GL_COLOR_BUFFER_BIT ); + + vignettes.current()->draw( frame_counter_ % animation_frame_count_ ); +} + +void CharacterView::keyPressEvent ( QKeyEvent* e ) +{ + static const GLdouble ZOOM = 1.0625; + static const GLdouble PAN = 0.002; + static const GLdouble ROTATOR = 1.; + GLdouble center; + bool redraw = true; + GLdouble speed = 1.0; + + if ( e->state() & ShiftButton ) + speed *= 2.0; + if ( e->state() & ControlButton ) + speed *= 2.0; + if ( e->state() & AltButton ) + speed *= 2.0; + + switch ( e->key() ) { + case Key_PageUp: + view_width_ = (GLdouble)view_width_ / (speed * ZOOM); + center = ( view_left_ + view_right_ ) / 2.; + view_left_ = center - view_width_ / 2.; + view_right_ = center + view_width_ / 2.; + + view_height_ = (GLdouble)view_height_ / (speed * ZOOM); + center = ( view_bottom_ + view_top_ ) / 2.; + view_bottom_ = center - view_height_ / 2.; + view_top_ = center + view_height_ / 2.; + break; + case Key_PageDown: + view_width_ = (GLdouble)view_width_ * (speed * ZOOM); + center = ( view_left_ + view_right_ ) / 2.; + view_left_ = center - view_width_ / 2.; + view_right_ = center + view_width_ / 2.; + + view_height_ = (GLdouble)view_height_ * (speed * ZOOM); + center = ( view_bottom_ + view_top_ ) / 2.; + view_bottom_ = center - view_height_ / 2.; + view_top_ = center + view_height_ / 2.; + break; + case Key_Up: + view_bottom_ += (speed * PAN) * view_height_; + view_top_ = view_bottom_ + view_height_; + break; + case Key_Down: + view_bottom_ -= (speed * PAN) * view_height_; + view_top_ = view_bottom_ + view_height_; + break; + case Key_Left: + view_left_ -= (speed * PAN) * view_width_; + view_right_ = view_left_ + view_width_; + break; + case Key_Right: + view_left_ += (speed * PAN) * view_width_; + view_right_ = view_left_ + view_width_; + break; + case Key_Q: + case Key_Escape: + qApp->exit( 0 ); + case Key_R: + view_width_ = window_width_; + view_height_ = window_height_; + + view_left_ = -view_width_ / 2.; + view_right_ = view_width_ / 2.; + view_bottom_ = -view_height_ / 2.; + view_top_ = view_height_ / 2.; + + rot_x_ = rot_y_ = rot_z_ = 0; + + vignettes.current()->reset(); + break; + case Key_X: + rot_x_ += speed * ROTATOR; + break; + case Key_Y: + rot_y_ += speed * ROTATOR; + break; + case Key_Z: + rot_z_ += speed * ROTATOR; + break; + default: + redraw = vignettes.current()->input( e ); + } + if ( redraw ) { + resetView(); + updateGL(); + } +} + +void CharacterView::resetView ( void ) +{ + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); +#if 1 + glOrtho( view_left_, view_right_, view_bottom_, view_top_, -250., 250. ); +#else + glFrustum( view_left_, view_right_, view_bottom_, view_top_, + 10., 100. ); +#endif + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + + glRotatef( rot_x_, 1., 0., 0. ); + glRotatef( rot_y_, 0., 1., 0. ); + glRotatef( rot_z_, 0., 0., 1. ); + + glTranslatef( -window_width_ / 2., -window_height_ / 2., 0. ); + glTranslatef( 0., 0., -10. ); + + vignettes.current()->view( view_left_, view_right_, view_bottom_, view_top_ ); +} + +int main ( int argc, char* argv[] ) +{ + + QApplication app( argc, argv ); + + if ( argc != 3 ) { + std::cerr << "usage: " << argv[0] << " string fontfile" << std::endl; + return 1; + } + + CharacterView cv ( argv[1], argv[2] ); + cv.resize( 500, 500 ); + + app.setMainWidget( &cv ); + cv.show(); + + return app.exec(); +} diff --git a/engine/libraries/oglft/tests/speedtest.h b/engine/libraries/oglft/tests/speedtest.h new file mode 100644 index 0000000..4463cbc --- /dev/null +++ b/engine/libraries/oglft/tests/speedtest.h @@ -0,0 +1,89 @@ +/* -*- c++ -*- + * speedtest.h: Header for Performance test for the OGLFT library + * Copyright (C) 2002 lignum Computing, Inc. + * $Id$ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef SPEEDTEST_H +#define SPEEDTEST_H + +#include + +#include +#include +#include + +// Little animation vignettes. The function calls essentially follow +// those of the Qt OpenGL widget and are called at the corresponding times. + +struct Vignette { + virtual ~Vignette ( void ) {} + virtual unsigned int frame_count ( void ) = 0; + virtual void init ( void ) = 0; + virtual void view ( GLdouble /*left*/, GLdouble /*right*/, + GLdouble /*bottom*/, GLdouble /*top*/ ) + {} + virtual void reset ( void ) + {} + virtual bool input ( QKeyEvent* e ) + { + if ( e->key() == Qt::Key_Escape ) exit( 0 ); + return false; + } + virtual void draw ( int frame_number ) = 0; + virtual void finish ( void ) = 0; +}; + +// Yet another OpenGL view widget. + +class CharacterView : public QGLWidget { +Q_OBJECT + GLsizei window_width_, window_height_; + GLdouble view_width_, view_height_; + GLdouble view_left_, view_right_, view_bottom_, view_top_; + GLdouble rot_x_, rot_y_, rot_z_; + + QTimer redraw_timer_; + QTimer performance_timer_; + + unsigned int frame_counter_; + unsigned int counter_snapshot_; + unsigned int animation_frame_count_; + unsigned int iteration_counter_; + unsigned int maximum_iterations_; + + static const unsigned int PERFORMANCE_SAMPLE_RATE_HZ = 1; + static const unsigned int MAXIMUM_ITERATIONS = 4; + + QQueue vignettes; + +protected slots: + void redraw ( void ); + void measure_performance ( void ); + +public: + CharacterView ( const char* text, const char* fontfile, + QWidget* parent = 0, const char* name = 0 ); +protected: + + void initializeGL ( void ); + void resizeGL ( int w, int h ); + void paintGL ( void ); + void keyPressEvent ( QKeyEvent* e ); + void resetView ( void ); +}; + +#endif /* SPEEDTEST_H */ diff --git a/engine/libraries/oglft/tests/texture.png b/engine/libraries/oglft/tests/texture.png new file mode 100644 index 0000000..3d38bb6 Binary files /dev/null and b/engine/libraries/oglft/tests/texture.png differ diff --git a/engine/libraries/oglft/tests/tosrc.pl b/engine/libraries/oglft/tests/tosrc.pl new file mode 100644 index 0000000..7f1f916 --- /dev/null +++ b/engine/libraries/oglft/tests/tosrc.pl @@ -0,0 +1,30 @@ +#!/usr/bin/perl + +use strict; + +# Convert the file given on the commandline into something which +# can be compiled with C and linked into an executable. +# Use the generated object module with the following declarations: +# extern unsigned char ${ARGV[0]}[]; /* Array of data */ +# extern int ${ARGV[0]}_size; /* Size of array */ +# Note, .'s in the argument filename are converted to _'s + +open FILE, $ARGV[0] or die "usage: $0 file"; + +# "Slurp the whole file" +undef $/; +my $contents = ; + +# Replace .'s in the filename with _'s (since . is an operator in C) +my $label = $ARGV[0]; +$label =~ s/\./_/; + +print "FT_Byte $label\[\] = {\n"; + +for ( my $i = 0; $i < length $contents; $i += 16 ) { + print join( ",", unpack( "C*", substr( $contents, $i, 16 ) ) ), ",\n"; +} + +print "};\n"; + +print "const int ${label}_size = sizeof( $label );\n"; diff --git a/engine/libraries/oglft/tests/tutorial1.cpp b/engine/libraries/oglft/tests/tutorial1.cpp new file mode 100644 index 0000000..e3c401f --- /dev/null +++ b/engine/libraries/oglft/tests/tutorial1.cpp @@ -0,0 +1,98 @@ +/* + * tutorial1.cpp: Tutorial for the OGLFT library + * Copyright (C) 2002 lignum Computing, Inc. + * $Id$ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include + +#include + +#include // Note: this will depend on where you've installed OGLFT + +// Declare a Face variable of the desired style +OGLFT::Monochrome* monochrome; + +void init ( const char* filename ) +{ + // Create a new face given the font filename and a size + + monochrome = new OGLFT::Monochrome( filename, 36 ); + + // Always check to make sure the face was properly constructed + + if ( monochrome == 0 || !monochrome->isValid() ) { + std::cerr << "Could not construct face from " << filename << std::endl; + return; + } + + // Set the face color to red + + monochrome->setForegroundColor( 1., 0., 0. ); + + // For the raster styles, it is essential that the pixel store + // unpacking alignment be set to 1 + + glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + + // Set the window's background color + + glClearColor( .75, .75, .75, 1. ); +} + +static void display ( void ) +{ + // First clear the window ... + glClear( GL_COLOR_BUFFER_BIT ); + // ... then draw the string + monochrome->draw( 0., 250., "Hello, World!" ); +} + +static void reshape ( int width, int height ) +{ + glViewport( 0, 0, width, height ); + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glOrtho( 0, width, 0, height, -1, 1 ); + + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); +} + +int main ( int argc, char* argv[] ) +{ + // Check to be sure the user specified something as a font file name + + if ( argc != 2 ) { + std::cerr << "usage: " << argv[0] << " fontfile" << std::endl; + return 1; + } + + // Standard GLUT setup commands + + glutInit( &argc, argv ); + glutInitWindowSize( 500, 500 ); + glutInitDisplayMode( GLUT_RGB ); // Note: OGLFT really only works in RGB mode + glutCreateWindow( argv[0] ); + + init( argv[1] ); + + glutReshapeFunc( reshape ); + glutDisplayFunc( display ); + glutMainLoop(); + + return 0; +} diff --git a/engine/libraries/oglft/tests/tutorial2.cpp b/engine/libraries/oglft/tests/tutorial2.cpp new file mode 100644 index 0000000..c95a637 --- /dev/null +++ b/engine/libraries/oglft/tests/tutorial2.cpp @@ -0,0 +1,131 @@ +/* + * tutorial2.cpp: Tutorial for the OGLFT library + * Copyright (C) 2002 lignum Computing, Inc. + * $Id$ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include + +#include + +#include // Note: this will depend on where you've installed OGLFT + +#define USE_BITMAP_FACE + +// Declare a Face variable of the desired style +#if defined( USE_BITMAP_FACE ) +OGLFT::Monochrome* face; +#else +OGLFT::Filled* face; +#endif + +void init ( const char* filename ) +{ + // Create a new face given the font filename and a size + +#if defined( USE_BITMAP_FACE ) + face = new OGLFT::Monochrome( filename, 36 ); +#else + face = new OGLFT::Filled( filename, 36 ); +#endif + + // Always check to make sure the face was properly constructed + + if ( face == 0 || !face->isValid() ) { + std::cerr << "Could not construct face from " << filename << std::endl; + return; + } + + // Set the face color to red + + face->setForegroundColor( 1., 0., 0. ); + + // Use centered justification + + face->setHorizontalJustification( OGLFT::Face::CENTER ); + + // For the raster styles, it is essential that the pixel store + // unpacking alignment be set to 1 + + glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + + // Set the window's background color + + glClearColor( .75, .75, .75, 1. ); +} + +static void display ( void ) +{ + // First clear the window ... + glClear( GL_COLOR_BUFFER_BIT ); + // ... then draw the string + face->draw( 250., 250., "Hello, World!" ); + + glutSwapBuffers(); +} + +static void reshape ( int width, int height ) +{ + glViewport( 0, 0, width, height ); + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glOrtho( 0, width, 0, height, -1, 1 ); + + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); +} + +static void idle ( void ) +{ + // Retrieve the current value of the string's rotation and increment + // it by 4 degrees + + face->setStringRotation( face->stringRotation() + 4 ); +#if !defined(WIN32) + // Too fast even without acceleration + struct timespec request = { 0, 40000000 }; + nanosleep( &request, 0 ); +#else + Sleep( 40 ); +#endif + glutPostRedisplay(); +} + +int main ( int argc, char* argv[] ) +{ + // Check to be sure the user specified something as a font file name + + if ( argc != 2 ) { + std::cerr << "usage: " << argv[0] << " fontfile" << std::endl; + return 1; + } + + // Standard GLUT setup commands + + glutInit( &argc, argv ); + glutInitWindowSize( 500, 500 ); + glutInitDisplayMode( GLUT_RGB | GLUT_DOUBLE ); // Note: OGLFT really only works in RGB mode + glutCreateWindow( argv[0] ); + + init( argv[1] ); + + glutReshapeFunc( reshape ); + glutDisplayFunc( display ); + glutIdleFunc( idle ); + glutMainLoop(); + + return 0; +} diff --git a/engine/libraries/oglft/tests/tutorial3.cpp b/engine/libraries/oglft/tests/tutorial3.cpp new file mode 100644 index 0000000..9025564 --- /dev/null +++ b/engine/libraries/oglft/tests/tutorial3.cpp @@ -0,0 +1,282 @@ +/* + * tutorial3.cpp: Tutorial for the OGLFT library + * Copyright (C) 2002 lignum Computing, Inc. + * $Id$ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#if defined(_MSC_VER) +#define _USE_MATH_DEFINES +#endif +#include +#include +#include // The STL vector +#include // The STL algorithms +#include + +#include // Note: this will depend on where you've installed OGLFT + +// A Face variable of the desired style +#ifndef OGLFT_NO_SOLID +OGLFT::Solid* solid; +#else // If Solid is not defined, use Filled instead +OGLFT::Filled* solid; +#endif + +// The Bounding Box for the string +OGLFT::BBox bbox; + +// A vector of OpenGL display list names +OGLFT::DisplayLists dlists; + +// A vector of displacements defining the ocean +struct vertex { + float y; + float nx, ny; +}; +std::vector< vertex > ocean_vertices; + +void init ( const char* filename ) +{ + // Create a new face given the font filename and a size + +#ifndef OGLFT_NO_SOLID + solid = new OGLFT::Solid( filename, 36 ); +#else + solid = new OGLFT::Filled( filename, 36 ); +#endif + + // Always check to make sure the face was properly constructed + + if ( solid == 0 || !solid->isValid() ) { + std::cerr << "Could not construct face from " << filename << std::endl; + return; + } + + const float AMPLITUDE = 25.; + GLuint dlist = glGenLists( 2*13 ); + + // The per character display lists are executed before the glyph is + // rendered; so, the first display list must contain an absolute + // transformation, but the subsequent ones must contain relative + // transformations. However, we need complete sets of both transformations, + // starting with a place holder for the first (absolute) transformation + + dlists.push_back( 0 ); + + // Next, generate a sequence of relative displacements + + for ( int i=0; i<13; i++ ) { + float dy = AMPLITUDE * ( sinf( (i+1) * 2.f * (float)M_PI / 13.f ) - + sinf( i * 2.f * (float)M_PI / 13.f ) ); + + glNewList( dlist, GL_COMPILE ); + glTranslatef( 0., dy, 0. ); + glEndList(); + + dlists.push_back( dlist ); + + dlist++; + } + + // Next, generate a sequence of absolute displacements + + for ( int i=0; i<13; i++ ) { + float y = AMPLITUDE * sinf( i * 2.f * (float)M_PI / 13.f ); + + glNewList( dlist, GL_COMPILE ); + glTranslatef( 0., y, 0. ); + glEndList(); + + dlists.push_back( dlist ); + + dlist++; + } + + // Finally, copy the first absolute displacement into the first element + // of the display list vector + + dlists[0] = dlists[13+1]; + + // Use centered justification + + solid->setHorizontalJustification( OGLFT::Face::CENTER ); +#ifndef OGLFT_NO_SOLID + // Make the glyphs rather thick + + solid->setDepth( 10. ); +#endif + // Apply the per character display lists + + solid->setCharacterDisplayLists( dlists ); + + // Get the size of the string before it is transformed + + bbox = solid->measure( "Hello, World!" ); + + // Make it (sea) green + + solid->setForegroundColor( 143.f/255.f, 188.f/255.f, 143.f/255.f ); + + // Set the window's background color + + glClearColor( .5, .5, .5, 1. ); + + // Build an "ocean" for the characters to float upon (a higher resolution + // version of the absolute displacements which we'll cycle through) + + for ( int i = 0; i <= 52; i++ ) { + float s = sinf( i * 2.f * (float)M_PI / 52.f ); + float c = cosf( i * 2.f * (float)M_PI / 52.f ); + vertex v; + v.y = AMPLITUDE * s; + v.nx = c; + v.ny = s; + ocean_vertices.push_back( v ); + } + + // Enable lighting and the depth test + + glEnable( GL_LIGHTING ); + glEnable( GL_LIGHT0 ); + glEnable( GL_COLOR_MATERIAL ); + glEnable( GL_DEPTH_TEST ); +} + +static int offset = 0; + +static void display ( void ) +{ + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + glPushMatrix(); + + solid->draw( 250., 250., "Hello, World!" ); + + glTranslatef( 254.f - ( bbox.x_max_ + bbox.x_min_ ) / 2.f, 255.f, 0.f ); + + glBegin( GL_QUAD_STRIP ); + + glColor3f( 0., 0., 1. ); + + for ( int i=0; i<=52; i++ ) { + float x = i * ( bbox.x_max_ - bbox.x_min_ ) / 52; + + glNormal3f( ocean_vertices[(i+offset)%53].nx, + ocean_vertices[(i+offset)%53].ny, + 0 ); + + glVertex3f( x, ocean_vertices[(i+offset)%53].y, -100. ); + glVertex3f( x, ocean_vertices[(i+offset)%53].y, 100. ); + } + + offset = offset < 48 ? offset+4 : 0; + + glEnd(); + + glPopMatrix(); + glutSwapBuffers(); +} + +static void reshape ( int width, int height ) +{ + glViewport( 0, 0, width, height ); + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glOrtho( 0, width, 0, height, -200, 200 ); + + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + + // Rotate the model slightly out of the XY plane so it looks 3D + // (note we rotate the model instead of the view so that the lighting + // is fixed relative to the view instead of the model) + + glTranslatef( width/2.f, height/2.f, 0.f ); + glRotatef( 25.f, 1.f, 0.f, 0.f ); + glRotatef( 25.f, 0.f, 1.f, 0.f ); + glTranslatef( -width/2.f, -height/2.f, 0. ); +} + +static void idle ( void ) +{ + // Use the STL rotate algorithm to animate the transformation display lists. + // First, rotate the lists containing the relative displacements + + OGLFT::DLI first = solid->characterDisplayLists().begin()+1; + OGLFT::DLI next = first + 1; + OGLFT::DLI last = first + 13; + + rotate( first, next, last ); + + // Next, rotate the the lists containing the absolute displacements + + first = solid->characterDisplayLists().begin() + 13 + 1; + next = first + 1; + last = first + 13; + + rotate( first, next, last ); + + // Finally, copy the current absolute displacement into the leading element + + solid->characterDisplayLists()[0] = solid->characterDisplayLists()[13+1]; + + glutPostRedisplay(); +#if !defined(WIN32) + // Too fast even without acceleration + struct timespec request = { 0, 80000000 }; + nanosleep( &request, 0 ); +#else + Sleep( 800 ); +#endif +} + +static void key ( unsigned char c, int /*x*/, int /*y*/ ) +{ + switch ( c ) { + case 'q': + case 27: + exit( 0 ); + } +} + +int main ( int argc, char* argv[] ) +{ + // Check to be sure the user specified something as a font file name + + if ( argc != 2 ) { + std::cerr << "usage: " << argv[0] << " fontfile" << std::endl; + return 1; + } + + // Standard GLUT setup commands + + glutInit( &argc, argv ); + glutInitWindowSize( 500, 500 ); + glutInitDisplayMode( GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE ); + glutCreateWindow( argv[0] ); + + init( argv[1] ); + + glutReshapeFunc( reshape ); + glutDisplayFunc( display ); + glutKeyboardFunc( key ); + + glutIdleFunc( idle ); + + glutMainLoop(); + + return 0; +} diff --git a/engine/libraries/oglft/tests/vignette.h b/engine/libraries/oglft/tests/vignette.h new file mode 100644 index 0000000..3809d80 --- /dev/null +++ b/engine/libraries/oglft/tests/vignette.h @@ -0,0 +1,99 @@ +/* -*- c++ -*- + * speedtest.h: Header for Performance test for the OGLFT library + * Copyright (C) 2002 lignum Computing, Inc. + * $Id$ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef VIGNETTE_H +#define VIGNETTE_H + +#include + +#if OGLFT_QT_VERSION == 3 +#include +#include +#include +#elif OGLFT_QT_VERSION == 4 +#include +#include +#include +#include +#endif + +// Little animation vignettes. The function calls essentially follow +// those of the Qt OpenGL widget and are called at the corresponding times. + +struct Vignette { + virtual ~Vignette ( void ) {} + virtual unsigned int frame_count ( void ) = 0; + virtual unsigned int frame_rate ( void ) = 0; + virtual void init ( void ) = 0; + virtual void view ( GLdouble /*left*/, GLdouble /*right*/, + GLdouble /*bottom*/, GLdouble /*top*/ ) + {} + virtual void reset ( void ) + {} + virtual bool input ( QKeyEvent* e ) + { + if ( e->key() == Qt::Key_Escape ) exit( 0 ); + return false; + } + virtual void draw ( unsigned int frame_number ) = 0; + virtual void finish ( void ) = 0; +}; + +// Yet another OpenGL view widget. + +class CharacterView : public QGLWidget { +Q_OBJECT + GLsizei window_width_, window_height_; + GLdouble view_width_, view_height_; + GLdouble view_left_, view_right_, view_bottom_, view_top_; + + bool flank_speed_; + QTimer redraw_timer_; + QTimer performance_timer_; + + unsigned int frame_counter_; + unsigned int counter_snapshot_; + unsigned int animation_frame_count_; + unsigned int animation_frame_rate_; + unsigned int animation_frame_counter_; + + static const unsigned int PERFORMANCE_SAMPLE_RATE_HZ = 1; +#if OGLFT_QT_VERSION == 3 + QQueue vignettes; +#elif OGLFT_QT_VERSION == 4 + QQueue vignettes; +#endif + +protected slots: + void redraw ( void ); + void measure_performance ( void ); + +public: + CharacterView ( bool flank_speed, const char* fontfile, + QWidget* parent = 0, const char* name = 0 ); +protected: + + void initializeGL ( void ); + void resizeGL ( int w, int h ); + void paintGL ( void ); + void keyPressEvent ( QKeyEvent* e ); + void resetView ( void ); +}; + +#endif /* VIGNETTE_H */ diff --git a/engine/tests/CMake/FindUnitTest++.cmake b/engine/tests/CMake/FindUnitTest++.cmake new file mode 100644 index 0000000..261e7a8 --- /dev/null +++ b/engine/tests/CMake/FindUnitTest++.cmake @@ -0,0 +1,28 @@ +# - Try to find UnitTest++ +# +# + +SET (UNITTEST++_FOUND FALSE) + +FIND_PATH (UNITTEST++_INCLUDE_DIR UnitTest++.h /usr/include/unittest++ /usr/local/include/unittest++ $ENV{UNITTESTXX_PATH}/src $ENV{UNITTESTXX_INCLUDE_PATH}) + +FIND_LIBRARY (UNITTEST++_LIBRARY NAMES UnitTest++ PATHS /usr/lib /usr/local/lib $ENV{UNITTESTXX_PATH} ENV{UNITTESTXX_LIBRARY_PATH}) + +IF (UNITTEST++_INCLUDE_DIR AND UNITTEST++_LIBRARY) + SET (UNITTEST++_FOUND TRUE) +ENDIF (UNITTEST++_INCLUDE_DIR AND UNITTEST++_LIBRARY) + +IF (UNITTEST++_FOUND) + IF (NOT UnitTest++_FIND_QUIETLY) + MESSAGE(STATUS "Found UnitTest++: ${UNITTEST++_LIBRARY}") + ENDIF (NOT UnitTest++_FIND_QUIETLY) +ELSE (UNITTEST++_FOUND) + IF (UnitTest++_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find UnitTest++") + ENDIF (UnitTest++_FIND_REQUIRED) +ENDIF (UNITTEST++_FOUND) + +MARK_AS_ADVANCED ( + UNITTEST++_INCLUDE_DIR + UNITTEST++_LIBRARY + ) diff --git a/engine/tests/CMakeLists.txt b/engine/tests/CMakeLists.txt new file mode 100644 index 0000000..54f96ba --- /dev/null +++ b/engine/tests/CMakeLists.txt @@ -0,0 +1,51 @@ +PROJECT (ENGINETESTS) + +CMAKE_MINIMUM_REQUIRED (VERSION 2.6) + +# Needed for UnitTest++ +LIST( APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMake ) + +SET ( TESTS_SRCS + main.cc + CommandsTests.cc + EntityTests.cc + EventsBaseTests.cc + PhysicsTests.cc + ControllerBaseTests.cc + ) + +FIND_PACKAGE (UnitTest++) + +INCLUDE_DIRECTORIES ( ../ ) + +SET_TARGET_PROPERTIES ( ${PROJECT_EXECUTABLES} PROPERTIES + LINKER_LANGUAGE CXX +) + +IF ( UNITTEST++_FOUND ) + ADD_EXECUTABLE ( enginetests ${TESTS_SRCS} ) + + INCLUDE_DIRECTORIES ( ${UNITTEST++_INCLUDE_DIR} ) + + SET_TARGET_PROPERTIES ( enginetests PROPERTIES + LINKER_LANGUAGE CXX + OUTPUT_NAME runtests + ) + + TARGET_LINK_LIBRARIES ( enginetests + ${UNITTEST++_LIBRARY} + Engine + ) + + OPTION (RUN_AUTOMATIC_TESTS "Perform automatic tests after compilation?" OFF) + + IF (RUN_AUTOMATIC_TESTS) + ADD_CUSTOM_COMMAND (TARGET runtests + POST_BUILD + COMMAND ./runtests + COMMENT "Running automated tests..." + ) + ENDIF (RUN_AUTOMATIC_TESTS) + +ENDIF ( UNITTEST++_FOUND ) + diff --git a/engine/tests/CommandsTests.cc b/engine/tests/CommandsTests.cc new file mode 100644 index 0000000..dfdadf5 --- /dev/null +++ b/engine/tests/CommandsTests.cc @@ -0,0 +1,138 @@ +#include + +#include "Logging.h" +#include "Commands.h" +#include "CommandsGlobal.h" + +using namespace std; +using namespace Engine; + +int global_int = -1; +string global_string = ""; +vector global_values; + +struct CommandsFixture { + CommandsFixture () { + LoggingModule.Init (0, NULL); + LoggingModule.SetLogPrintLevel (LogLevelWarning); + CommandsModule.Init (0, NULL); + } + + ~CommandsFixture () { + CommandsModule.Destroy (); + LoggingModule.Destroy (); + } + + Logging LoggingModule; + Commands CommandsModule; +}; + +bool test_cmd_set_global_int (vector argv) { + global_int = 1; + return true; +}; + +bool test_cmd_set_global_string (vector argv) { + if (argv.size() > 0) { + global_string = argv[0]; + return true; + } + + return false; +} + +bool test_cmd_set_global_values (vector argv) { + global_values = argv; + return true; +}; + +bool test_cmd_set_error (vector argv) { + if (argv.size() > 0) { + CommandSetErrorString ("too many arguments passed to function!"); + return false; + } + + return true; +} + +TEST_FIXTURE ( CommandsFixture, CommandSystemAddCommand ) { + AddCommand ("test", test_cmd_set_global_int); + CHECK_EQUAL (true, RunCommand ("test")); +} + +TEST_FIXTURE ( CommandsFixture, CommandSystemRun ) { + global_int = -1; + + AddCommand ("test", test_cmd_set_global_int); + CHECK_EQUAL (true, RunCommand ("test")); + + CHECK_EQUAL (1, global_int); +} + +TEST_FIXTURE ( CommandsFixture, CommandSystemArgv ) { + global_string = "oldstring"; + AddCommand ("test_string", test_cmd_set_global_string); + CHECK_EQUAL (1, RunCommand ("test_string newstring")); + + CHECK_EQUAL ("newstring", global_string); +} + +TEST_FIXTURE ( CommandsFixture, CommandSystemArgValues ) { + global_values.clear(); + AddCommand ("test_values", test_cmd_set_global_values); + CHECK_EQUAL (true, RunCommand ("test_values value1 value2 value3 ")); + + CHECK_EQUAL (static_cast (3), global_values.size()); + if (global_values.size() == 3) { + CHECK_EQUAL ("value1", global_values[0]); + CHECK_EQUAL ("value2", global_values[1]); + CHECK_EQUAL ("value3", global_values[2]); + } + + global_values.clear (); + CHECK_EQUAL (true, RunCommand ("test_values")); + CHECK_EQUAL (static_cast (0), global_values.size()); + + global_values.clear (); + CHECK_EQUAL (true, RunCommand ("test_values value1 \"value consisting of a string\"")); + CHECK_EQUAL (static_cast (2), global_values.size()); + + if (global_values.size () == 2) { + CHECK_EQUAL ("value consisting of a string", global_values[1]); + } + + global_values.clear (); + CHECK_EQUAL (true, RunCommand ("test_values \"value consisting of a string\"")); + CHECK_EQUAL (static_cast (1), global_values.size()); + + if (global_values.size () == 1) { + CHECK_EQUAL ("value consisting of a string", global_values[0]); + } +} + +TEST_FIXTURE ( CommandsFixture, CommandSystemQueue ) { + global_string = "oldstring"; + global_int = -1; + + AddCommand ("test_string", test_cmd_set_global_string); + AddCommand ("test_int", test_cmd_set_global_int); + + QueueCommand ("test_string newstring"); + QueueCommand ("test_int"); + CommandQueueExecute (); + + CHECK_EQUAL ("newstring", global_string); + CHECK_EQUAL (1, global_int); +} + +TEST_FIXTURE ( CommandsFixture, CommandSystemError ) { + AddCommand ("set_error", test_cmd_set_error); + + CHECK_EQUAL (false, RunCommand ("set_error error")); + CHECK_EQUAL ("too many arguments passed to function!", CommandGetErrorString ()); + + CHECK_EQUAL (true, RunCommand ("set_error")); + CHECK_EQUAL ("", CommandGetErrorString ()); +} + + diff --git a/engine/tests/ControllerBaseTests.cc b/engine/tests/ControllerBaseTests.cc new file mode 100644 index 0000000..0db8764 --- /dev/null +++ b/engine/tests/ControllerBaseTests.cc @@ -0,0 +1,20 @@ +#include + +#include "ControllerBase.h" +#include "keytable.h" + +using namespace std; +using namespace Engine; + +TEST (test_convert_keystring) { + int key; + + key = convert_keystring ("up"); + CHECK_EQUAL (SDLK_UP, key); + key = convert_keystring ("escape"); + CHECK_EQUAL (SDLK_ESCAPE, key); + + key = convert_keystring ("blaaa"); + CHECK_EQUAL (0, key); +} + diff --git a/engine/tests/EntityTests.cc b/engine/tests/EntityTests.cc new file mode 100644 index 0000000..31e8c9a --- /dev/null +++ b/engine/tests/EntityTests.cc @@ -0,0 +1,90 @@ +#include + +#include "Logging.h" +#include "EntityBase.h" + +using namespace std; +using namespace Engine; + +struct EntityFixture { + EntityFixture () { + LoggingModule.Init (0, NULL); + LoggingModule.SetLogPrintLevel (LogLevelWarning); + } + + ~EntityFixture () { + LoggingModule.Destroy (); + } + + Logging LoggingModule; +}; + +TEST_FIXTURE ( EntityFixture, EntityPhysicStateGlobalizeTranslation ) { + EntityPhysicState phys_entity; + + vector3d position (1, 0, -2); + vector3d orientation (0, 0.25 * M_PI, 0); + + phys_entity.SetPosition (position); + phys_entity.SetOrientation (orientation); + + vector3d origin (0, 0, 0); + phys_entity.Globalize (origin); + + CHECK_EQUAL (origin[0], 1); + CHECK_EQUAL (origin[1], 0); + CHECK_EQUAL (origin[2], -2); +} + +TEST_FIXTURE ( EntityFixture, EntityPhysicStateGlobalizeFull ) { + EntityPhysicState phys_entity; + + vector3d position (3, 0, 1); + vector3d orientation (0, 45, 0); + + phys_entity.SetPosition (position); + phys_entity.SetOrientation (orientation); + + vector3d front (5, 0, 0); + phys_entity.Globalize (front); + + CHECK_CLOSE (3 + cos (45 * M_PI / 180) * 5., front[0], 1.0e-5); + CHECK_CLOSE (0, front[1], 1.0e-5); + CHECK_CLOSE (1 - sin (45 * M_PI / 180) * 5., front[2], 1.0e-5); +} + +TEST_FIXTURE ( EntityFixture, EntityPhysicStateLocalizeTranslation ) { + EntityPhysicState phys_entity; + + vector3d position (1, 0, -2); + vector3d orientation (0, 45, 0); + + phys_entity.SetPosition (position); + phys_entity.SetOrientation (orientation); + + vector3d origin (1., 0, -2.); + phys_entity.Localize (origin); + + CHECK_CLOSE (0, origin[0], 1.0e-5); + CHECK_CLOSE (0, origin[1], 1.0e-5); + CHECK_CLOSE (0, origin[2], 1.0e-5); +} + +TEST_FIXTURE ( EntityFixture, EntityPhysicStateLocalizeFull ) { + EntityPhysicState phys_entity; + + vector3d position (1, 0, -1); + vector3d orientation (0, 45, 0); + + phys_entity.SetPosition (position); + phys_entity.SetOrientation (orientation); + + vector3d origin (0, 0, 0); + phys_entity.Localize (origin); + + CHECK_CLOSE (-sqrt (2), origin[0], 1.0e-5); + CHECK_CLOSE (0, origin[1], 1.0e-5); + CHECK_CLOSE (0, origin[2], 1.0e-5); +} + + diff --git a/engine/tests/EventsBaseTests.cc b/engine/tests/EventsBaseTests.cc new file mode 100644 index 0000000..8228668 --- /dev/null +++ b/engine/tests/EventsBaseTests.cc @@ -0,0 +1,130 @@ +#include + +#include "Logging.h" +#include "EventsBase.h" + +using namespace std; +using namespace Engine; + +int global_event_type = -1; +string global_event_string = ""; + +class TestEventListener : public EventListenerBase { + public: + TestEventListener() { + mName = "TestEventListener"; + } + + virtual bool HandleEvent (const EventBasePtr &event) const { + global_event_type = event->mEventType; + global_event_string = event->mEventData; + + return true; + } +}; + +struct EventsFixture { + EventsFixture () { + LoggingModule.Init (0, NULL); + LoggingModule.SetLogPrintLevel (LogLevelWarning); + + TestEventManager = new EventManager; + + global_event_type = -1; + global_event_string = ""; + } + + ~EventsFixture () { + delete TestEventManager; + TestEventManager = NULL; + + LoggingModule.Destroy (); + } + + Logging LoggingModule; + + TestEventListener Listener; + EventManager *TestEventManager; +}; + +TEST_FIXTURE ( EventsFixture, TestEventListenerHandleEvent ) { + EventBasePtr event (new EventBase); + + event->mEventType = 1; + event->mEventData = "test"; + + Listener.HandleEvent (event); + + CHECK_EQUAL (1, global_event_type); + CHECK_EQUAL ("test", global_event_string); +} + +TEST_FIXTURE ( EventsFixture, TestTestEventManagerZeroListeners ) { + CHECK_EQUAL (false, TestEventManager->HasEventTypeListener(123)); + CHECK_EQUAL (0, TestEventManager->GetEventTypeListenerCount(123)); +} + +TEST_FIXTURE ( EventsFixture, TestTestEventManagerAddListener ) { + TestEventManager->RegisterListener (&Listener, 1); + + CHECK_EQUAL (true, TestEventManager->HasEventTypeListener(1)); + CHECK_EQUAL (false, TestEventManager->HasEventTypeListener(2)); + + TestEventManager->RegisterListener (&Listener, 1); + CHECK_EQUAL (2, TestEventManager->GetEventTypeListenerCount(1)); +} + +TEST_FIXTURE ( EventsFixture, TestTestEventManagerTriggerEvent ) { + TestEventManager->RegisterListener (&Listener, 1); + + CHECK_EQUAL (true, TestEventManager->HasEventTypeListener(1)); + + EventBasePtr event (new EventBase); + + event->mEventType = 1; + event->mEventData = "test"; + + CHECK_EQUAL (true, TestEventManager->TriggerEvent (event)); + + CHECK_EQUAL (1, global_event_type); + CHECK_EQUAL ("test", global_event_string); + + event->mEventType = 0; + CHECK_EQUAL (false, TestEventManager->TriggerEvent (event)); +} + +TEST_FIXTURE ( EventsFixture, TestTestEventManagerQueueEvent ) { + TestEventManager->RegisterListener (&Listener, 1); + + EventBasePtr event (new EventBase); + + event->mEventType = 1; + event->mEventData = "test"; + + CHECK_EQUAL (true, TestEventManager->QueueEvent (event)); + CHECK_EQUAL (1, TestEventManager->GetQueuedEventCount()); + CHECK_EQUAL (true, TestEventManager->QueueEvent (event)); + CHECK_EQUAL (2, TestEventManager->GetQueuedEventCount()); + + event->mEventType = 2; + CHECK_EQUAL (false, TestEventManager->QueueEvent (event)); +} + +TEST_FIXTURE ( EventsFixture, TestTestEventManagerProcess ) { + TestEventManager->RegisterListener (&Listener, 1); + + EventBasePtr event (new EventBase); + + event->mEventType = 1; + event->mEventData = "test"; + + CHECK_EQUAL (true, TestEventManager->QueueEvent (event)); + CHECK_EQUAL (1, TestEventManager->GetQueuedEventCount()); + CHECK_EQUAL (true, TestEventManager->QueueEvent (event)); + CHECK_EQUAL (2, TestEventManager->GetQueuedEventCount()); + + TestEventManager->Process(); + CHECK_EQUAL (1, global_event_type); + CHECK_EQUAL ("test", global_event_string); + CHECK_EQUAL (0, TestEventManager->GetQueuedEventCount()); +} diff --git a/engine/tests/PhysicsTests.cc b/engine/tests/PhysicsTests.cc new file mode 100644 index 0000000..63c2ac2 --- /dev/null +++ b/engine/tests/PhysicsTests.cc @@ -0,0 +1,66 @@ +#include + +#include "Logging.h" +#include "PhysicsBase.h" +#include "ModelBase.h" +#include "EntityBase.h" + +using namespace std; +using namespace Engine; + +struct PhysicsFixture { + PhysicsFixture () { + LoggingModule.Init (0, NULL); + LoggingModule.SetLogPrintLevel (LogLevelMessage); + PhysicsModule.Init (0, NULL); + + dummy_entity = CreateEntityPhysicState (EntityBaseTypeNone, 0); + particle_entity = CreateEntityPhysicState (EntityBaseTypeParticle, 1); + block_entity = CreateEntityPhysicState (EntityBaseTypeBlock, 2); + actor_entity = CreateEntityPhysicState (EntityBaseTypeActor, 3); + + PhysicsModule.RegisterEntity (dummy_entity); + PhysicsModule.RegisterEntity (particle_entity); + PhysicsModule.RegisterEntity (block_entity); + PhysicsModule.RegisterEntity (actor_entity); + } + + ~PhysicsFixture () { + PhysicsModule.UnregisterEntity (0); + PhysicsModule.UnregisterEntity (1); + PhysicsModule.UnregisterEntity (2); + PhysicsModule.UnregisterEntity (3); + + PhysicsModule.Destroy (); + LoggingModule.Destroy (); + } + + Logging LoggingModule; + PhysicsBase PhysicsModule; + + EntityPhysicState* dummy_entity; + EntityPhysicState* particle_entity; + EntityPhysicState* block_entity; + EntityPhysicState* actor_entity; +}; + +TEST_FIXTURE ( PhysicsFixture, PhysicsModuleActorActorCollision ) { + int result = -1; + + EntityPhysicState* actor_2_entity = CreateEntityPhysicState (EntityBaseTypeActor, 4); + assert (actor_2_entity->mShape); + assert (actor_entity->mShape); + + PhysicsModule.RegisterEntity (actor_2_entity); + + actor_entity->mPosition = vector3d (0., 0., 0.); + actor_entity->mVelocity = vector3d (1., 0., 0.); + + actor_2_entity->mPosition = vector3d (1.5, 0., 0.1); + actor_2_entity->mVelocity = vector3d (-1., 0., 0.); + + result = PhysicsModule.Simulate (1000.); + CHECK_EQUAL (0, result); + + PhysicsModule.UnregisterEntity (actor_2_entity->mId); +} diff --git a/engine/tests/main.cc b/engine/tests/main.cc new file mode 100644 index 0000000..e9daeaa --- /dev/null +++ b/engine/tests/main.cc @@ -0,0 +1,8 @@ +#include + +using namespace std; + +int main (int argc, char *argv[]) +{ + return UnitTest::RunAllTests (); +}