commit 78c27f03c82d8b2f4e2d85bb8f4832498403b5fe Author: Anonymous Maarten Date: Thu Dec 3 01:37:02 2015 +0100 2006-12-10 diff --git a/Doxyfile b/Doxyfile new file mode 100644 index 0000000..9190d7e --- /dev/null +++ b/Doxyfile @@ -0,0 +1,1237 @@ +# Doxyfile 1.4.5 + +# 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 +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = OGTA + +# 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 = 0.0 + +# 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: +# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, +# Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese, +# Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian, +# Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, +# Swedish, and Ukrainian. + +OUTPUT_LANGUAGE = English + +# This tag can be used to specify the encoding used in the generated output. +# The encoding is not always determined by the language that is chosen, +# but also whether or not the output is meant for Windows or non-Windows users. +# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES +# forces the Windows encoding (this is the default for the Windows binary), +# whereas setting the tag to NO uses a Unix-style encoding (the default for +# all platforms other than Windows). + +USE_WINDOWS_ENCODING = NO + +# 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 = + +# 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 the Qt-style comments (thus requiring an +# explicit @brief command for a brief description. + +JAVADOC_AUTOBRIEF = YES + +# 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 DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. + +DETAILS_AT_TOP = 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 + +# 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 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 + +#--------------------------------------------------------------------------- +# 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 = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# 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 = 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_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 = 30 + +# 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 = NO + +# 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 YES. + +SHOW_DIRECTORIES = NO + +# 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 = + +#--------------------------------------------------------------------------- +# 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 = . util + +# 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 + +FILE_PATTERNS = *.cpp *.c *.h *.hpp + +# 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 = loki tools + +# 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 = lid_normal_data.h slope_data.h slope1_tcoords.h + +# 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 = NO + +# 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 (the default) +# 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 (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = 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 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 compressed 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 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 + +# 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 + +# If the GENERATE_TREEVIEW tag is set to YES, 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. + +GENERATE_TREEVIEW = NO + +# 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 + +#--------------------------------------------------------------------------- +# 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 = NO + +# 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 = NO + +# 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 = YES + +# 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 = NO + +# 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 tags 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 = NO + +# 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 MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_WIDTH = 1024 + +# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_HEIGHT = 1024 + +# 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 a graph may be further truncated if the graph's +# image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH +# and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default), +# the graph is not depth-constrained. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, which results in a white background. +# 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 = NO + +# 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 + +#--------------------------------------------------------------------------- +# Configuration::additions 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/blockdata.cpp b/blockdata.cpp new file mode 100644 index 0000000..f2a653d --- /dev/null +++ b/blockdata.cpp @@ -0,0 +1,12 @@ +#include "blockdata.h" +namespace OpenGTA { + float BlockData::slope_raw_data[numBlockTypes][numFaces][4][3] = { +#include "slope1_data.h" + }; + float BlockData::slope_tex_data[numBlockTypes][numFaces-1][4][2] = { +#include "slope1_tcoords.h" + }; + float BlockData::lid_normal_data[numBlockTypes][3] = { +#include "lid_normal_data.h" + }; +} diff --git a/blockdata.h b/blockdata.h new file mode 100644 index 0000000..27d0a28 --- /dev/null +++ b/blockdata.h @@ -0,0 +1,25 @@ +#ifndef OPENGTA_BLOCKDATA +#define OPENGTA_BLOCKDATA +#include +#include "Singleton.h" + +namespace OpenGTA { + struct BlockData { + static const size_t numBlockTypes = 45; + static const size_t numFaces = 5; + + static float slope_raw_data[numBlockTypes][numFaces][4][3]; + static float slope_tex_data[numBlockTypes][numFaces-1][4][2]; + static float lid_normal_data[numBlockTypes][3]; + }; + + typedef Loki::SingletonHolder BlockDataHolder; +} + +#define SLOPE_RAW_DATA BlockDataHolder::Instance().slope_raw_data +#define SLOPE_TEX_DATA BlockDataHolder::Instance().slope_tex_data +#define LID_NORMAL_DATA BlockDataHolder::Instance().lid_normal_data + + +#endif diff --git a/coldet/box.cpp b/coldet/box.cpp new file mode 100644 index 0000000..3306667 --- /dev/null +++ b/coldet/box.cpp @@ -0,0 +1,281 @@ +/* ColDet - C++ 3D Collision Detection Library + * Copyright (C) 2000 Amir Geva + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library 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. + * + * Any comments, questions and bug reports send to: + * photon@photoneffect.com + * + * Or visit the home page: http://photoneffect.com/coldet/ + */ +#include "sysdep.h" +#include "box.h" +#include "mytritri.h" + +__CD__BEGIN +//////////////////////////////////////////////////// +// code from here is used in detection process + +int BoxTreeInnerNode::getTrianglesNumber() +{ + return m_Boxes.size(); +} + +BoxedTriangle* BoxTreeInnerNode::getTriangle(int which) +{ + if (which<0 || which>=getTrianglesNumber()) return NULL; + return m_Boxes[which]; +} + +RotationState::RotationState(const Matrix3D& transform) +: t(transform) +{ + N[0]=Vector3D(t._11,t._12,t._13); + N[1]=Vector3D(t._21,t._22,t._23); + N[2]=Vector3D(t._31,t._32,t._33); +} + +inline float DotWithCol(const Vector3D& v, const Matrix3& m, int col) +{ + return v.x*m(0,col) + v.y*m(1,col) + v.z*m(2,col); +} + +bool Box::intersect(const Vector3D& O, float radius) +{ + Vector3D mx=m_Pos+m_Size; + float dist=0.0f; + for(int i=0;i<3;i++) + { + if (O[i] < m_Pos[i]) + { + float d=O[i]-m_Pos[i]; + dist+=d*d; + } + else + if (O[i] > mx[i]) + { + float d=O[i]-mx[i]; + dist+=d*d; + } + } + return (dist <= (radius*radius)); +} + +bool Box::intersect(const Vector3D& O, const Vector3D& D, + float segmax) +{ + if (segmax>3e30f) return intersect(O,D); // infinite ray + Vector3D abs_segdir, abs_diff, abs_cross; + + Vector3D segdir=0.5f*segmax*D; + Vector3D seg_center=O+segdir; + Vector3D diff=seg_center - getCenter(); + int i; + for(i=0;i<3;i++) + { + abs_segdir[i]=flabs(segdir[i]); + abs_diff[i]=flabs(diff[i]); + float f=getSize()[i] + abs_segdir[i]; + if (abs_diff[i] > f) return false; + } + Vector3D cross=CrossProduct(segdir,diff); + int idx[] = {0,1,2,0,1}; + for(i=0;i<3;i++) + { + int i1=idx[i+1]; + int i2=idx[i+2]; + abs_cross[i] = flabs(cross[i]); + float f = getSize()[i1]*abs_segdir[i2] + getSize()[i2]*abs_segdir[i1]; + if ( abs_cross[i] > f ) return false; + } + return true; +} + +bool Box::intersect(const Vector3D& O, const Vector3D& D) +{ + Vector3D abs_segdir, abs_cross; + float f; + Vector3D diff = O - getCenter(); + + for(int i=0;i<3;i++) + { + abs_segdir[i] = flabs(D[i]); + if ( flabs(diff[i])>m_Size[i] && diff[i]*D[i]>=0.0f ) + return false; + } + + Vector3D cross = CrossProduct(D,diff); + + abs_cross[0] = flabs(cross[0]); + f = m_Size[1]*abs_segdir[2] + m_Size[2]*abs_segdir[1]; + if ( abs_cross[0] > f ) + return false; + + abs_cross[1] = flabs(cross[1]); + f = m_Size[0]*abs_segdir[2] + m_Size[2]*abs_segdir[0]; + if ( abs_cross[1] > f ) + return false; + + abs_cross[2] = flabs(cross[2]); + f = m_Size[0]*abs_segdir[1] + m_Size[1]*abs_segdir[0]; + if ( abs_cross[2] > f ) + return false; + + return true; +} + +bool Box::intersect(const Box& b, RotationState& rs) +{ + const Vector3D bCenter=Transform(b.getCenter(),rs.t); + Vector3D EA=0.5f*getSize(); + Vector3D EB=0.5f*b.getSize(); + Vector3D distance=bCenter-getCenter(); + Matrix3 C,abs_C; + float R0,R1,R,R01; + int i; + + for(i=0;i<3;i++) + { + C(i,0)=rs.N[0][i]; + C(i,1)=rs.N[1][i]; + C(i,2)=rs.N[2][i]; + abs_C(i,0)=flabs(C(i,0)); + abs_C(i,1)=flabs(C(i,1)); + abs_C(i,2)=flabs(C(i,2)); + R=flabs(distance[i]); + R1=EB*abs_C.baseRow(i); + R01=EA[i]+R1; + if (R>R01) return false; + } + for(i=0;i<3;i++) + { + R=flabs(rs.N[i]*distance); + R0=DotWithCol(EA,abs_C,i); + R01=R0+EB[i]; + if (R>R01) return false; + } + + R=flabs(distance.z*C(1,0) - distance.y*C(2,0)); + R0=EA.y*abs_C(2,0) + EA.z*abs_C(1,0); + R1=EB.y*abs_C(0,2) + EB.z*abs_C(0,1); + R01=R0+R1; + if (R>R01) return false; + + R=flabs(distance.z*C(1,1) - distance.y*C(2,1)); + R0=EA.y*abs_C(2,1) + EA.z*abs_C(1,1); + R1=EB.x*abs_C(0,2) + EB.z*abs_C(0,0); + R01=R0+R1; + if (R>R01) return false; + + R=flabs(distance.z*C(1,2) - distance.y*C(2,2)); + R0=EA.y*abs_C(2,2) + EA.z*abs_C(1,2); + R1=EB.x*abs_C(0,1) + EB.y*abs_C(0,0); + R01=R0+R1; + if (R>R01) return false; + + R=flabs(distance.x*C(2,0) - distance.z*C(0,0)); + R0=EA.x*abs_C(2,0) + EA.z*abs_C(0,0); + R1=EB.y*abs_C(1,2) + EB.z*abs_C(1,1); + R01=R0+R1; + if (R>R01) return false; + + R=flabs(distance.x*C(2,1) - distance.z*C(0,1)); + R0=EA.x*abs_C(2,1) + EA.z*abs_C(0,1); + R1=EB.x*abs_C(1,2) + EB.z*abs_C(1,0); + R01=R0+R1; + if (R>R01) return false; + + R=flabs(distance.x*C(2,2) - distance.z*C(0,2)); + R0=EA.x*abs_C(2,2) + EA.z*abs_C(0,2); + R1=EB.x*abs_C(1,1) + EB.y*abs_C(1,0); + R01=R0+R1; + if (R>R01) return false; + + R=flabs(distance.y*C(0,0) - distance.x*C(1,0)); + R0=EA.x*abs_C(1,0) + EA.y*abs_C(0,0); + R1=EB.y*abs_C(2,2) + EB.z*abs_C(2,1); + R01=R0+R1; + if (R>R01) return false; + + R=flabs(distance.y*C(0,1) - distance.x*C(1,1)); + R0=EA.x*abs_C(1,1) + EA.y*abs_C(0,1); + R1=EB.x*abs_C(2,2) + EB.z*abs_C(2,0); + R01=R0+R1; + if (R>R01) return false; + + R=flabs(distance.y*C(0,2) - distance.x*C(1,2)); + R0=EA.x*abs_C(1,2) + EA.y*abs_C(0,2); + R1=EB.x*abs_C(2,1) + EB.y*abs_C(2,0); + R01=R0+R1; + if (R>R01) return false; + + return true; +} + +extern "C" { +int tri_tri_intersect(float V0[3],float V1[3],float V2[3], + float U0[3],float U1[3],float U2[3]); +}; + +Triangle::Triangle(const Vector3D& _1, const Vector3D& _2, const Vector3D& _3) +: v1(_1), v2(_2), v3(_3), center((1.0f/3.0f)*(_1+_2+_3)) +{} + +bool Triangle::intersect(const Vector3D& O, const Vector3D& D, Vector3D& cp, + float& tparm, float segmax) +{ + Plane p(v1,v2,v3); + float denom=p.normal*D; + if (IsZero(denom)) return false; + float t=-(p.d+p.normal*O)/denom; + if (t<=0.0f) return false; + if (t>segmax) return false; + TriangleDesc td(*this,p); + cp=O+t*D; + if (td.pointInTri(cp)) + { + tparm=t; + return true; + } + return false; +} + +bool Triangle::intersect(const Vector3D& O, float radius, Vector3D& cp) +{ + Plane p(v1,v2,v3); + float dist=p.Classify(O); + if (flabs(dist) > radius) return false; + Vector3D point=O-dist*p.normal; + TriangleDesc td(*this,p); + if (td.pointInTri(point)) + { + cp=point; + return true; + } + return false; +} + +bool Triangle::intersect(const Triangle& t) const +{ + return (tri_tri_intersect((float*)&v1.x, + (float*)&v2.x, + (float*)&v3.x, + (float*)&t.v1.x, + (float*)&t.v2.x, + (float*)&t.v3.x) != 0); +} + +__CD__END diff --git a/coldet/box.h b/coldet/box.h new file mode 100644 index 0000000..7c5f403 --- /dev/null +++ b/coldet/box.h @@ -0,0 +1,205 @@ +/* ColDet - C++ 3D Collision Detection Library + * Copyright (C) 2000 Amir Geva + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library 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. + * + * Any comments, questions and bug reports send to: + * photon@photoneffect.com + * + * Or visit the home page: http://photoneffect.com/coldet/ + */ +#ifndef H_BOX +#define H_BOX + +#include +#include "math3d.h" +#include "sysdep.h" + +__CD__BEGIN + +/** Stores rotation vectors used in the intersection tests, + to avoid recalculating them each time. */ +class RotationState +{ +public: + RotationState(const Matrix3D& transform); + Vector3D N[3]; + Matrix3D t; +}; + +/** AABB class, with support for testing against OBBs. */ +class Box +{ +public: + /** Default constructor */ + Box() {} + /** Construct from scalar corner position and size */ + Box(float x, float y, float z, float sx, float sy, float sz) + : m_Pos(x,y,z), m_Size(sx,sy,sz), + m_Center(x+0.5f*sx,y+0.5f*sy,z+0.5f*sz) {} + /** Construct from corner position and size */ + Box(const Vector3D& pos, const Vector3D& size) + : m_Pos(pos), m_Size(size), m_Center(pos+0.5f*size) {} + /** Copy constructor */ + Box(const Box& b) : m_Pos(b.m_Pos), m_Size(b.m_Size), m_Center(b.m_Center) {} + virtual ~Box() {} + /** Returns the box's position */ + const Vector3D& getPosition() const { return m_Pos; } + /** Returns the sizes of the box's edges */ + const Vector3D& getSize() const { return m_Size; } + /** Returns the center position of the box */ + const Vector3D& getCenter() const { return m_Center; } + /** Returns the volume of the box */ + float getVolume() const { return m_Size.x*m_Size.y*m_Size.z; } + /** Ray intersection */ + bool intersect(const Vector3D& O, const Vector3D& D); + /** Line segment intersection */ + bool intersect(const Vector3D& O, const Vector3D& D, float segmax); + /** Sphere intersection */ + bool intersect(const Vector3D& O, float radius); + /** Point in box */ + bool intersect(const Vector3D& p) const; + /** Aligned box intersection */ + bool intersect(const Box& b); + /** Oriented box intersection. */ + bool intersect(const Box& b, RotationState& rs); + + /** Position of box corner */ + Vector3D m_Pos; + /** Size of box box edges */ + Vector3D m_Size; + /** Position of box center. m_Pos+0.5f*m_Size; */ + Vector3D m_Center; +}; + +/** A single triangle in the model */ +class Triangle +{ +public: + /** Default constructor */ + Triangle() {} + /** Constructor to build a triangle from 3 points */ + Triangle(const Vector3D& _1, const Vector3D& _2, const Vector3D& _3); + /** Tests for intersection with another triangle. */ + bool intersect(const Triangle& t) const; + /** Tests for intersection with a ray (O origin, D direction) + Returns true if collision occured. + Outputs collision point in cp + Outputs the distance from the origin to the collision point in tparm + This distance is relative to the magnitude of D + Allows testing against a finite segment, by specifying + the maximum length of the ray in segmax + This length is also relative to the magnitude of D + */ + bool intersect(const Vector3D& O, const Vector3D& D, Vector3D& cp, + float& tparm, float segmax); + /** Test for intersection with a sphere (O origin) + Returns true if collision occured. + Outputs collision point in cp + */ + bool intersect(const Vector3D& O, float radius, Vector3D& cp); + + Vector3D v1,v2,v3; + Vector3D center; +}; + +class BoxedTriangle; + +/** Base class for hierarchy tree nodes. */ +class BoxTreeNode : public Box +{ +public: + /** Default constructor */ + BoxTreeNode() : Box() {} + /** Constructor for a box from position and size */ + BoxTreeNode(const Vector3D& pos, const Vector3D& size) + : Box(pos,size) {} + /** Returns true if the node is a leaf node. */ + virtual bool isLeaf() const = 0; + /** Returns the number of sons this node has */ + virtual int getSonsNumber() = 0; + /** Returns a son node, by index */ + virtual BoxTreeNode* getSon(int which) = 0; + /** Returns the number of triangles in this node. + Only non-zero for leaf nodes. */ + virtual int getTrianglesNumber() = 0; + /** Returns the boxed triangle contained in this node + by its index + */ + virtual BoxedTriangle* getTriangle(int which) = 0; +}; + +/** Inner node, containing other nodes. */ +class BoxTreeInnerNode : public BoxTreeNode +{ +public: + BoxTreeInnerNode(const Vector3D& pos, const Vector3D& size, int logdepth) + : BoxTreeNode(pos,size), m_First(NULL), m_Second(NULL), + m_logdepth(logdepth) {}; + virtual bool isLeaf() const { return false; } + /** Create the sons that will divide this box */ + int createSons(const Vector3D& center); + /** Recalculate the bounds of this box to fully contain + all of its triangles + */ + void recalcBounds(Vector3D& center); + /** Recursively divide this box */ + int divide(int p_depth); + + int getSonsNumber() + { + int n=0; + if (m_First!=NULL) n++; + if (m_Second!=NULL) n++; + return n; + } + + int getTrianglesNumber(); + BoxedTriangle* getTriangle(int which); + + BoxTreeNode* getSon(int which) + { + if (which==0) return m_First; + if (which==1) return m_Second; + return NULL; + } + + BoxTreeNode* m_First; + BoxTreeNode* m_Second; + int m_logdepth; + std::vector m_Boxes; +}; + +/** Leaf node, containing 1 triangle. */ +class BoxedTriangle : public BoxTreeNode, public Triangle +{ +public: + BoxedTriangle(const Vector3D& _1, const Vector3D& _2, const Vector3D& _3); + virtual bool isLeaf() const { return true; } + int getSonsNumber() { return 0; } + BoxTreeNode* getSon(int which) { return NULL; } + int getTrianglesNumber() { return 1; } + BoxedTriangle* getTriangle(int which) + { + if (which==0) return this; + return NULL; + } + +}; + +__CD__END + +#endif // H_BOX diff --git a/coldet/box_bld.cpp b/coldet/box_bld.cpp new file mode 100644 index 0000000..4274b00 --- /dev/null +++ b/coldet/box_bld.cpp @@ -0,0 +1,197 @@ +/* ColDet - C++ 3D Collision Detection Library + * Copyright (C) 2000 Amir Geva + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library 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. + * + * Any comments, questions and bug reports send to: + * photon@photoneffect.com + * + * Or visit the home page: http://photoneffect.com/coldet/ + */ +#include "sysdep.h" +#include "box.h" + +__CD__BEGIN + +// point in box test +bool Box::intersect(const Vector3D& p) const +{ + const Vector3D& pos=getPosition(); + const Vector3D& s=getSize(); + if (p.x(pos.x+s.x)) return false; + if (p.y(pos.y+s.y)) return false; + if (p.z(pos.z+s.z)) return false; + return true; +} + +// Non-oriented intersection test +bool Box::intersect(const Box& b) +{ + const Vector3D& t1=getPosition(); + Vector3D t2=getPosition()+getSize(); + const Vector3D& p1=b.getPosition(); + Vector3D p2=b.getPosition()+b.getSize(); + return (Max(p1.x,t1.x) <= Min(p2.x,t2.x) && + Max(p1.y,t1.y) <= Min(p2.y,t2.y) && + Max(p1.z,t1.z) <= Min(p2.z,t2.z)); +} + +BoxedTriangle::BoxedTriangle(const Vector3D& _1, + const Vector3D& _2, + const Vector3D& _3) + : BoxTreeNode(), Triangle(_1,_2,_3) +{ + m_Pos.x=Min(Min(_1.x,_2.x),_3.x); + m_Pos.y=Min(Min(_1.y,_2.y),_3.y); + m_Pos.z=Min(Min(_1.z,_2.z),_3.z); + Vector3D mx; + mx.x=Max(Max(_1.x,_2.x),_3.x); + mx.y=Max(Max(_1.y,_2.y),_3.y); + mx.z=Max(Max(_1.z,_2.z),_3.z); + m_Size=mx-getPosition(); + m_Center=getPosition()+0.5f*getSize(); +} + +int BoxTreeInnerNode::createSons(const Vector3D& center) +{ + int longest=0; + Vector3D p=getPosition(); + Vector3D s=getSize(); + if (1) + { + Vector3D dist(Vector3D::Zero); + for(unsigned i=0;icenter.x - center.x); + dist.y+=flabs(bt->center.y - center.y); + dist.z+=flabs(bt->center.z - center.z); + } + if (dist.y>dist.x && dist.y>dist.z) longest=1; + else + if (dist.z>dist.x && dist.z>dist.y) longest=2; + } + else + { + int dist[3]; + dist[0]=dist[1]=dist[2]=0; + for(unsigned i=0;icenter[j] > center[j]) dist[j]++; + else dist[j]--; + } + for(int j=0;j<3;j++) + dist[j]=abs(dist[j]); + if (dist[1]center; + mn.x=Min(Min(Min(bt->v1.x,bt->v2.x),bt->v3.x),mn.x); + mn.y=Min(Min(Min(bt->v1.y,bt->v2.y),bt->v3.y),mn.y); + mn.z=Min(Min(Min(bt->v1.z,bt->v2.z),bt->v3.z),mn.z); + mx.x=Max(Max(Max(bt->v1.x,bt->v2.x),bt->v3.x),mx.x); + mx.y=Max(Max(Max(bt->v1.y,bt->v2.y),bt->v3.y),mx.y); + mx.z=Max(Max(Max(bt->v1.z,bt->v2.z),bt->v3.z),mx.z); + } + center/=float(m_Boxes.size()); + m_Pos=mn; + m_Size=mx-mn; + if (m_Size.x==0.0f) { m_Size.x=0.002f; m_Pos.x-=0.001f; } + if (m_Size.y==0.0f) { m_Size.y=0.002f; m_Pos.y-=0.001f; } + if (m_Size.z==0.0f) { m_Size.z=0.002f; m_Pos.z-=0.001f; } + m_Center=getPosition()+0.5f*getSize(); +} + +int BoxTreeInnerNode::divide(int p_depth) +{ + if (m_Boxes.empty()) return 0; + Vector3D center; + recalcBounds(center); + int longest=createSons(center); + BoxTreeInnerNode* f=static_cast(m_First); + BoxTreeInnerNode* s=static_cast(m_Second); + int depth=1; + int bnum=m_Boxes.size(); +#ifdef _DEBUG + int fnum=0; +#endif + for(unsigned i=0;icenter[longest]m_Boxes.push_back(bt); + #ifdef _DEBUG + fnum++; + #endif + } + else + { + s->m_Boxes.push_back(bt); + } + } + + int b1num=f->m_Boxes.size(); + int b2num=s->m_Boxes.size(); + if ((b1num==bnum || b2num==bnum))// && p_depth>m_logdepth) + { + delete m_First; m_First=NULL; + delete m_Second; m_Second=NULL; + return depth+1; + } + + m_Boxes.clear(); + if (f->m_Boxes.empty()) { delete m_First; m_First=NULL; } + else + if (f->m_Boxes.size()==1) + { + BoxedTriangle* bt=f->m_Boxes.back(); + delete m_First; + m_First=bt; + } else depth=f->divide(p_depth+1); + if (s->m_Boxes.empty()) { delete m_Second; m_Second=NULL; } + else + if (s->m_Boxes.size()==1) + { + BoxedTriangle* bt=s->m_Boxes.back(); + delete m_Second; + m_Second=bt; + } else depth=Max(depth,s->divide(p_depth+1)); + return depth+1; +} + +__CD__BEGIN diff --git a/coldet/coldet.cpp b/coldet/coldet.cpp new file mode 100644 index 0000000..8dcf3de --- /dev/null +++ b/coldet/coldet.cpp @@ -0,0 +1,389 @@ +/* ColDet - C++ 3D Collision Detection Library + * Copyright (C) 2000 Amir Geva + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library 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. + * + * Any comments, questions and bug reports send to: + * photon@photoneffect.com + * + * Or visit the home page: http://photoneffect.com/coldet/ + */ +#include "sysdep.h" +#include "coldetimpl.h" +#include "mytritri.h" +#include + +__CD__BEGIN + +class Check +{ +public: + Check() {} + Check(BoxTreeNode* f, BoxTreeNode* s, int d) + : m_first(f), m_second(s), depth(d) {} + BoxTreeNode* m_first; + BoxTreeNode* m_second; + int depth; +}; + +bool CollisionModel3DImpl::collision(CollisionModel3D* other, + int AccuracyDepth, + int MaxProcessingTime, + float* other_transform) +{ + m_ColType=Models; + CollisionModel3DImpl* o=static_cast(other); + if (!m_Final) throw Inconsistency(); + if (!o->m_Final) throw Inconsistency(); + Matrix3D t=( other_transform==NULL ? o->m_Transform : *((Matrix3D*)other_transform) ); + if (m_Static) t *= m_InvTransform; + else t *= m_Transform.Inverse(); + RotationState rs(t); + + if (AccuracyDepth<0) AccuracyDepth=0xFFFFFF; + if (MaxProcessingTime==0) MaxProcessingTime=0xFFFFFF; + + DWORD EndTime,BeginTime = GetTickCount(); + int num=Max(m_Triangles.size(),o->m_Triangles.size()); + int Allocated=Max(64,(num>>4)); + std::vector checks(Allocated); + + int queue_idx=1; + Check& c=checks[0]; + c.m_first=&m_Root; + c.depth=0; + c.m_second=&o->m_Root; + while (queue_idx>0) + { + if (queue_idx>(Allocated/2)) // enlarge the queue. + { + Check c; + checks.insert(checks.end(),Allocated,c); + Allocated*=2; + } + EndTime=GetTickCount(); + if (EndTime >= (BeginTime+MaxProcessingTime)) throw TimeoutExpired(); + + // @@@ add depth check + //Check c=checks.back(); + Check& c=checks[--queue_idx]; + BoxTreeNode* first=c.m_first; + BoxTreeNode* second=c.m_second; + assert(first!=NULL); + assert(second!=NULL); + if (first->intersect(*second,rs)) + { + int tnum1=first->getTrianglesNumber(); + int tnum2=second->getTrianglesNumber(); + if (tnum1>0 && tnum2>0) + { + { + for(int i=0;igetTriangle(i); + Triangle tt(Transform(bt2->v1,rs.t),Transform(bt2->v2,rs.t),Transform(bt2->v3,rs.t)); + for(int j=0;jgetTriangle(j); + if (tt.intersect(*bt1)) + { + m_ColTri1=*bt1; + m_iColTri1=getTriangleIndex(bt1); + m_ColTri2=tt; + m_iColTri2=o->getTriangleIndex(bt2); + return true; + } + } + } + } + } + else + if (first->getSonsNumber()==0) + { + BoxTreeNode* s1=second->getSon(0); + BoxTreeNode* s2=second->getSon(1); + assert(s1!=NULL); + assert(s2!=NULL); + + Check& c1=checks[queue_idx++]; + c1.m_first=first; + c1.m_second=s1; + + Check& c2=checks[queue_idx++]; + c2.m_first=first; + c2.m_second=s2; + } + else + if (second->getSonsNumber()==0) + { + BoxTreeNode* f1=first->getSon(0); + BoxTreeNode* f2=first->getSon(1); + assert(f1!=NULL); + assert(f2!=NULL); + + Check& c1=checks[queue_idx++]; + c1.m_first=f1; + c1.m_second=second; + + Check& c2=checks[queue_idx++]; + c2.m_first=f2; + c2.m_second=second; + } + else + { + float v1=first->getVolume(); + float v2=second->getVolume(); + if (v1>v2) + { + BoxTreeNode* f1=first->getSon(0); + BoxTreeNode* f2=first->getSon(1); + assert(f1!=NULL); + assert(f2!=NULL); + + Check& c1=checks[queue_idx++]; + c1.m_first=f1; + c1.m_second=second; + + Check& c2=checks[queue_idx++]; + c2.m_first=f2; + c2.m_second=second; + } + else + { + BoxTreeNode* s1=second->getSon(0); + BoxTreeNode* s2=second->getSon(1); + assert(s1!=NULL); + assert(s2!=NULL); + + Check& c1=checks[queue_idx++]; + c1.m_first=first; + c1.m_second=s1; + + Check& c2=checks[queue_idx++]; + c2.m_first=first; + c2.m_second=s2; + } + } + } + } + return false; +} + +bool CollisionModel3DImpl::rayCollision(float origin[3], + float direction[3], + bool closest, + float segmin, + float segmax) +{ + float mintparm=9e9f,tparm; + Vector3D col_point; + m_ColType=Ray; + Vector3D O; + Vector3D D; + if (m_Static) + { + O=Transform(*(Vector3D*)origin,m_InvTransform); + D=rotateVector(*(Vector3D*)direction,m_InvTransform); + } + else + { + Matrix3D inv=m_Transform.Inverse(); + O=Transform(*(Vector3D*)origin,inv); + D=rotateVector(*(Vector3D*)direction,inv); + } + if (segmin!=0.0f) // normalize ray + { + O+=segmin*D; + segmax-=segmin; + segmin=0.0f; + } + if (segmax checks; + checks.push_back(&m_Root); + while (!checks.empty()) + { + BoxTreeNode* b=checks.back(); + checks.pop_back(); + if (b->intersect(O,D,segmax)) + { + int sons=b->getSonsNumber(); + if (sons) + while (sons--) checks.push_back(b->getSon(sons)); + else + { + int tri=b->getTrianglesNumber(); + while (tri--) + { + BoxedTriangle* bt=b->getTriangle(tri); + Triangle* t=static_cast(bt); + if (t->intersect(O,D,col_point,tparm,segmax)) + { + if (closest) + { + if (tparm checks; + checks.push_back(&m_Root); + while (!checks.empty()) + { + BoxTreeNode* b=checks.back(); + checks.pop_back(); + if (b->intersect(O,radius)) + { + int sons=b->getSonsNumber(); + if (sons) + while (sons--) checks.push_back(b->getSon(sons)); + else + { + int tri=b->getTrianglesNumber(); + while (tri--) + { + BoxedTriangle* bt=b->getTriangle(tri); + Triangle* t=static_cast(bt); + if (t->intersect(O,radius,m_ColPoint)) + { + m_ColTri1=*bt; + m_iColTri1=getTriangleIndex(bt); + return true; + } + } + } + } + } + return false; +} + +bool CollisionModel3DImpl::getCollidingTriangles(float t1[9], float t2[9], bool ModelSpace) +{ + if (ModelSpace) + { + if (t1!=NULL) + { + *((Vector3D*)&t1[0]) = m_ColTri1.v1; + *((Vector3D*)&t1[3]) = m_ColTri1.v2; + *((Vector3D*)&t1[6]) = m_ColTri1.v3; + } + if (t2!=NULL) + { + *((Vector3D*)&t2[0]) = m_ColTri2.v1; + *((Vector3D*)&t2[3]) = m_ColTri2.v2; + *((Vector3D*)&t2[6]) = m_ColTri2.v3; + } + } + else + { + if (t1!=NULL) + { + *((Vector3D*)&t1[0]) = Transform(m_ColTri1.v1,m_Transform); + *((Vector3D*)&t1[3]) = Transform(m_ColTri1.v2,m_Transform); + *((Vector3D*)&t1[6]) = Transform(m_ColTri1.v3,m_Transform); + } + if (t2!=NULL) + { + *((Vector3D*)&t2[0]) = Transform(m_ColTri2.v1,m_Transform); + *((Vector3D*)&t2[3]) = Transform(m_ColTri2.v2,m_Transform); + *((Vector3D*)&t2[6]) = Transform(m_ColTri2.v3,m_Transform); + } + } + return true; +} + +bool CollisionModel3DImpl::getCollidingTriangles(int& t1, int& t2) +{ + t1=m_iColTri1; + t2=m_iColTri2; + return true; +} + +bool CollisionModel3DImpl::getCollisionPoint(float p[3], bool ModelSpace) +{ + Vector3D& v=*((Vector3D*)p); + switch (m_ColType) + { + case Models: v=my_tri_tri_intersect(m_ColTri1,m_ColTri2); break; + case Sphere: + case Ray: v=m_ColPoint; break; + default: v=Vector3D::Zero; + } + if (!ModelSpace) v=Transform(v,m_Transform); + return true; +} + +bool SphereRayCollision(float center[3], float radius, + float origin[3], float direction[3], + float point[3]) +{ + Vector3D& C=*((Vector3D*)center); + Vector3D& O=*((Vector3D*)origin); + Vector3D D=((Vector3D*)direction)->Normalized(); + Vector3D& P=*((Vector3D*)point); + Vector3D EO=C-O; + float v=EO*D; + float disc=radius*radius - (EO*EO - v*v); + if (disc<0.0f) return false; + float d=sqrt(disc); + P=O+(v-d)*D; + return true; +} + +bool SphereSphereCollision(float c1[3], float r1, + float c2[3], float r2) +{ + Vector3D& C1=*((Vector3D*)c1); + Vector3D& C2=*((Vector3D*)c2); + float dist=(C2-C1).SquareMagnitude(); + float sum=r1+r2; + return (dist < sum*sum); +} + +__CD__END diff --git a/coldet/coldet.dsp b/coldet/coldet.dsp new file mode 100644 index 0000000..aaa54d9 --- /dev/null +++ b/coldet/coldet.dsp @@ -0,0 +1,157 @@ +# Microsoft Developer Studio Project File - Name="coldet" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=coldet - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "coldet.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "coldet.mak" CFG="coldet - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "coldet - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "coldet - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "coldet - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "COLDET_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "COLDET_EXPORTS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 + +!ELSEIF "$(CFG)" == "coldet - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "COLDET_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "COLDET_EXPORTS" /FR /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "coldet - Win32 Release" +# Name "coldet - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\box.cpp +# End Source File +# Begin Source File + +SOURCE=.\box_bld.cpp +# End Source File +# Begin Source File + +SOURCE=.\coldet.cpp +# End Source File +# Begin Source File + +SOURCE=.\coldet_bld.cpp +# End Source File +# Begin Source File + +SOURCE=.\math3d.cpp +# End Source File +# Begin Source File + +SOURCE=.\mytritri.cpp +# End Source File +# Begin Source File + +SOURCE=.\sysdep.cpp +# End Source File +# Begin Source File + +SOURCE=.\tritri.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\box.h +# End Source File +# Begin Source File + +SOURCE=.\coldet.h +# End Source File +# Begin Source File + +SOURCE=.\coldetimpl.h +# End Source File +# Begin Source File + +SOURCE=.\math3d.h +# End Source File +# Begin Source File + +SOURCE=.\mytritri.h +# End Source File +# Begin Source File + +SOURCE=.\sysdep.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/coldet/coldet.h b/coldet/coldet.h new file mode 100644 index 0000000..2c8f287 --- /dev/null +++ b/coldet/coldet.h @@ -0,0 +1,180 @@ +/* ColDet - C++ 3D Collision Detection Library + * Copyright (C) 2000 Amir Geva + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library 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. + * + * Any comments, questions and bug reports send to: + * photon@photoneffect.com + * + * Or visit the home page: http://photoneffect.com/coldet/ + */ +/** \file coldet.h + 3D Collision Detection + + Interface for the library. + Isolated from any implementation details. +*/ +#ifndef H_COLDET +#define H_COLDET + +#ifndef EXPORT +#define EXPORT +#endif + +/** Collision Model. Will represent the mesh to be tested for + collisions. It has to be notified of all triangles, via + addTriangle() + After all triangles are added, a call to finalize() will + process the information and prepare for collision tests. + Call collision() to check for a collision + + Note: Transformations must not contain scaling. +*/ +class CollisionModel3D +{ +public: + virtual ~CollisionModel3D() {} + + /** Optional: Optimization for construction speed. + If you know the number of triangles. */ + virtual void setTriangleNumber(int num) = 0; + + /** Use any of the forms of this functions to enter the coordinates + of the model's triangles. */ + virtual void addTriangle(float x1, float y1, float z1, + float x2, float y2, float z2, + float x3, float y3, float z3) = 0; + virtual void addTriangle(float v1[3], float v2[3], float v3[3]) = 0; + + /** All triangles have been added, process model. */ + virtual void finalize() = 0; + + /** The the current affine matrix for the model. + See transform.txt for format information */ + virtual void setTransform(float m[16]) = 0; + + /** Check for collision with another model. + Do not mix model types here. + + MaxProcessingTime determines the maximum time in milliseconds + to check for collision. If a rejection is not found by that + time, the function will return true. + + AccuracyDepth is not yet supported. + + other_transform allows overriding the other model's + transform, by supplying an alternative one. + This can be useful when testing a model against itself + with different orientations. + */ + virtual bool collision(CollisionModel3D* other, + int AccuracyDepth=-1, + int MaxProcessingTime=0, + float* other_transform=0) = 0; + + /** Returns true if the ray given in world space coordinates + intersects with the object. + getCollidingTriangles() and getCollisionPoint() can be + used to retrieve information about a collision. + If closest if false, the first triangle that collides with + the ray is used. Otherwise the closest one will be used. + Closest triangle searching will slow the test considerably. + The default ray is a standard infinite ray. However, using + segmin and segmax you can define a line segment along the + ray. + */ + virtual bool rayCollision(float origin[3], + float direction[3], + bool closest=false, + float segmin=0.0f, + float segmax=3.4e+38F) = 0; + + /** Returns true if the given sphere collides with the model. + getCollidingTriangles() and getCollisionPoint() can be + used to retrieve information about a collision. + */ + virtual bool sphereCollision(float origin[3], + float radius) = 0; + + /** Retrieve the pair of triangles that collided. + Only valid after a call to collision() that returned true. + t1 is this model's triangle and t2 is the other one. + In case of ray or sphere collision, only t1 will be valid. + The coordinates will be in _this_ model's coordinate space, + unless ModelSpace is false, in which case, coordinates will + be transformed by the model's current transform to world space. + */ + virtual bool getCollidingTriangles(float t1[9], float t2[9], bool ModelSpace=true) = 0; + + /** Retrieve the pair of triangles indices that collided. + Only valid after a call to collision() that returned true. + t1 belongs to _this_ model, while t2 is in the other one. + */ + virtual bool getCollidingTriangles(int& t1, int& t2) = 0; + + /** Retrieve the detected collision point. + Only valid after a call to collision() + that returned true. + The coordinates will be in _this_ model's coordinate space, + unless ModelSpace is false, in which case, coordinates will + be transformed by the model's current transform to world space. + */ + virtual bool getCollisionPoint(float p[3], bool ModelSpace=true) = 0; +}; + +/** Timeout exception class. Exception will be thrown if + the detection algorithm could not complete within + the given time limit. */ +class TimeoutExpired {}; + +/** Inconsistency exception. Exception will be thrown if + the model is inconsistent. + Examples: + Checking for collisions before calling finalize() + Trying to add triangles after calling finalize() */ +class Inconsistency {}; + +/** Create a new collision model object. + Use delete when finished with it. + + Setting Static to true indicates that the model does not + move a lot, and certain calculations can be done every time + its transform changes instead of every collision test. +*/ +EXPORT CollisionModel3D* newCollisionModel3D(bool Static=false); + + + +////////////////////////////////////////////// +// Utility Functions +////////////////////////////////////////////// + +/** Checks for intersection between a ray and a sphere. + center, radius define the sphere + origin, direction define the ray + point will contain point of intersection, if one is found. +*/ +bool SphereRayCollision(float center[3], float radius, + float origin[3], float direction[3], + float point[3]); + +/** Checks for intersection between 2 spheres. */ +bool SphereSphereCollision(float c1[3], float r1, + float c2[3], float r2); + + + +#endif // H_COLDET diff --git a/coldet/coldet_bld.cpp b/coldet/coldet_bld.cpp new file mode 100644 index 0000000..9a4f990 --- /dev/null +++ b/coldet/coldet_bld.cpp @@ -0,0 +1,74 @@ +/* ColDet - C++ 3D Collision Detection Library + * Copyright (C) 2000 Amir Geva + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library 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. + * + * Any comments, questions and bug reports send to: + * photon@photoneffect.com + * + * Or visit the home page: http://photoneffect.com/coldet/ + */ +#include "sysdep.h" +#include "coldetimpl.h" + +__CD__BEGIN + +CollisionModel3D* newCollisionModel3D(bool Static) +{ + return new CollisionModel3DImpl(Static); +} + +CollisionModel3DImpl::CollisionModel3DImpl(bool Static) +: m_Root(Vector3D::Zero, Vector3D::Zero,0), + m_Transform(Matrix3D::Identity), + m_InvTransform(Matrix3D::Identity), + m_ColTri1(Vector3D::Zero,Vector3D::Zero,Vector3D::Zero), + m_ColTri2(Vector3D::Zero,Vector3D::Zero,Vector3D::Zero), + m_iColTri1(0), + m_iColTri2(0), + m_Final(false), + m_Static(Static) +{} + +void CollisionModel3DImpl::addTriangle(const Vector3D& v1, const Vector3D& v2, const Vector3D& v3) +{ + if (m_Final) throw Inconsistency(); + m_Triangles.push_back(BoxedTriangle(v1,v2,v3)); +} + +void CollisionModel3DImpl::setTransform(const Matrix3D& m) +{ + m_Transform=m; + if (m_Static) m_InvTransform=m_Transform.Inverse(); +} + +void CollisionModel3DImpl::finalize() +{ + if (m_Final) throw Inconsistency(); + // Prepare initial triangle list + m_Final=true; + for(unsigned i=0;i0;num>>=1,logdepth++); + m_Root.m_logdepth=int(logdepth*1.5f); + m_Root.divide(0); +} + +__CD__END diff --git a/coldet/coldetimpl.h b/coldet/coldetimpl.h new file mode 100644 index 0000000..d8c0c91 --- /dev/null +++ b/coldet/coldetimpl.h @@ -0,0 +1,108 @@ +/* ColDet - C++ 3D Collision Detection Library + * Copyright (C) 2000 Amir Geva + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library 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. + * + * Any comments, questions and bug reports send to: + * photon@photoneffect.com + * + * Or visit the home page: http://photoneffect.com/coldet/ + */ +#ifndef H_COLDET_IMPL +#define H_COLDET_IMPL + +#include "coldet.h" +#include "box.h" +#include "math3d.h" +#include + +__CD__BEGIN + +class CollisionModel3DImpl : public CollisionModel3D +{ +public: + CollisionModel3DImpl(bool Static); + void setTriangleNumber(int num) { if (!m_Final) m_Triangles.reserve(num); } + + void addTriangle(float x1, float y1, float z1, + float x2, float y2, float z2, + float x3, float y3, float z3) + { + addTriangle(Vector3D(x1,y1,z1), + Vector3D(x2,y2,z2), + Vector3D(x3,y3,z3)); + } + void addTriangle(float v1[3], float v2[3], float v3[3]) + { + addTriangle(Vector3D(v1[0],v1[1],v1[2]), + Vector3D(v2[0],v2[1],v2[2]), + Vector3D(v3[0],v3[1],v3[2])); + } + void addTriangle(const Vector3D& v1, const Vector3D& v2, const Vector3D& v3); + void finalize(); + + void setTransform(float m[16]) { setTransform(*(Matrix3D*)m); } + void setTransform(const Matrix3D& m); + + bool collision(CollisionModel3D* other, + int AccuracyDepth, + int MaxProcessingTime, + float* other_transform); + + bool rayCollision(float origin[3], float direction[3], bool closest, + float segmin, float segmax); + bool sphereCollision(float origin[3], float radius); + + bool getCollidingTriangles(float t1[9], float t2[9], bool ModelSpace); + bool getCollidingTriangles(int& t1, int& t2); + bool getCollisionPoint(float p[3], bool ModelSpace); + + + int getTriangleIndex(BoxedTriangle* bt) + { + //return int(bt-m_Triangles.begin()); + return int(bt-&(*m_Triangles.begin())); + } + + /** Stores all the actual triangles. Other objects will use + pointers into this array. + */ + std::vector m_Triangles; + /** Root of the hierarchy tree */ + BoxTreeInnerNode m_Root; + /** The current transform and its inverse */ + Matrix3D m_Transform,m_InvTransform; + /** The triangles that last collided */ + Triangle m_ColTri1,m_ColTri2; + /** The indices of the triangles that last collided */ + int m_iColTri1,m_iColTri2; + /** The collision point of the last test */ + Vector3D m_ColPoint; + + /** Type of the last collision test */ + enum { Models, Ray, Sphere } + m_ColType; + /** Flag for indicating the model is finalized. */ + bool m_Final; + /** Static models will maintain the same transform for a while + so the inverse transform is calculated each set instead + of in the collision test. */ + bool m_Static; +}; + +__CD__BEGIN + +#endif // H_COLDET_IMPL diff --git a/coldet/makefile.g++ b/coldet/makefile.g++ new file mode 100644 index 0000000..4a16c98 --- /dev/null +++ b/coldet/makefile.g++ @@ -0,0 +1,50 @@ +PROJECT=coldet +LIB=libcoldet.a +CC=g++ +OPT=-O2 +CFLAGS=-c $(OPT) -DGCC +OBJS= \ +coldet.o \ +coldet_bld.o \ +box.o \ +box_bld.o \ +tritri.o \ +math3d.o \ +sysdep.o \ +mytritri.o + +all: $(LIB) + +$(LIB): $(OBJS) + rm -f $(LIB) + ar cr $(LIB) $(OBJS) + ranlib $(LIB) + +coldet.o: coldet.cpp + $(CC) $(CFLAGS) coldet.cpp + +coldet_bld.o: coldet_bld.cpp + $(CC) $(CFLAGS) coldet_bld.cpp + +box.o: box.cpp + $(CC) $(CFLAGS) box.cpp + +box_bld.o: box_bld.cpp + $(CC) $(CFLAGS) box_bld.cpp + +tritri.o: tritri.c + gcc $(CFLAGS) tritri.c + +mytritri.o: mytritri.cpp + $(CC) $(CFLAGS) mytritri.cpp + +math3d.o: math3d.cpp + $(CC) $(CFLAGS) math3d.cpp + +sysdep.o: sysdep.cpp + $(CC) $(CFLAGS) sysdep.cpp + + +clean: + rm -f *.o $(LIB) + diff --git a/coldet/math3d.cpp b/coldet/math3d.cpp new file mode 100644 index 0000000..99631fc --- /dev/null +++ b/coldet/math3d.cpp @@ -0,0 +1,93 @@ +/* ColDet - C++ 3D Collision Detection Library + * Copyright (C) 2000 Amir Geva + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library 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. + * + * Any comments, questions and bug reports send to: + * photon@photoneffect.com + * + * Or visit the home page: http://photoneffect.com/coldet/ + */ +#include "sysdep.h" +#include "math3d.h" + +const Vector3D Vector3D::Zero(0.0f,0.0f,0.0f); +const Matrix3D Matrix3D::Identity(1.0f,0.0f,0.0f,0.0f, + 0.0f,1.0f,0.0f,0.0f, + 0.0f,0.0f,1.0f,0.0f, + 0.0f,0.0f,0.0f,1.0f); + + +inline float +MINOR(const Matrix3D& m, const int r0, const int r1, const int r2, const int c0, const int c1, const int c2) +{ + return m(r0,c0) * (m(r1,c1) * m(r2,c2) - m(r2,c1) * m(r1,c2)) - + m(r0,c1) * (m(r1,c0) * m(r2,c2) - m(r2,c0) * m(r1,c2)) + + m(r0,c2) * (m(r1,c0) * m(r2,c1) - m(r2,c0) * m(r1,c1)); +} + + +Matrix3D +Matrix3D::Adjoint() const +{ + return Matrix3D( MINOR(*this, 1, 2, 3, 1, 2, 3), + -MINOR(*this, 0, 2, 3, 1, 2, 3), + MINOR(*this, 0, 1, 3, 1, 2, 3), + -MINOR(*this, 0, 1, 2, 1, 2, 3), + + -MINOR(*this, 1, 2, 3, 0, 2, 3), + MINOR(*this, 0, 2, 3, 0, 2, 3), + -MINOR(*this, 0, 1, 3, 0, 2, 3), + MINOR(*this, 0, 1, 2, 0, 2, 3), + + MINOR(*this, 1, 2, 3, 0, 1, 3), + -MINOR(*this, 0, 2, 3, 0, 1, 3), + MINOR(*this, 0, 1, 3, 0, 1, 3), + -MINOR(*this, 0, 1, 2, 0, 1, 3), + + -MINOR(*this, 1, 2, 3, 0, 1, 2), + MINOR(*this, 0, 2, 3, 0, 1, 2), + -MINOR(*this, 0, 1, 3, 0, 1, 2), + MINOR(*this, 0, 1, 2, 0, 1, 2)); +} + + +float +Matrix3D::Determinant() const +{ + return m[0][0] * MINOR(*this, 1, 2, 3, 1, 2, 3) - + m[0][1] * MINOR(*this, 1, 2, 3, 0, 2, 3) + + m[0][2] * MINOR(*this, 1, 2, 3, 0, 1, 3) - + m[0][3] * MINOR(*this, 1, 2, 3, 0, 1, 2); +} + +Matrix3D +Matrix3D::Inverse() const +{ + return (1.0f / Determinant()) * Adjoint(); +} + +Vector3D Matrix3D::GetTranslate() const +{ + return Vector3D(m[3][0], m[3][1], m[3][2]); +} + +void Matrix3D::Translate(const Vector3D & v) +{ + m[3][0] += v.x * m[0][0] + v.y * m[1][0] + v.z * m[2][0]; + m[3][1] += v.x * m[0][1] + v.y * m[1][1] + v.z * m[2][1]; + m[3][2] += v.x * m[0][2] + v.y * m[1][2] + v.z * m[2][2]; +} diff --git a/coldet/math3d.h b/coldet/math3d.h new file mode 100644 index 0000000..d7dc4e2 --- /dev/null +++ b/coldet/math3d.h @@ -0,0 +1,335 @@ +/* ColDet - C++ 3D Collision Detection Library + * Copyright (C) 2000 Amir Geva + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library 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. + * + * Any comments, questions and bug reports send to: + * photon@photoneffect.com + * + * Or visit the home page: http://photoneffect.com/coldet/ + */ +#ifndef H_MATH3D +#define H_MATH3D + +#include + +struct Vector3D; +struct Matrix3; +struct Matrix3D; +struct Plane; + +inline float flabs(float f) { return (f>=0.0f?f:-f); } +const float epsilon=1e-8f; +inline bool IsZero(float f) { return flabs(f) +inline T Max(T a, T b) +{ + return (a>b ? a : b); +} + +template +inline T Min(T a, T b) +{ + return (a0.0f && f23>0.0f) return Vector3D::Zero; + other_side=(f12<0.0f?(f23<0.0f?1:0):2); + } + Plane p2(t2.v1,t2.v2,t2.v3); + Vector3D n12(p1.normal+p2.normal); + TriangleDesc td2(t2,p2); + const Vector3D& a2=td2[other_side+1]; + const Vector3D& b2=td2[other_side]; + const Vector3D& c2=td2[other_side+2]; + float t21=-(p1.d+p2.d+a2*n12)/((b2-a2)*n12); + TriangleDesc td1(t1,p1); + Vector3D P21(a2+t21*(b2-a2)); + if (td1.pointInTri(P21)) return P21; + float t22=-(p1.d+p2.d+c2*n12)/((b2-c2)*n12); + Vector3D P22(c2+t22*(b2-c2)); + if (td1.pointInTri(P22)) return P22; + + { + float f1=p2.Classify(t1.v1); + float f2=p2.Classify(t1.v2); + float f3=p2.Classify(t1.v3); + float f12=f1*f2; + float f23=f2*f3; + if (f12>0.0f && f23>0.0f) return Vector3D::Zero; + other_side=(f12<0.0f?(f23<0.0f?1:0):2); + } + const Vector3D& a1=td1[other_side+1]; + const Vector3D& b1=td1[other_side]; + const Vector3D& c1=td1[other_side+2]; + float t11=-(p1.d+p2.d+a1*n12)/((b1-a1)*n12); + Vector3D P11(a1+t11*(b1-a1)); + if (td2.pointInTri(P11)) return P11; + float t12=-(p1.d+p2.d+c1*n12)/((b1-c1)*n12); + Vector3D P12(c1+t12*(b1-c1)); + if (td2.pointInTri(P12)) return P12; + return Vector3D::Zero; +} diff --git a/coldet/mytritri.h b/coldet/mytritri.h new file mode 100644 index 0000000..426775a --- /dev/null +++ b/coldet/mytritri.h @@ -0,0 +1,96 @@ +/* ColDet - C++ 3D Collision Detection Library + * Copyright (C) 2000 Amir Geva + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library 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. + * + * Any comments, questions and bug reports send to: + * photon@photoneffect.com + * + * Or visit the home page: http://photoneffect.com/coldet/ + */ +#ifndef H_MYTRITRI +#define H_MYTRITRI + +#include "box.h" + +/** A slower triangle-triangle intersection test, that returns the + point of intersection. */ +Vector3D my_tri_tri_intersect(const Triangle& t1, const Triangle& t2); + +/** Triangle description class. It is used to determine if a point + on the triangle's plane is inside the triangle. */ +class TriangleDesc : public Triangle +{ +public: + TriangleDesc(const Triangle& t, const Plane& p) + : Triangle(t) + { + const Vector3D& n=p.normal; + Vector3D a(flabs(n.x),flabs(n.y),flabs(n.z)); + if (a.x>a.y) + { + if (a.x>a.z) { i1=1; i2=2; } + else { i1=0; i2=1; } + } + else + { + if (a.y>a.z) { i1=0; i2=2; } + else { i1=0; i2=1; } + } + } + + bool pointInTri(const Vector3D& P) + { + Vector3D u(P[i1]-v1[i1], + v2[i1]-v1[i1], + v3[i1]-v1[i1]); + Vector3D v(P[i2]-v1[i2], + v2[i2]-v1[i2], + v3[i2]-v1[i2]); + float a,b; + if (u.y==0.0f) + { + b=u.x/u.z; + if (b>=0.0f && b<=1.0f) a=(v.x-b*v.z)/v.y; + else return false; + } + else + { + b=(v.x*u.y-u.x*v.y)/(v.z*u.y-u.z*v.y); + if (b>=0.0f && b<=1.0f) a=(u.x-b*u.z)/u.y; + else return false; + } + return (a>=0 && (a+b)<=1); + } + + const Vector3D& operator[] (int index) + { + switch (index) + { + case 0: return v1; + case 1: return v2; + case 2: return v3; + case 3: return v1; + } + return v2; + } + + int i1,i2; +}; + + + +#endif // H_MYTRITRI diff --git a/coldet/quickstart.html b/coldet/quickstart.html new file mode 100644 index 0000000..e341c79 --- /dev/null +++ b/coldet/quickstart.html @@ -0,0 +1,52 @@ + + + + + ColDet - Quick Start + + + +
+

+Quickstart:

+ +

+Model Setup:

+For each mesh, create a collision model by using: +
CollisionModel3D* model = newCollisionModel3D();
+(Shared meshes can use one model) +
Add all the triangles the mesh has to the model by using: +
model->addTriangle(vertex1,vertex2,vertex3);
+Call: +
model->finalize();
+  +

+Collision Test:

+Assuming you have two models (m1,m2), either set both of their’s transformation +
matrices (world matrix) by calling: +
m1->setTransform(model1_transformation_matrix);
+ +
m2->setTransform(model2_transformation_matrix);
+or set only one of them (in case you’re testing the model against itself, +with different +
transform) Then call: +
m1->collision(m2);
+The function returns a bool indicating if a collision has occurred.   +Note that if you test a +
model against itself with a different transform, you need to supply +that transform as an +
optional parameter. +
  +

+Collision Test Results:

+Use the getCollidingTriangles function to get which triangles have collided.   +Use the +
getCollisionPoint function to find the exact collision point. +
  +

+Other Collision Tests:

+You can use the rayCollision and sphereCollision functions to test the +model against +
these primitives. + + diff --git a/coldet/readme.txt b/coldet/readme.txt new file mode 100644 index 0000000..58d828a --- /dev/null +++ b/coldet/readme.txt @@ -0,0 +1,29 @@ + ColDet - 3D Collision Detection Library + Copyright (C) 2000 Amir Geva + + +Description: +ColDet is a 3D collision detection library, intended for games. +It supports generic polyhedra, and even polygon soups. + +Requirements: +It is written in standard C++ and can be compiled on these systems: +Windows: Visual C++ 6 +Windows: Borland C++ Builder 5 +Linux: g++ 2.8 +Other systems that have g++ will probably compile with no modification. +The code is portable to any system with a standard C++ compliant compiler +(as compliant as they get) + +Installation: +Use the supplied Visual C++ project file (coldet.dsp) +or the makefile (for systems with g++) +In other cases, just create a project/makefile and include all of the source files. + +Distribution: +It is distributed under the Library GNU Public License (See the file: COPYING) +Any redistribution of the files in this package must include the entire package. + +Contact Information: + Web Site: http://photoneffect.com/coldet/ + email: photon@photoneffect.com diff --git a/coldet/sysdep.cpp b/coldet/sysdep.cpp new file mode 100644 index 0000000..05b8ca7 --- /dev/null +++ b/coldet/sysdep.cpp @@ -0,0 +1,41 @@ +/* ColDet - C++ 3D Collision Detection Library + * Copyright (C) 2000 Amir Geva + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library 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. + * + * Any comments, questions and bug reports send to: + * photon@photoneffect.com + * + * Or visit the home page: http://photoneffect.com/coldet/ + */ +#include "sysdep.h" + +#ifdef GCC + +#include + +// Returns a time index in milliseconds +DWORD GetTickCount() +{ + static struct timezone tz={0,0}; + static const double t1=1000.0; + static const double t2=0.001; + timeval t; + gettimeofday(&t,&tz); + return long((t.tv_sec&0x000FFFFF)*t1 + t.tv_usec*t2); +} + +#endif diff --git a/coldet/sysdep.h b/coldet/sysdep.h new file mode 100644 index 0000000..57e1c43 --- /dev/null +++ b/coldet/sysdep.h @@ -0,0 +1,55 @@ +/* ColDet - C++ 3D Collision Detection Library + * Copyright (C) 2000 Amir Geva + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library 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. + * + * Any comments, questions and bug reports send to: + * photon@photoneffect.com + * + * Or visit the home page: http://photoneffect.com/coldet/ + */ +#ifndef H_SYSDEP +#define H_SYSDEP + +#ifdef GCC + +typedef unsigned long DWORD; +DWORD GetTickCount(); +#define __CD__BEGIN +#define __CD__END + +#elif defined(WIN32) + + #define WIN32_LEAN_AND_MEAN + #include + #define __CD__BEGIN + #define __CD__END + #ifdef COLDET_EXPORTS + #define EXPORT __declspec(dllexport) + #else + #define EXPORT __declspec(dllimport) + #endif +#else + +#error No system specified (WIN32 GCC) + +#endif + +#ifndef EXPORT + #define EXPORT +#endif + +#endif // H_SYSDEP diff --git a/coldet/transform.txt b/coldet/transform.txt new file mode 100644 index 0000000..63d7be0 --- /dev/null +++ b/coldet/transform.txt @@ -0,0 +1,21 @@ +The matrices used to describe model transformations are affine 4x4 matrices +which are D3D style, row major with translations in the 4th row. + +1 2 3 4 +5 6 7 8 +9 10 11 12 +13 14 15 16 + +The translation vector Tx,Ty,Tz is in elements 13,14,15 + +If you use the transposed scheme, which is: +1 5 9 13 +2 6 10 14 +3 7 11 15 +4 8 12 16 + +With the translation vector in elements 13,14,15 you can use +this matrix without any conversion. + +It is also important to note that the transformations should include +no scaling, as it disrupts the consistency of the collision model. diff --git a/coldet/tritri.c b/coldet/tritri.c new file mode 100644 index 0000000..33cc109 --- /dev/null +++ b/coldet/tritri.c @@ -0,0 +1,292 @@ +/* Triangle/triangle intersection test routine, + * by Tomas Moller, 1997. + * See article "A Fast Triangle-Triangle Intersection Test", + * Journal of Graphics Tools, 2(2), 1997 + * + * int tri_tri_intersect(float V0[3],float V1[3],float V2[3], + * float U0[3],float U1[3],float U2[3]) + * + * parameters: vertices of triangle 1: V0,V1,V2 + * vertices of triangle 2: U0,U1,U2 + * result : returns 1 if the triangles intersect, otherwise 0 + * + */ + +#include + + +/* if USE_EPSILON_TEST is true then we do a check: + if |dv|b) \ + { \ + float c; \ + c=a; \ + a=b; \ + b=c; \ + } + +#define ISECT(VV0,VV1,VV2,D0,D1,D2,isect0,isect1) \ + isect0=VV0+(VV1-VV0)*D0/(D0-D1); \ + isect1=VV0+(VV2-VV0)*D0/(D0-D2); + + +#define COMPUTE_INTERVALS(VV0,VV1,VV2,D0,D1,D2,D0D1,D0D2,isect0,isect1) \ + if(D0D1>0.0f) \ + { \ + /* here we know that D0D2<=0.0 */ \ + /* that is D0, D1 are on the same side, D2 on the other or on the plane */ \ + ISECT(VV2,VV0,VV1,D2,D0,D1,isect0,isect1); \ + } \ + else if(D0D2>0.0f) \ + { \ + /* here we know that d0d1<=0.0 */ \ + ISECT(VV1,VV0,VV2,D1,D0,D2,isect0,isect1); \ + } \ + else if(D1*D2>0.0f || D0!=0.0f) \ + { \ + /* here we know that d0d1<=0.0 or that D0!=0.0 */ \ + ISECT(VV0,VV1,VV2,D0,D1,D2,isect0,isect1); \ + } \ + else if(D1!=0.0f) \ + { \ + ISECT(VV1,VV0,VV2,D1,D0,D2,isect0,isect1); \ + } \ + else if(D2!=0.0f) \ + { \ + ISECT(VV2,VV0,VV1,D2,D0,D1,isect0,isect1); \ + } \ + else \ + { \ + /* triangles are coplanar */ \ + return coplanar_tri_tri(N1,V0,V1,V2,U0,U1,U2); \ + } + + + +/* this edge to edge test is based on Franlin Antonio's gem: + "Faster Line Segment Intersection", in Graphics Gems III, + pp. 199-202 */ +#define EDGE_EDGE_TEST(V0,U0,U1) \ + Bx=U0[i0]-U1[i0]; \ + By=U0[i1]-U1[i1]; \ + Cx=V0[i0]-U0[i0]; \ + Cy=V0[i1]-U0[i1]; \ + f=Ay*Bx-Ax*By; \ + d=By*Cx-Bx*Cy; \ + if((f>0 && d>=0 && d<=f) || (f<0 && d<=0 && d>=f)) \ + { \ + e=Ax*Cy-Ay*Cx; \ + if(f>0) \ + { \ + if(e>=0 && e<=f) return 1; \ + } \ + else \ + { \ + if(e<=0 && e>=f) return 1; \ + } \ + } + +#define EDGE_AGAINST_TRI_EDGES(V0,V1,U0,U1,U2) \ +{ \ + float Ax,Ay,Bx,By,Cx,Cy,e,d,f; \ + Ax=V1[i0]-V0[i0]; \ + Ay=V1[i1]-V0[i1]; \ + /* test edge U0,U1 against V0,V1 */ \ + EDGE_EDGE_TEST(V0,U0,U1); \ + /* test edge U1,U2 against V0,V1 */ \ + EDGE_EDGE_TEST(V0,U1,U2); \ + /* test edge U2,U1 against V0,V1 */ \ + EDGE_EDGE_TEST(V0,U2,U0); \ +} + +#define POINT_IN_TRI(V0,U0,U1,U2) \ +{ \ + float a,b,c,d0,d1,d2; \ + /* is T1 completly inside T2? */ \ + /* check if V0 is inside tri(U0,U1,U2) */ \ + a=U1[i1]-U0[i1]; \ + b=-(U1[i0]-U0[i0]); \ + c=-a*U0[i0]-b*U0[i1]; \ + d0=a*V0[i0]+b*V0[i1]+c; \ + \ + a=U2[i1]-U1[i1]; \ + b=-(U2[i0]-U1[i0]); \ + c=-a*U1[i0]-b*U1[i1]; \ + d1=a*V0[i0]+b*V0[i1]+c; \ + \ + a=U0[i1]-U2[i1]; \ + b=-(U0[i0]-U2[i0]); \ + c=-a*U2[i0]-b*U2[i1]; \ + d2=a*V0[i0]+b*V0[i1]+c; \ + if(d0*d1>0.0) \ + { \ + if(d0*d2>0.0) return 1; \ + } \ +} + +int coplanar_tri_tri(float N[3],float V0[3],float V1[3],float V2[3], + float U0[3],float U1[3],float U2[3]) +{ + float A[3]; + short i0,i1; + /* first project onto an axis-aligned plane, that maximizes the area */ + /* of the triangles, compute indices: i0,i1. */ + A[0]=fabs(N[0]); + A[1]=fabs(N[1]); + A[2]=fabs(N[2]); + if(A[0]>A[1]) + { + if(A[0]>A[2]) + { + i0=1; /* A[0] is greatest */ + i1=2; + } + else + { + i0=0; /* A[2] is greatest */ + i1=1; + } + } + else /* A[0]<=A[1] */ + { + if(A[2]>A[1]) + { + i0=0; /* A[2] is greatest */ + i1=1; + } + else + { + i0=0; /* A[1] is greatest */ + i1=2; + } + } + + /* test all edges of triangle 1 against the edges of triangle 2 */ + EDGE_AGAINST_TRI_EDGES(V0,V1,U0,U1,U2); + EDGE_AGAINST_TRI_EDGES(V1,V2,U0,U1,U2); + EDGE_AGAINST_TRI_EDGES(V2,V0,U0,U1,U2); + + /* finally, test if tri1 is totally contained in tri2 or vice versa */ + POINT_IN_TRI(V0,U0,U1,U2); + POINT_IN_TRI(U0,V0,V1,V2); + + return 0; +} + + +int tri_tri_intersect(float V0[3],float V1[3],float V2[3], + float U0[3],float U1[3],float U2[3]) +{ + float E1[3],E2[3]; + float N1[3],N2[3],d1,d2; + float du0,du1,du2,dv0,dv1,dv2; + float D[3]; + float isect1[2], isect2[2]; + float du0du1,du0du2,dv0dv1,dv0dv2; + short index; + float vp0,vp1,vp2; + float up0,up1,up2; + float b,c,max; + + /* compute plane equation of triangle(V0,V1,V2) */ + SUB(E1,V1,V0); + SUB(E2,V2,V0); + CROSS(N1,E1,E2); + d1=-DOT(N1,V0); + /* plane equation 1: N1.X+d1=0 */ + + /* put U0,U1,U2 into plane equation 1 to compute signed distances to the plane*/ + du0=DOT(N1,U0)+d1; + du1=DOT(N1,U1)+d1; + du2=DOT(N1,U2)+d1; + + /* coplanarity robustness check */ +#if USE_EPSILON_TEST==TRUE + if(fabs(du0)0.0f && du0du2>0.0f) /* same sign on all of them + not equal 0 ? */ + return 0; /* no intersection occurs */ + + /* compute plane of triangle (U0,U1,U2) */ + SUB(E1,U1,U0); + SUB(E2,U2,U0); + CROSS(N2,E1,E2); + d2=-DOT(N2,U0); + /* plane equation 2: N2.X+d2=0 */ + + /* put V0,V1,V2 into plane equation 2 */ + dv0=DOT(N2,V0)+d2; + dv1=DOT(N2,V1)+d2; + dv2=DOT(N2,V2)+d2; + +#if USE_EPSILON_TEST==TRUE + if(fabs(dv0)0.0f && dv0dv2>0.0f) /* same sign on all of them + not equal 0 ? */ + return 0; /* no intersection occurs */ + + /* compute direction of intersection line */ + CROSS(D,N1,N2); + + /* compute and index to the largest component of D */ + max=fabs(D[0]); + index=0; + b=fabs(D[1]); + c=fabs(D[2]); + if(b>max) max=b,index=1; + if(c>max) max=c,index=2; + + /* this is the simplified projection onto L*/ + vp0=V0[index]; + vp1=V1[index]; + vp2=V2[index]; + + up0=U0[index]; + up1=U1[index]; + up2=U2[index]; + + /* compute interval for triangle 1 */ + COMPUTE_INTERVALS(vp0,vp1,vp2,dv0,dv1,dv2,dv0dv1,dv0dv2,isect1[0],isect1[1]); + + /* compute interval for triangle 2 */ + COMPUTE_INTERVALS(up0,up1,up2,du0,du1,du2,du0du1,du0du2,isect2[0],isect2[1]); + + SORT(isect1[0],isect1[1]); + SORT(isect2[0],isect2[1]); + + if(isect1[1] +#include +#include + +extern int global_EC; + +#ifndef VIEWGL_FOVY +#define VIEWGL_FOVY 60.0f +#endif +#ifndef VIEWGL_ZNEAR +#define VIEWGL_ZNEAR 0.1f +#endif +#ifndef VIEWGL_ZFAR +#define VIEWGL_ZFAR 250.0f +#endif + +extern SDL_Surface* screen; +int videoFlags = 0; + +/* +void ERROR(const char* s) { + std::cerr << "Error" << s << std::endl; + std::cerr << "* last SDL error was: " << SDL_GetError() << std::endl; + global_EC = 1; + exit(1); +}*/ + +int resize(int w, int h) { + GLfloat ratio; + if ( h == 0 ) + h = 1; + ratio = ( GLfloat )w / ( GLfloat )h; + glViewport( 0, 0, ( GLint )w, ( GLint )h ); + glMatrixMode( GL_PROJECTION ); + glLoadIdentity( ); + //glRotatef(180, 0, 0, 1); + gluPerspective( VIEWGL_FOVY, ratio, VIEWGL_ZNEAR, VIEWGL_ZFAR); + + //glOrtho(-1.0f, 1.0f, -1.0f, 1.0f, 0.1f, 300.0f); + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity( ); + return(1); +} + +void initVideo(int w, int h, int bpp) { + const SDL_VideoInfo *videoInfo; + /* + SDL_Rect **modes; + int i; + + videoInfo = SDL_GetVideoInfo( ); + modes=SDL_ListModes(videoInfo->vfmt, SDL_FULLSCREEN|SDL_HWSURFACE); + if(modes == (SDL_Rect **)0){ + printf("No modes available!\n"); + exit(1); + } + + if(modes == (SDL_Rect **)-1){ + printf("All resolutions available.\n"); + } + else{ + printf("Available Modes\n"); + for(i=0;modes[i];++i) + printf(" %d x %d\n", modes[i]->w, modes[i]->h); + } + */ + + if (!videoInfo) + ERROR("VideoInfo query failed"); + videoFlags = SDL_OPENGL; + videoFlags |= SDL_GL_DOUBLEBUFFER; + videoFlags |= SDL_HWPALETTE; + //videoFlags |= SDL_RESIZABLE; + //videoFlags |= SDL_FULLSCREEN; + + if ( videoInfo->hw_available ) { + std::cerr << "Info: Using HWSURFACE" << std::endl; + videoFlags |= SDL_HWSURFACE; + } + else { + std::cerr << "Info: Using SWSURFACE" << std::endl; + videoFlags |= SDL_SWSURFACE; + } + if ( videoInfo->blit_hw ) { + std::cerr << "Info: Using HWACCEL" << std::endl; + videoFlags |= SDL_HWACCEL; + } + SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); + + screen = SDL_SetVideoMode( w, h, bpp, videoFlags ); + if (!screen) + ERROR("SDL failed to generate requested VideoSurface!"); + + resize(w, h); +} + +void initGL() { + GLfloat LightAmbient[] = { 0.5f, 0.5f, 0.5f, 1.0f }; + GLfloat LightDiffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f }; + GLfloat LightPosition[] = { 500.0f, 200.0f, 300.0f, 1.0f }; + + //glEnable( GL_TEXTURE_2D ); + glShadeModel( GL_SMOOTH ); + glClearColor( 0.0f, 0.0f, 0.0f, 0.0f ); + //glClearDepth( 1.0f ); + glEnable( GL_DEPTH_TEST ); + glEnable( GL_LIGHTING ); + //glDepthFunc( GL_LEQUAL ); + glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST ); + glLightfv( GL_LIGHT0, GL_AMBIENT, LightAmbient ); + glLightfv( GL_LIGHT0, GL_DIFFUSE, LightDiffuse ); + glLightfv( GL_LIGHT0, GL_POSITION, LightPosition ); + glEnable( GL_LIGHT0 ); + glEnable( GL_COLOR_MATERIAL); + glCullFace(GL_BACK); + glPolygonMode(GL_FRONT, GL_FILL); + glPolygonMode(GL_BACK, GL_LINE); +} + +/* + +SDL_Surface* createRGBASurface(int w, int h) { +#if SDL_BYTEORDER == SDL_BIG_ENDIAN +#define rmask 0xff000000 +#define gmask 0x00ff0000 +#define bmask 0x0000ff00 +#define amask 0x000000ff +#else +#define rmask 0x000000ff +#define gmask 0x0000ff00 +#define bmask 0x00ff0000 +#define amask 0xff000000 +#endif + return SDL_CreateRGBSurface(SDL_SWSURFACE|SDL_SRCALPHA, + 64, 64, 32, rmask, gmask, bmask, amask); +} + +SDL_Surface* createRGBSurface(int w, int h) { +#if SDL_BYTEORDER == SDL_BIG_ENDIAN +#define rmask 0xff000000 +#define gmask 0x00ff0000 +#define bmask 0x0000ff00 +#define amask 0x000000ff +#else +#define rmask 0x000000ff +#define gmask 0x0000ff00 +#define bmask 0x00ff0000 +#define amask 0xff000000 +#endif + return SDL_CreateRGBSurface(SDL_SWSURFACE, + w, h, 24, rmask, gmask, bmask, amask); +} +*/ diff --git a/common_sdl_gl.h b/common_sdl_gl.h new file mode 100644 index 0000000..ca4eeaf --- /dev/null +++ b/common_sdl_gl.h @@ -0,0 +1,17 @@ +#ifndef SDL_GL_COMMON_FUNCS_H +#define SDL_GL_COMMON_FUNCS_H +#include + +int resize(int w, int h); +void initVideo(int w, int h, int bpp); +void initGL(); +//void ERROR(const char* s); + +//SDL_Surface* createRGBSurface(int w, int h); +//SDL_Surface* createRGBASurface(int w, int h); + +extern int videoFlags; +extern int global_Done; +extern int global_EC; + +#endif diff --git a/dataholder.cpp b/dataholder.cpp new file mode 100644 index 0000000..3bf8296 --- /dev/null +++ b/dataholder.cpp @@ -0,0 +1,100 @@ +/************************************************************************ +* Copyright (c) 2005-2006 tok@openlinux.org.uk * +* * +* This software is provided as-is, without any express or implied * +* warranty. In no event will the authors be held liable for any * +* damages arising from the use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute * +* it freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must * +* not claim that you wrote the original software. If you use this * +* software in a product, an acknowledgment in the product documentation * +* would be appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must * +* not be misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source * +* distribution. * +************************************************************************/ +#include "dataholder.h" +#include "log.h" +#include "cistring.h" + +namespace OpenGTA { + template<> ActiveStyle::ActiveData() { + m_data = 0; + } + + template<> ActiveStyle::~ActiveData() { + unload(); + } + + template<> GraphicsBase & ActiveStyle::get() { + assert(m_data); + return *m_data; + } + + template void ActiveData::unload() { + if (m_data) + delete m_data; + m_data = 0; + } + + template<> void ActiveStyle::load(const std::string & file) { + unload(); + Util::ci_string tmpname(file.c_str()); + if (tmpname.find(".g24") != std::string::npos) { + m_data = new Graphics24Bit(file); + } + else if (tmpname.find(".gry") != std::string::npos) { + m_data = new Graphics8Bit(file); + } + else { + try { + m_data = new Graphics8Bit(file); + } + catch (const Exception & e) { + INFO << "loading 8 bit failed: " << e.what() << std::endl; + m_data = 0; + try { + m_data = new Graphics24Bit(file); + } + catch (const Exception & e) { + ERROR << "loading 24 bit failed " << e.what() << std::endl; + m_data = 0; + } + } + } + assert(m_data); + } + + template<> ActiveMap::ActiveData() { + m_data = 0; + } + + template<> ActiveMap::~ActiveData() { + unload(); + } + + template<> Map & ActiveMap::get() { + assert(m_data); + return *m_data; + } + + template<> void ActiveMap::load(const std::string & file) { + unload(); + try { + m_data = new Map(file); + } + catch (const Exception & e) { + ERROR << "loading map failed: " << e.what(); + m_data = 0; + } + assert(m_data); + } + +} diff --git a/dataholder.h b/dataholder.h new file mode 100644 index 0000000..9db4982 --- /dev/null +++ b/dataholder.h @@ -0,0 +1,64 @@ +/************************************************************************ +* Copyright (c) 2005-2006 tok@openlinux.org.uk * +* * +* This software is provided as-is, without any express or implied * +* warranty. In no event will the authors be held liable for any * +* damages arising from the use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute * +* it freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must * +* not claim that you wrote the original software. If you use this * +* software in a product, an acknowledgment in the product documentation * +* would be appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must * +* not be misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source * +* distribution. * +************************************************************************/ +#ifndef STYLE_HOLDER_H +#define STYLE_HOLDER_H +#include +#include "opengta.h" +#include "m_exceptions.h" +#include "Singleton.h" + +namespace OpenGTA { +/* + class ActiveStyle { + public: + ActiveStyle(); + ~ActiveStyle(); + GraphicsBase & getStyle(); + void load(const std::string & file); + private: + void unload(); + GraphicsBase* m_style; + }; +*/ + template + class ActiveData { + public: + ActiveData(); + ~ActiveData(); + T & get(); + void load(const std::string & file); + private: + void unload(); + T* m_data; + }; + + typedef ActiveData< GraphicsBase > ActiveStyle; + typedef ActiveData< Map > ActiveMap; + + typedef Loki::SingletonHolder< ActiveStyle, Loki::CreateUsingNew, Loki::DefaultLifetime, + Loki::SingleThreaded> StyleHolder; + typedef Loki::SingletonHolder< ActiveMap, Loki::CreateUsingNew, Loki::DefaultLifetime, + Loki::SingleThreaded> MapHolder; +} + +#endif diff --git a/doc/doc_links.txt b/doc/doc_links.txt new file mode 100644 index 0000000..c950869 --- /dev/null +++ b/doc/doc_links.txt @@ -0,0 +1,27 @@ +Cityscape Data Structure +http://azz.gouranga.com/files/cds.zip + +Do not miss the rest of these gems: +http://azz.gouranga.com/index.php?page=8 + +GTA Mission Template Description (can be found elsewhere) +http://www.fileplanet.com/19571/10000/fileinfo/2/0/section/General + +GTA2 Format Docs (somewhat related ;-) +http://files.filefront.com/GTA2+Format+Docs/;10536;;/fileinfo.html + +The unofficial Grand Theft Auto Reference Handbook +http://gta.mendelsohn.de/Reference/ + +Corrections and Notes regarding DMA GTA technical doument [cds.doc v12.10] +http://www.fifengr.com/gtacars/topic.html + +--- other links --- +A Stream-based Time Synchronization Technique For Networked Computer Games +http://www.mine-control.com/zack/timesync/timesync.html + +Fix Your Timestep! +http://www.gaffer.org/game-physics/fix-your-timestep/ + +Car Physics for Games - by Marco Monster +http://web.archive.org/web/20051218060818/http://home.planet.nl/~monstrous/tutcar.html diff --git a/doc/gouranga.txt b/doc/gouranga.txt new file mode 100644 index 0000000..dfa7152 --- /dev/null +++ b/doc/gouranga.txt @@ -0,0 +1,133 @@ +i emailed you all this but am posting it here just-in-case: + +hi, i declare myself an expert on gta1 and gta2 map format, i can help you with anything you ask, email me at jernejcoder@gmail.com, let's get straight to answers: + +* Can anyone tell me more about the height/size of blocks as drawn in-game? Currently I texture a unit-cube, but that doesn't look right (may have other causes). + +one gta cube is 1*1*1 unit big + +* When looking at the "slope type" bits in "type_map" (CDS.DOC) I am not sure howto interpret something like "1- 2 = up 26 low, high"; is it 26 degrees, pixel or another unit? + +use it like this: + +* 1- 2 = up 26 low, high +* 3 - 4 = down 26 low, high +* 5 - 6 = left 26 low, high +* 7 - 8 = right 26 low, high + +* 9 - 16 = up 7 low - high +* 17 - 24 = down 7 low - high +* 25 - 32 = left 7 low - high +* 33 - 40 = right 7 low - high + +* 41 - 44 = 45 up,down,left,right + +there are 3 types of slopes in gta1, (90°, 45° and 7°) all change level for just one cube. +take parts 1 and 2, these when used together make a pass from lower to upper level over 2 cubes, cube 1 is lower and cube 2 is upper part for this. + +* Concerning the FON files; the GTA hacking handbook (http://gta.mendelsohn.de/Reference/Fil_index.html) seems to define them, but the link is broken; the older (zip) version doesn't contain that file. Is that fileformat documented somewhere? + +this is very easy format: + +Quote: + + +type +TFon_file_header = packed record +NumPics:byte; +height:Byte; +end; + +type +TFon_image_header = packed record +width:byte; +end; + +the file starts with TFon_file_header which tells you num pics and all pictures height + +then for NumPics there are repeatitous data for characters starting with TFon_image_header that tells you character width, following that you get charwidth * fontheight of 8 bit bitmap data. + +last 768 bytes in a file is palette (RGB format) + + + + +i have sound format as well: + +Quote: + + +grand theft auto .sdt format +this file is copyright 2002 Delfi delfi@volja.net +data types are named as used in borland delphi's object pascal language + +warning! +this is for GTA1 and GTA:L only +gta2 and gta3 use different format... + +sdt files contains info for raw sound data files with same name but raw extension + +divide size of sdt file by 12 to get number of sound records +each record contains information on how to use raw file + +here is delphi packed record: + +Tsoundentry = packed record +rawstart: longword; +rawsize: longword; +samplerate: longword; +end; + +you can export sound like this: + +make new empty file and write wav file header: + +TWave_data = packed record + ChunkID: array[0..3] of char; // 'RIFF' text + ChunkSize: Longword; + Format: array[0..3] of char; // 'WAVE' text + Subchunk1ID: array[0..3] of char; // 'fmt ' text + Subchunk1Size: Longword; + AudioFormat: Word; + NumChannels: Word; + SampleRate: Longword; + ByteRate: Longword; + BlockAlign: Word; + BitsPerSample: Word; + Subchunk2ID: array[0..3] of char; // 'data' text + Subchunk2Size: Longword; +end; + +set info in header + +Wave_data.ChunkID := 'RIFF'; +Wave_data.ChunkSize:= 49188; +Wave_data.Format:= 'WAVE'; +Wave_data.Subchunk1ID:= 'fmt '; +Wave_data.Subchunk1Size:= 16; +Wave_data.AudioFormat:= 1; +Wave_data.NumChannels:= 1; +Wave_data.SampleRate:= sample rate of entry +Wave_data.BitsPerSample:=8; +Wave_data.ByteRate:= sample rate of entry +Wave_data.BlockAlign:=1; +Wave_data.Subchunk2ID:= 'data'; +Wave_data.Subchunk2Size:= raw data size + +now copy raw data and write it after this header +if you did everything correct you will be able to play wav file with any program... + +something interesting from GTA Wave gta sound editor readme file: +In GTA, all the sounds are in 8-bit mono format, except for those in +LEVEL000. The sounds in this file are 16-bit, the first three being +stereo. + + + +should this be all for now, i have slope cubes in a 3d format representation if you want, email me at jernejcoder@gmail.com or MSN - stdcall@gmail.com (messenger only) + +i'm onto a similar project, a gta1 / gta2 clone project, but gta formats are too limiting for me, and i went onto coding my own gta game clone, you can get screenshoots & demo http://gtatools.com/tdc/, it is written with opengl and delphi 4, i've gotten rendering pretty much perfect, and made my own map and sprite editor, but won't be compatible with gta file formats, but converters can be easily done, here is a picture: + +gtatools.com/tdc/tdcware.jpg + +best regards, Jernej L. diff --git a/doc/gta1_winex.txt b/doc/gta1_winex.txt new file mode 100644 index 0000000..e57e203 --- /dev/null +++ b/doc/gta1_winex.txt @@ -0,0 +1,20 @@ +GTA Settings runs fine here. Winex 2.2.1. +--- +In order to run "GTA Settings.exe" you need a legit Windows install (so you have MFC42.DLL to be specific). The DLL needs to be in your WineX "windows\system32" directory. +--- +no need to use the setup tool, drop this into your system.reg: + +[Software\\DMA Design\\Grand Theft Auto] +"Language"=dword:00000000 + +[Software\\DMA Design\\Grand Theft Auto\\Controls] +"Control 0"=dword:000000cb +"Control 1"=dword:000000cd +"Control 2"=dword:000000c8 +"Control 3"=dword:000000d0 +"Control 4"=dword:00000039 +"Control 5"=dword:0000001c +"Control 6"=dword:0000001d +"Control 7"=dword:0000002d +"Control 8"=dword:0000002c +"Control 9"=dword:0000000f diff --git a/doc/hacking.txt b/doc/hacking.txt new file mode 100644 index 0000000..0eb692b --- /dev/null +++ b/doc/hacking.txt @@ -0,0 +1,79 @@ +== OpenGTA project overview == + +- \subpage Fileformats +- \subpage Rendering + + +\namespace Audio +\brief Making noise. + +Very incomplete wrapper around \e SDL_sound and \e SDL_mixer. + +\namespace Loki +\brief From: http://loki-lib.sourceforge.net/ + +Loki is a C++ library of designs, containing flexible implementations +of common design patterns and idioms. + +I only use the \e Singleton a lot, but there is some nice code there. + +\namespace OpenGL +\brief Drawing stuff + +A bunch of quite experimental code; the highlights: + +- #OpenGL::TextureCache + +The following are used as \e Singletons: +- #OpenGL::Camera --- #OpenGL::CameraHolder +- #OpenGL::Screen --- #OpenGL::ScreenHolder +- #OpenGL::SpriteCache --- #OpenGL::SpriteCacheHolder + +\namespace Util +\brief This and that + +- #Util::ci_string +- #Util::Log +- A bunch of exceptions derived from std::exception + +And a \e Singleton object: +- #Util::BufferCache --- #Util::BufferCacheHolder + + += Fileformats of the GTA1 data files = + +Fortunately some documentation exists, specifically about +the most important formats. + +Reading _(cds.doc) is probably vital; you may want them all. +So in no particular order: + +include(`doc/doc_links.txt')dnl ~oh m4, your glorious thing~ + +Support for the following formats is implemented: +- CMP (compressed map) #OpenGTA::Map +- FNT (font bitmaps) #OpenGTA::Font +- FXT (text strings) #OpenGTA::MessageDB +- GRY (8bit graphics) #OpenGTA::Graphics8Bit +- G24 (24 bit graphics) #OpenGTA::Graphics24Bit +- SDT (RAW sound offsets) #OpenGTA::SoundsDB + + += Rendering the scene = + +The central part is #OpenGTA::CityView, which renders the #OpenGTA::Map +using the graphics loaded inside a derived instance of +#OpenGTA::GraphicsBase. + +CityView maintains a couple of #OpenGL::TextureCache (s) to store the +texture-ids (for the static city blocks). + +#OpenGTA::BlockData contains the vertex data and texture coords for +each of the of the possible blocks; this corresponds to +#OpenGTA::Map::BlockInfo::slopeType. + +Drawing of objects (sprites) is being moved into #OpenGTA::SpriteManager, +though this is not yet complete. + +#OpenGL::DrawableFont can display strings using the bitmaps from +a #OpenGTA::Font. diff --git a/doc/more_hints_delfi.txt b/doc/more_hints_delfi.txt new file mode 100644 index 0000000..6d11772 --- /dev/null +++ b/doc/more_hints_delfi.txt @@ -0,0 +1,80 @@ +2006/8/21, tok@openlinux.org.uk : +> I have got it; quite a lot in there. +> +> Guess I have to try my luck with dosbox, wine and such; last time I +> tried neither m1win nor junction25 were working very well (if I remember +> correctly). + +thats weird, since i can run both topdown j25 and 3D beta j25 just +fine, only M1win doesn't work (on xp) for some reason. i sent you old +M1 for DOS in the pack i sent you the url to. + +> Thanks for the other tips; now the main problem are the incorrect +> tex-coords. I think I have a pretty good idea why it breaks and a few +> on how to fix it. +> I found the off-by-one in the tile-indices on my own on the same afternoon. + +some of your side tiles are rotated, there is no side rotation, sides +only have a bit flag which means "mirror" - no rotations. + +cds says about side tiles that bit 5 and 6 in type_map_ext contain the +mirroring: +bit 5 means that textures on north and south sides should be mirrored, +bit 6 indicates same for east and west side of that cube. + +> I have started with the g24 files and hit a small snag; I don't +> understand 'palette_index' (so far) and the actual way tiles +> (lid+side textures; not working with sprites so far) index the clut +> isn't working either. [but don't tell me yet] + +the pal_index reindexes virtual palette numbers into actual - physical +palette numbers. + +it works like this: + +you take tile palette index, you add palette_index + +for sprites for example you need to skip clut dedicated to tiles: +pal_index = (g24_header.tileclut_size div 1024)+ activesprite.clut; + +for tiles it is just simple, but remember - if it is lid you need to +add side tiles to the number (and side tiles + lid tiles for animation +/ aux tiles): +pal_index[tile number] + +please_see_attached_illustration which also shows the palclut visualized :) + +> I have put a few new screenshots (after your fixes) up and removed the +> code until I have worked out the license. +> I am soon going on vacation for some time, so there won't be any updates +> for a few weeks. You'll hear from me when I am back. +> +> these already look a lot better +> http://skybound.portland.co.uk/gta/nyc_3d__two_bugs_down_2006-08-21.jpg + +In the nyc i can clearly see you are rotating side tiles, where they +shouldn't be, side tiles are never rotated in gta1 (they are in gta2) +in gta1 they just have mirroring flag in type_map_ext. + +> http://skybound.portland.co.uk/gta/far_out_2006-08-21.jpg + +and i am seeing this right? - the san andreas map on screenshoot - +unless you rotated the camera itself, should be rotated 180° left (i +know how it shouls look, since i drawn entire san andreas map layout +in mspaint with alt + tabbing 7 years ago ;) ). + +> http://skybound.portland.co.uk/gta/texture_coords_are_next_2006-08-21.jpg + +this looks quite right, but it appears lid rotation is wrong in blocks +where the type_map_ext mirror flags are used. + +i have a delphi project written in pascal, which can render all tiles +from g24 files, i can send you whole source code for it, it may help +you understand how g24 files work :) + +thats all for now, and enjoy your vacations! :) + + +> Best regards, +> tok +> diff --git a/doc/more_info.txt b/doc/more_info.txt new file mode 100644 index 0000000..b635bf7 --- /dev/null +++ b/doc/more_info.txt @@ -0,0 +1,115 @@ +you should only work with g24 files, since most, if not all mods for gta +only use g24 files, they are much better quality and are not any harder than +gry files (actually there are also GRX files described in cds.doc for use by +editor but nobody has these). + +you should take a look at how the slope models work, it is all in very +simple order, you could simplify things a lot, since gta1 maps don't use +more complex things. + +my friend steve made some nice code for rendering map slopes some time back, +here is how it works in pascal, basicly it modifies edge heights for the +cube depending on slope number, slope 0 is normal cube and other slope +levels modify the height, the bottom vertices stay where they are, so you +can easily draw quads using this: + + // 1---2 + // | z | + // 3---4 + + // calculate slope (lid vertex z values) + case s of + 1..8: // 2 blocks + begin + z1:=(((s-1) mod 2)+ord(s in [1,2, 5,6]))/2; + z2:=(((s-1) mod 2)+ord(s in [1,2, 7,8]))/2; + z3:=(((s-1) mod 2)+ord(s in [3,4, 5,6]))/2; + z4:=(((s-1) mod 2)+ord(s in [3,4, 7,8]))/2; + end; + 9..40: // 8 blocks + begin + z1:=(((s-9) mod 8)+ord(s in [9..16, 25..32]))/8; + z2:=(((s-9) mod 8)+ord(s in [9..16, 33..40]))/8; + z3:=(((s-9) mod 8)+ord(s in [17..24, 25..32]))/8; + z4:=(((s-9) mod 8)+ord(s in [17..24, 33..40]))/8; + end; + 41..44: // 1 block + begin + z1:=ord(s in [41, 43]); + z2:=ord(s in [41, 44]); + z3:=ord(s in [42, 43]); + z4:=ord(s in [42, 44]); + end; + else // no slope + z1:=1; z2:=1; z3:=1; z4:=1; + //z1:=0; z2:=0; z3:=0; z4:=0; + end; + +i don't recomment display lists for this, i tried it in my game and got huge +slowdowns, maybe if you compiled bigger parts of maps into it, but you will +need to take into account that the game uses sprites, and sprites + slopes +don't mix too good, in my game i solved this by rendering without any depth +buffer at all, but i have to do some painful sorting of rendering order, if +you want to render map with zbuffer you could use projected textures for +cars or render map in layers bottom to up, switching off zbuffer when +rendering sprites. + +if a cube is set to flat, all faces on it use transparency, and if left + +right faces use same texture the right face is folded into left double-sided +tile one to form fences (same applies to up and bottom tiles, they create a +double-sided fence on north tile only) + +transparency rule in gta this: + +every palette index 0 is transparent if the map tile is set to use it. i +recommend you to convert graphical data, take the 8 bit graphic and palette +and create 32 bit image and upload it to opengl, create alpha transparency +channel using palette index 0. + +every tile can have 4 shading levels, in g24 they are stored in palette +clut, i recommend you to ignore the shading data as whole, and use glcolor +commands to apply shading levels, also gtacars g24 editor has a terrible +habbit of generating these shades wrong and they look ugly in some mode. + +for sprites, they use one palette, except peds (pedesterian and cop) and +cars, +i suggest your game should render all ped sprites into one bigger texture, +using vertex coordinates to address each of these, you will need to create +around 30 or more different recoloured remaps while loading g24 file (all +tiles would fit into 256*512 texture if you use block sorting algorythm to +optimize room usage), every ped remap would cost 524.288 bytes, 30 remaps +would so end up at 15728640b - 15 mb, too bad that all new hardware comes +without support for palettized textures, they would help a lot here, gta1 +for windows for example uses driectdraw and the renderer is custom built. + +cars are even more difficult, they come in 16 different color variant, and +each of cars can have delta damage or doors or special police light +animations on them, i talked to dma employee working on gta2 and he said +that each car simply uses its own texture, which is created, modified and +destroyed as needed, don't worrs about speed, i done tests for my own game +and i can say that this is not a problem, the deltas don't change too often, +and when they are they are cached as long as the car is onscreen. + +some fonts in gta1 have limited use, such as pager messages or score counter +so they are missing characters or use modified order, a lot of font stuff is +hard-coded, but i found out that some onscreen fonts such as score +multiplier and lifes counter fonts are actually remapped using a palette in +G24 file (!!), i suggest you to just use the font as-is and ignore the +recoloring variants, probably very few people would notice this, but then +you can recolour them with glcolor commands aniway. + +the idea of a project website sounds good, don't worry about legal troubles, +take2 won't bother about this little project for a older game that they +released as free few years ago on rockstar classics website ( +http://www.rockstargames.com/classics/), they did discreetly interfere when +a member on gtaforums ran a project to bring gta vice city map to unreal +tournament engine, and for hot cofee fiasco they knew it was their fault all +the time, and they even supported us with emails that aren't for the public, +as for the CDS and MTD docs, they were officaly released to public with no +licenses (same for gta2 too) so using them is not legal trouble. + +also when you have time check out http://www.dmadesign.org/ it is DMA Design +history website ran by Mike Daily who worked on gta1 graphical engine for +gta1, gta1 prototypes and created lemmings game, it has some history pages +and a forum, altrough it looks inactive it is not, if you want you can ask +him questions in the forum :) diff --git a/doc/slopes1.txt b/doc/slopes1.txt new file mode 100644 index 0000000..de15bfe --- /dev/null +++ b/doc/slopes1.txt @@ -0,0 +1,1413 @@ +# there are 45 cubes in gta1, that is 0..44. +# every cube starts with name "slope: " + slope number (0..44) +# then there are 5 faces following: +# +# each face has a name (always in same order (lid, north, south, west, east)) +# two numbers in next line reprisent two special flags (LidFirstPass and +# Reverse) +# no idea what LidFirstPass does, i guess it is something about my special +# depthbuffer-less rendering, the reverse flag means you need to reverse the +# triangle / quad drawing order (otherwise it will be culled off +# inappropriately i think) +# after this there are 4 vectors XYZ, you can handle those easily i think :) +# +# i will work on, and remove the need for reverse draw order, i'll send you +# new copy of data when i'm done :) +# +# there is more email after this data, btw ;) +# +Slope: 0 +LID +0 0 +0,00, -1,00, 1,00 +1,00, -1,00, 1,00 +1,00, 0,00, 1,00 +0,00, 0,00, 1,00 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 1,00 +1,00, 0,00, 1,00 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 1,00 +0,00, -1,00, 1,00 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 1,00 +0,00, 0,00, 1,00 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 1,00 +1,00, -1,00, 1,00 +Slope: 1 +LID +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, 0,00, 0,50 +0,00, 0,00, 0,50 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 0,50 +1,00, 0,00, 0,50 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 0,00 +0,00, -1,00, 0,00 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 0,00 +0,00, 0,00, 0,50 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 0,50 +1,00, -1,00, 0,00 +Slope: 2 +LID +0 0 +0,00, -1,00, 0,50 +1,00, -1,00, 0,50 +1,00, 0,00, 1,00 +0,00, 0,00, 1,00 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 1,00 +1,00, 0,00, 1,00 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 0,50 +0,00, -1,00, 0,50 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 0,50 +0,00, 0,00, 1,00 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 1,00 +1,00, -1,00, 0,50 +Slope: 3 +LID +0 0 +0,00, -1,00, 0,50 +1,00, -1,00, 0,50 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 0,00 +1,00, 0,00, 0,00 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 0,50 +0,00, -1,00, 0,50 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 0,50 +0,00, 0,00, 0,00 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 0,00 +1,00, -1,00, 0,50 +Slope: 4 +LID +0 0 +0,00, -1,00, 1,00 +1,00, -1,00, 1,00 +1,00, 0,00, 0,50 +0,00, 0,00, 0,50 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 0,50 +1,00, 0,00, 0,50 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 1,00 +0,00, -1,00, 1,00 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 1,00 +0,00, 0,00, 0,50 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 0,50 +1,00, -1,00, 1,00 +Slope: 5 +LID +0 0 +0,00, -1,00, 0,50 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +0,00, 0,00, 0,50 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 0,50 +1,00, 0,00, 0,00 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 0,00 +0,00, -1,00, 0,50 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 0,50 +0,00, 0,00, 0,50 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 0,00 +1,00, -1,00, 0,00 +Slope: 6 +LID +0 0 +0,00, -1,00, 1,00 +1,00, -1,00, 0,50 +1,00, 0,00, 0,50 +0,00, 0,00, 1,00 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 1,00 +1,00, 0,00, 0,50 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 0,50 +0,00, -1,00, 1,00 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 1,00 +0,00, 0,00, 1,00 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 0,50 +1,00, -1,00, 0,50 +Slope: 7 +LID +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,50 +1,00, 0,00, 0,50 +0,00, 0,00, 0,00 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 0,00 +1,00, 0,00, 0,50 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 0,50 +0,00, -1,00, 0,00 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 0,00 +0,00, 0,00, 0,00 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 0,50 +1,00, -1,00, 0,50 +Slope: 8 +LID +0 0 +0,00, -1,00, 0,50 +1,00, -1,00, 1,00 +1,00, 0,00, 1,00 +0,00, 0,00, 0,50 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 0,50 +1,00, 0,00, 1,00 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 1,00 +0,00, -1,00, 0,50 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 0,50 +0,00, 0,00, 0,50 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 1,00 +1,00, -1,00, 1,00 +Slope: 9 +LID +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, 0,00, 0,13 +0,00, 0,00, 0,13 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 0,13 +1,00, 0,00, 0,13 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 0,00 +0,00, -1,00, 0,00 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 0,00 +0,00, 0,00, 0,13 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 0,13 +1,00, -1,00, 0,00 +Slope: 10 +LID +0 0 +0,00, -1,00, 0,13 +1,00, -1,00, 0,13 +1,00, 0,00, 0,25 +0,00, 0,00, 0,25 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 0,25 +1,00, 0,00, 0,25 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 0,13 +0,00, -1,00, 0,13 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 0,13 +0,00, 0,00, 0,25 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 0,25 +1,00, -1,00, 0,13 +Slope: 11 +LID +0 0 +0,00, -1,00, 0,25 +1,00, -1,00, 0,25 +1,00, 0,00, 0,38 +0,00, 0,00, 0,38 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 0,38 +1,00, 0,00, 0,38 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 0,25 +0,00, -1,00, 0,25 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 0,25 +0,00, 0,00, 0,38 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 0,38 +1,00, -1,00, 0,25 +Slope: 12 +LID +0 0 +0,00, -1,00, 0,38 +1,00, -1,00, 0,38 +1,00, 0,00, 0,50 +0,00, 0,00, 0,50 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 0,50 +1,00, 0,00, 0,50 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 0,38 +0,00, -1,00, 0,38 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 0,38 +0,00, 0,00, 0,50 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 0,50 +1,00, -1,00, 0,38 +Slope: 13 +LID +0 0 +0,00, -1,00, 0,50 +1,00, -1,00, 0,50 +1,00, 0,00, 0,63 +0,00, 0,00, 0,63 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 0,63 +1,00, 0,00, 0,63 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 0,50 +0,00, -1,00, 0,50 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 0,50 +0,00, 0,00, 0,63 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 0,63 +1,00, -1,00, 0,50 +Slope: 14 +LID +0 0 +0,00, -1,00, 0,63 +1,00, -1,00, 0,63 +1,00, 0,00, 0,75 +0,00, 0,00, 0,75 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 0,75 +1,00, 0,00, 0,75 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 0,63 +0,00, -1,00, 0,63 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 0,63 +0,00, 0,00, 0,75 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 0,75 +1,00, -1,00, 0,63 +Slope: 15 +LID +0 0 +0,00, -1,00, 0,75 +1,00, -1,00, 0,75 +1,00, 0,00, 0,88 +0,00, 0,00, 0,88 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 0,88 +1,00, 0,00, 0,88 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 0,75 +0,00, -1,00, 0,75 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 0,75 +0,00, 0,00, 0,88 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 0,88 +1,00, -1,00, 0,75 +Slope: 16 +LID +0 0 +0,00, -1,00, 0,88 +1,00, -1,00, 0,88 +1,00, 0,00, 1,00 +0,00, 0,00, 1,00 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 1,00 +1,00, 0,00, 1,00 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 0,88 +0,00, -1,00, 0,88 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 0,88 +0,00, 0,00, 1,00 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 1,00 +1,00, -1,00, 0,88 +Slope: 17 +LID +0 0 +0,00, -1,00, 0,13 +1,00, -1,00, 0,13 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 0,00 +1,00, 0,00, 0,00 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 0,13 +0,00, -1,00, 0,13 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 0,13 +0,00, 0,00, 0,00 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 0,00 +1,00, -1,00, 0,13 +Slope: 18 +LID +0 0 +0,00, -1,00, 0,25 +1,00, -1,00, 0,25 +1,00, 0,00, 0,13 +0,00, 0,00, 0,13 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 0,13 +1,00, 0,00, 0,13 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 0,25 +0,00, -1,00, 0,25 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 0,25 +0,00, 0,00, 0,13 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 0,13 +1,00, -1,00, 0,25 +Slope: 19 +LID +0 0 +0,00, -1,00, 0,38 +1,00, -1,00, 0,38 +1,00, 0,00, 0,25 +0,00, 0,00, 0,25 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 0,25 +1,00, 0,00, 0,25 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 0,38 +0,00, -1,00, 0,38 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 0,38 +0,00, 0,00, 0,25 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 0,25 +1,00, -1,00, 0,38 +Slope: 20 +LID +0 0 +0,00, -1,00, 0,50 +1,00, -1,00, 0,50 +1,00, 0,00, 0,38 +0,00, 0,00, 0,38 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 0,38 +1,00, 0,00, 0,38 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 0,50 +0,00, -1,00, 0,50 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 0,50 +0,00, 0,00, 0,38 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 0,38 +1,00, -1,00, 0,50 +Slope: 21 +LID +0 0 +0,00, -1,00, 0,63 +1,00, -1,00, 0,63 +1,00, 0,00, 0,50 +0,00, 0,00, 0,50 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 0,50 +1,00, 0,00, 0,50 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 0,63 +0,00, -1,00, 0,63 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 0,63 +0,00, 0,00, 0,50 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 0,50 +1,00, -1,00, 0,63 +Slope: 22 +LID +0 0 +0,00, -1,00, 0,75 +1,00, -1,00, 0,75 +1,00, 0,00, 0,63 +0,00, 0,00, 0,63 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 0,63 +1,00, 0,00, 0,63 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 0,75 +0,00, -1,00, 0,75 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 0,75 +0,00, 0,00, 0,63 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 0,63 +1,00, -1,00, 0,75 +Slope: 23 +LID +0 0 +0,00, -1,00, 0,88 +1,00, -1,00, 0,88 +1,00, 0,00, 0,75 +0,00, 0,00, 0,75 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 0,75 +1,00, 0,00, 0,75 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 0,88 +0,00, -1,00, 0,88 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 0,88 +0,00, 0,00, 0,75 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 0,75 +1,00, -1,00, 0,88 +Slope: 24 +LID +0 0 +0,00, -1,00, 1,00 +1,00, -1,00, 1,00 +1,00, 0,00, 0,88 +0,00, 0,00, 0,88 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 0,88 +1,00, 0,00, 0,88 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 1,00 +0,00, -1,00, 1,00 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 1,00 +0,00, 0,00, 0,88 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 0,88 +1,00, -1,00, 1,00 +Slope: 25 +LID +0 0 +0,00, -1,00, 0,13 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +0,00, 0,00, 0,13 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 0,13 +1,00, 0,00, 0,00 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 0,00 +0,00, -1,00, 0,13 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 0,13 +0,00, 0,00, 0,13 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 0,00 +1,00, -1,00, 0,00 +Slope: 26 +LID +0 0 +0,00, -1,00, 0,25 +1,00, -1,00, 0,13 +1,00, 0,00, 0,13 +0,00, 0,00, 0,25 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 0,25 +1,00, 0,00, 0,13 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 0,13 +0,00, -1,00, 0,25 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 0,25 +0,00, 0,00, 0,25 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 0,13 +1,00, -1,00, 0,13 +Slope: 27 +LID +0 0 +0,00, -1,00, 0,38 +1,00, -1,00, 0,25 +1,00, 0,00, 0,25 +0,00, 0,00, 0,38 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 0,38 +1,00, 0,00, 0,25 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 0,25 +0,00, -1,00, 0,38 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 0,38 +0,00, 0,00, 0,38 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 0,25 +1,00, -1,00, 0,25 +Slope: 28 +LID +0 0 +0,00, -1,00, 0,50 +1,00, -1,00, 0,38 +1,00, 0,00, 0,38 +0,00, 0,00, 0,50 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 0,50 +1,00, 0,00, 0,38 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 0,38 +0,00, -1,00, 0,50 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 0,50 +0,00, 0,00, 0,50 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 0,38 +1,00, -1,00, 0,38 +Slope: 29 +LID +0 0 +0,00, -1,00, 0,63 +1,00, -1,00, 0,50 +1,00, 0,00, 0,50 +0,00, 0,00, 0,63 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 0,63 +1,00, 0,00, 0,50 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 0,50 +0,00, -1,00, 0,63 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 0,63 +0,00, 0,00, 0,63 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 0,50 +1,00, -1,00, 0,50 +Slope: 30 +LID +0 0 +0,00, -1,00, 0,75 +1,00, -1,00, 0,63 +1,00, 0,00, 0,63 +0,00, 0,00, 0,75 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 0,75 +1,00, 0,00, 0,63 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 0,63 +0,00, -1,00, 0,75 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 0,75 +0,00, 0,00, 0,75 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 0,63 +1,00, -1,00, 0,63 +Slope: 31 +LID +0 0 +0,00, -1,00, 0,88 +1,00, -1,00, 0,75 +1,00, 0,00, 0,75 +0,00, 0,00, 0,88 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 0,88 +1,00, 0,00, 0,75 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 0,75 +0,00, -1,00, 0,88 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 0,88 +0,00, 0,00, 0,88 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 0,75 +1,00, -1,00, 0,75 +Slope: 32 +LID +0 0 +0,00, -1,00, 1,00 +1,00, -1,00, 0,88 +1,00, 0,00, 0,88 +0,00, 0,00, 1,00 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 1,00 +1,00, 0,00, 0,88 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 0,88 +0,00, -1,00, 1,00 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 1,00 +0,00, 0,00, 1,00 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 0,88 +1,00, -1,00, 0,88 +Slope: 33 +LID +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,13 +1,00, 0,00, 0,13 +0,00, 0,00, 0,00 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 0,00 +1,00, 0,00, 0,13 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 0,13 +0,00, -1,00, 0,00 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 0,00 +0,00, 0,00, 0,00 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 0,13 +1,00, -1,00, 0,13 +Slope: 34 +LID +0 0 +0,00, -1,00, 0,13 +1,00, -1,00, 0,25 +1,00, 0,00, 0,25 +0,00, 0,00, 0,13 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 0,13 +1,00, 0,00, 0,25 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 0,25 +0,00, -1,00, 0,13 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 0,13 +0,00, 0,00, 0,13 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 0,25 +1,00, -1,00, 0,25 +Slope: 35 +LID +0 0 +0,00, -1,00, 0,25 +1,00, -1,00, 0,38 +1,00, 0,00, 0,38 +0,00, 0,00, 0,25 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 0,25 +1,00, 0,00, 0,38 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 0,38 +0,00, -1,00, 0,25 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 0,25 +0,00, 0,00, 0,25 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 0,38 +1,00, -1,00, 0,38 +Slope: 36 +LID +0 0 +0,00, -1,00, 0,38 +1,00, -1,00, 0,50 +1,00, 0,00, 0,50 +0,00, 0,00, 0,38 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 0,38 +1,00, 0,00, 0,50 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 0,50 +0,00, -1,00, 0,38 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 0,38 +0,00, 0,00, 0,38 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 0,50 +1,00, -1,00, 0,50 +Slope: 37 +LID +0 0 +0,00, -1,00, 0,50 +1,00, -1,00, 0,63 +1,00, 0,00, 0,63 +0,00, 0,00, 0,50 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 0,50 +1,00, 0,00, 0,63 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 0,63 +0,00, -1,00, 0,50 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 0,50 +0,00, 0,00, 0,50 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 0,63 +1,00, -1,00, 0,63 +Slope: 38 +LID +0 0 +0,00, -1,00, 0,63 +1,00, -1,00, 0,75 +1,00, 0,00, 0,75 +0,00, 0,00, 0,63 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 0,63 +1,00, 0,00, 0,75 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 0,75 +0,00, -1,00, 0,63 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 0,63 +0,00, 0,00, 0,63 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 0,75 +1,00, -1,00, 0,75 +Slope: 39 +LID +0 0 +0,00, -1,00, 0,75 +1,00, -1,00, 0,88 +1,00, 0,00, 0,88 +0,00, 0,00, 0,75 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 0,75 +1,00, 0,00, 0,88 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 0,88 +0,00, -1,00, 0,75 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 0,75 +0,00, 0,00, 0,75 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 0,88 +1,00, -1,00, 0,88 +Slope: 40 +LID +0 0 +0,00, -1,00, 0,88 +1,00, -1,00, 1,00 +1,00, 0,00, 1,00 +0,00, 0,00, 0,88 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 0,88 +1,00, 0,00, 1,00 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 1,00 +0,00, -1,00, 0,88 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 0,88 +0,00, 0,00, 0,88 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 1,00 +1,00, -1,00, 1,00 +Slope: 41 +LID +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, 0,00, 1,00 +0,00, 0,00, 1,00 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 1,00 +1,00, 0,00, 1,00 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 1,00 +0,00, -1,00, 1,00 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 0,00 +0,00, 0,00, 1,00 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 1,00 +1,00, -1,00, 0,00 +Slope: 42 +LID +0 0 +0,00, -1,00, 1,00 +1,00, -1,00, 1,00 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 1,00 +1,00, 0,00, 1,00 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 1,00 +0,00, -1,00, 1,00 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 1,00 +0,00, 0,00, 0,00 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 0,00 +1,00, -1,00, 1,00 +Slope: 43 +LID +0 0 +0,00, -1,00, 1,00 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +0,00, 0,00, 1,00 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 1,00 +1,00, 0,00, 0,00 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 0,00 +0,00, -1,00, 1,00 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 1,00 +0,00, 0,00, 1,00 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 1,00 +1,00, -1,00, 1,00 +Slope: 44 +LID +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 1,00 +1,00, 0,00, 1,00 +0,00, 0,00, 0,00 +NORTH +0 0 +1,00, 0,00, 0,00 +0,00, 0,00, 0,00 +0,00, 0,00, 0,00 +1,00, 0,00, 1,00 +SOUTH +0 0 +0,00, -1,00, 0,00 +1,00, -1,00, 0,00 +1,00, -1,00, 1,00 +0,00, -1,00, 0,00 +WEST +0 0 +0,00, 0,00, 0,00 +0,00, -1,00, 0,00 +0,00, -1,00, 1,00 +0,00, 0,00, 1,00 +EAST +0 0 +1,00, -1,00, 0,00 +1,00, 0,00, 0,00 +1,00, 0,00, 1,00 +1,00, -1,00, 1,00 diff --git a/font_cache.cpp b/font_cache.cpp new file mode 100644 index 0000000..91fae9b --- /dev/null +++ b/font_cache.cpp @@ -0,0 +1,33 @@ +#include "font_cache.h" + +namespace OpenGTA { + FontCache::FontCache() { + } + + FontCache::~FontCache() { + } + + OpenGL::DrawableFont & FontCache::getFont(const std::string & file, + const uint32_t scale) { + FontMap::iterator i = findFont(file, scale); + if (i == loadedFonts.end()) { + OpenGL::DrawableFont* fnt = createFont(file, scale); + assert(fnt); + loadedFonts.insert(std::make_pair( + FontIdentifier(file, scale), fnt)); + return *fnt; + } + return *i->second; + } + + FontCache::FontMap::iterator FontCache::findFont(const std::string & file, const uint32_t & scale) { + return loadedFonts.find(FontIdentifier(file, scale)); + } + + OpenGL::DrawableFont* FontCache::createFont(const std::string & file, const uint32_t & scale) { + OpenGL::DrawableFont * fnt = new OpenGL::DrawableFont(); + fnt->setScale(scale); + fnt->loadFont(file); + return fnt; + } +} diff --git a/font_cache.h b/font_cache.h new file mode 100644 index 0000000..bfa2802 --- /dev/null +++ b/font_cache.h @@ -0,0 +1,44 @@ +#ifndef FONT_CACHE_H +#define FONT_CACHE_H +#include +#include +#include "gl_font.h" +#include "Singleton.h" + +namespace OpenGTA { + + class FontCache { + public: + FontCache(); + ~FontCache(); + OpenGL::DrawableFont & getFont(const std::string & file, const uint32_t scale); + + private: + struct FontIdentifier { + FontIdentifier(const std::string & f, const uint32_t s) : filename(f), scale(s) {} + const std::string filename; + const uint32_t scale; + bool operator == (const FontIdentifier & o) const { + if (scale == o.scale && filename == o.filename) + return true; + return false; + } + bool operator < (const FontIdentifier & o) const { + if (scale < o.scale) + return true; + if (scale > o.scale) + return false; + return filename < o.filename; + } + }; + typedef std::map FontMap; + FontMap loadedFonts; + FontMap::iterator findFont(const std::string & file, const uint32_t & scale); + OpenGL::DrawableFont* createFont(const std::string & file, const uint32_t & scale); + }; + + typedef Loki::SingletonHolder FontCacheHolder; +} + +#endif diff --git a/fx_sdt.h b/fx_sdt.h new file mode 100644 index 0000000..dc540e9 --- /dev/null +++ b/fx_sdt.h @@ -0,0 +1,60 @@ +/* derived from: + * + * grand theft auto .sdt format + * copyright 2002 Delfi delfi@volja.net + */ + +/************************************************************************ +* Copyright (c) 2005-2006 tok@openlinux.org.uk * +* * +* This software is provided as-is, without any express or implied * +* warranty. In no event will the authors be held liable for any * +* damages arising from the use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute * +* it freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must * +* not claim that you wrote the original software. If you use this * +* software in a product, an acknowledgment in the product documentation * +* would be appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must * +* not be misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source * +* distribution. * +************************************************************************/ +#ifndef OGTA_FX_SDT_H +#define OGTA_FX_SDT_H +#include +#include +#include + +namespace OpenGTA { + class SoundsDB { + public: + SoundsDB(); + SoundsDB(const std::string & sdt_file); + ~SoundsDB(); + void load(const std::string & sdt_file); + struct Entry { + Entry(PHYSFS_uint32, PHYSFS_uint32, PHYSFS_uint32); + PHYSFS_uint32 rawStart; + PHYSFS_uint32 rawSize; + PHYSFS_uint32 sampleRate; + }; + typedef PHYSFS_uint16 KeyType; + Entry & getEntry(KeyType key); + unsigned char* getBuffered(KeyType key); + + private: + void clear(); + typedef std::map MapType; + MapType knownEntries; + PHYSFS_file *dataFile; + }; +} + +#endif diff --git a/gfx_extract.cpp b/gfx_extract.cpp new file mode 100644 index 0000000..778fcf3 --- /dev/null +++ b/gfx_extract.cpp @@ -0,0 +1,247 @@ +/************************************************************************ +* Copyright (c) 2005-2006 tok@openlinux.org.uk * +* * +* This software is provided as-is, without any express or implied * +* warranty. In no event will the authors be held liable for any * +* damages arising from the use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute * +* it freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must * +* not claim that you wrote the original software. If you use this * +* software in a product, an acknowledgment in the product documentation * +* would be appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must * +* not be misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source * +* distribution. * +************************************************************************/ +#include +#include +#include +#include + +#ifdef DUMP_DELTA_DEBUG +#include +#include +#include +#endif + +#include "opengta.h" +#include "dataholder.h" +#include "m_exceptions.h" +#include "set.h" + +SDL_Surface* image = NULL; + +void at_exit() { + if (image) + SDL_FreeSurface(image); + PHYSFS_deinit(); + SDL_Quit(); +} + +void display_image(SDL_Surface* s) { + SDL_Surface *screen = SDL_SetVideoMode(640, 480, 32, SDL_DOUBLEBUF); + SDL_Event event; + SDL_BlitSurface(s, NULL, screen, NULL); + SDL_Flip(screen); + while (1) { + while (SDL_PollEvent(&event)) { + switch(event.type) { + case SDL_QUIT: + return; + case SDL_KEYDOWN: + if (event.key.keysym.sym == SDLK_ESCAPE) + return; + default: + break; + } + } + SDL_Delay(100); + } +} + +SDL_Surface* get_image(unsigned char* rp, unsigned int w, unsigned int h) { +#if SDL_BYTEORDER == SDL_BIG_ENDIAN +#define rmask 0xff000000 +#define gmask 0x00ff0000 +#define bmask 0x0000ff00 +#define amask 0x000000ff +#else +#define rmask 0x000000ff +#define gmask 0x0000ff00 +#define bmask 0x00ff0000 +#define amask 0xff000000 +#endif + SDL_Surface* s = SDL_CreateRGBSurface(SDL_SWSURFACE|SDL_SRCALPHA, + w, h, 32, rmask, gmask, bmask, amask); + SDL_LockSurface(s); + unsigned char* dst = static_cast(s->pixels); + for (unsigned int i=0; iw) << "x" << int(sprite->h) << + " with " << int(sprite->deltaCount) << " deltas" << std::endl; + image = get_image(graphics.getSpriteBitmap(idx, remap, delta), sprite->w, sprite->h); +#ifdef DUMP_DELTA_DEBUG + if (delta && !delta_set) { + std::cout << "dumping delta" << std::endl; + OpenGTA::GraphicsBase::DeltaInfo & dinfo = sprite->delta[delta-1]; + int dump_fd = open("delta.raw", O_WRONLY | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR); + write(dump_fd, dinfo.ptr, dinfo.size); + close(dump_fd); + } +#endif + break; + } + + if (!image) { + std::cerr << "Error: image pointer is NULL; aborting" << std::endl; + return 1; + } + if (mode & 1) { + display_image(image); + } + if (mode & 2) { + SDL_SaveBMP(image, "out.bmp"); + } + } + catch (Exception & e) { + std::cerr << "Exception occured: " << e.what() << std::endl; + return 1; + } + + + return 0; +} diff --git a/gl_base.cpp b/gl_base.cpp new file mode 100644 index 0000000..36a9d5f --- /dev/null +++ b/gl_base.cpp @@ -0,0 +1,85 @@ +/************************************************************************ +* Copyright (c) 2005-2006 tok@openlinux.org.uk * +* * +* This software is provided as-is, without any express or implied * +* warranty. In no event will the authors be held liable for any * +* damages arising from the use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute * +* it freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must * +* not claim that you wrote the original software. If you use this * +* software in a product, an acknowledgment in the product documentation * +* would be appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must * +* not be misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source * +* distribution. * +************************************************************************/ +#include "gl_base.h" +#include +#include "common_sdl_gl.h" +#include "log.h" + +using namespace Util; + +namespace OpenGL { + template void ImmediateRenderer::assertCorrectPrimitive(GLenum newType) { + if (!insideBegin) { + WARN << "Missing glBegin() call" << std::endl; + } + else { + if (currentPrimitiveType == newType) + return; + WARN << "Forcing switch of primitive type" << std::endl; + glEnd(); + } + glBegin(newType); + currentPrimitiveType = newType; + insideBegin = true; + } + + template void ImmediateRenderer::begin(GLenum type) { + // FIXME: check !insideBegin + currentPrimitiveType = type; + insideBegin = true; + } + template void ImmediateRenderer::end() { + insideBegin = false; + } + + template GLenum ImmediateRenderer::currentPrimitiveType = GL_POINTS; + + template<> void ImmediateRenderer::draw(const Quad2Int & quad) { + //assertCorrectPrimitive(quad.primitiveType); + for (int i = 0; i < 4; i++) + glVertex2i(quad.vertices[i][0], quad.vertices[i][1]); + } + template<> void ImmediateRenderer::draw(const ColoredQuad2Int & quad) { + //assertCorrectPrimitive(quad.primitiveType); + for (int i = 0; i < 4; i++) { + glColor3f(quad.colors[i][0], quad.colors[i][1], quad.colors[i][2]); + glVertex2i(quad.vertices[i][0], quad.vertices[i][1]); + } + } + template<> void ImmediateRenderer::draw(const FontQuad & quad) { + glBindTexture(GL_TEXTURE_2D, quad.texId); + glBegin(quad.primitiveType); + for (int i = 0; i < 4; i++) { + glTexCoord2f(quad.texCoords[i][0], quad.texCoords[i][1]); + glVertex2i(quad.vertices[i][0], quad.vertices[i][1]); + } + glEnd(); + } + + /* + template<> void ImmediateRenderer::draw(const OpenGTA::Map::BlockInfo & info) { + for (int i = 0; i < 4; ++i) { + } + } + */ +} diff --git a/gl_base.h b/gl_base.h new file mode 100644 index 0000000..ab5faf2 --- /dev/null +++ b/gl_base.h @@ -0,0 +1,94 @@ +/************************************************************************ +* Copyright (c) 2005-2006 tok@openlinux.org.uk * +* * +* This software is provided as-is, without any express or implied * +* warranty. In no event will the authors be held liable for any * +* damages arising from the use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute * +* it freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must * +* not claim that you wrote the original software. If you use this * +* software in a product, an acknowledgment in the product documentation * +* would be appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must * +* not be misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source * +* distribution. * +************************************************************************/ +#ifndef GL_BASE_H +#define GL_BASE_H +#include + +namespace OpenGL { + /* + template + struct TexOps + { + static GLuint create(); + static void bind(GLuint id); + static void destropy(GLuint id); + }; + */ + + template + struct Quad + { + vertex_type vertices[4][entries_per_vertex]; + static const GLenum primitiveType = GL_QUADS; + }; + template + struct QuadNormals + { + normal_type normals[4][entries_per_vertex]; + }; + template + struct QuadColors + { + color_type colors[4][num_colors]; + }; + template + struct QuadTexCoords + { + texcoord_type texCoords[4][num_coords]; + GLuint texId; + }; + + template + struct ColoredQuad : public Quad, + public QuadColors + { + }; + + template + struct TexturedQuad : public Quad, + public QuadTexCoords + { + }; + + typedef Quad Quad2Int; + typedef ColoredQuad ColoredQuad2Int; + typedef TexturedQuad FontQuad; + + + template class ImmediateRenderer + { + public: + static void draw(const T & t); + static void begin(GLenum type); + static void end(); + protected: + static GLenum currentPrimitiveType; + static bool insideBegin; + static void assertCorrectPrimitive(GLenum newType); + }; + + // ugly as hell, but seems to work +#define Renderer ImmediateRenderer + +} +#endif diff --git a/gl_camera.cpp b/gl_camera.cpp new file mode 100644 index 0000000..310262b --- /dev/null +++ b/gl_camera.cpp @@ -0,0 +1,255 @@ +#include "gl_camera.h" +#include "gl_screen.h" +#include "opengta.h" +#include "dataholder.h" +#include +#include "log.h" +#include "blockdata.h" + +using namespace OpenGTA; +float slope_height_offset(unsigned char slope_type, float dx, float dz); +namespace OpenGL { + +/* implementation mainly taken from: + * + * "Talk to me like I'm a 3 year old!" Programming Lessons + * by DigiBen (digiben@gametutorials.com) + * + */ + + Camera::Camera() : eye(), center(), up(), doRotate(false), + camGravity(false), gameCamMode(false), followTarget(¢er) { + interpolateStart = 0; + interpolateEnd = 0; + } + + void Camera::update_game(Uint32 ticks) { + Vector3D delta(center - *followTarget); + //INFO << delta.x << ", " << delta.y << ", " << delta.z << std::endl; + delta.y = 0; + center += -delta; + eye += -delta; + gluLookAt(eye.x, eye.y, eye.z, center.x, center.y, center.z, + up.x, up.y, up.z); + } + + void Camera::setFollowMode(const Vector3D & target) { + followTarget = ⌖ + //INFO << "following " << target.x << ", " << target.y << ", " << target.z << std::endl; + gameCamMode = 1; + } + + void Camera::releaseFollowMode() { + followTarget = ¢er; + gameCamMode = 0; + } + + void Camera::update(Uint32 ticks) { + if (gameCamMode) { + update_game(ticks); + return; + } + moveByMouse(); + + float x,y,z; + x = floor(eye.x); + y = floor(eye.y); + z = floor(eye.z); + int do_grav = 1; + float delta_y = 0; + if (camGravity && do_grav) { + center.y -= 0.1f * ticks/40.0f; + eye.y -= 0.1f * ticks/40.0f; + } + + OpenGTA::Map & map = OpenGTA::MapHolder::Instance().get(); + if (y < map.getNumBlocksAtNew(PHYSFS_uint8(x), PHYSFS_uint8(z)) && y > 0.0f) { + OpenGTA::Map::BlockInfo * block = map.getBlockAtNew(PHYSFS_uint8(x), PHYSFS_uint8(z), PHYSFS_uint8(y)); + if (block->blockType() > 0 && block->blockType() <= 5) { + float bz = slope_height_offset(block->slopeType(), eye.x - x, eye.z - z); + if (block->slopeType() == 0 && block->blockType() != 5) + bz -= 1; + //INFO << int(block->blockType()) << ", " << eye.y << ", " << + // eye.y - y << ", " << eye.y - y - bz << ", " << bz << std::endl; + float react_delta = 0.3f; + if (block->blockType() == 5) + react_delta = 0.0f; + if (eye.y - y - bz < react_delta) { + //do_grav = 0; + Vector3D new_eye(eye); + new_eye.y = y + bz + react_delta; + delta_y = new_eye.y - eye.y; + center.y = center.y - eye.y + new_eye.y; + eye.y = new_eye.y; + } + } + +#if 0 + if (y >= 1.0f) { + block = map.getBlockAtNew(PHYSFS_uint8(x), PHYSFS_uint8(z), PHYSFS_uint8(y-1)); + if (block->blockType() > 0) { + INFO << "below is " << int(block->blockType()) << std::endl; + center.y = center.y - eye.y + y + 0.3f; + eye.y = y + 0.3f; + } + } +#endif + } + y -= 1; + if (y < map.getNumBlocksAtNew(PHYSFS_uint8(x), PHYSFS_uint8(z)) && y > 0.0f) { + OpenGTA::Map::BlockInfo * block = map.getBlockAtNew(PHYSFS_uint8(x), PHYSFS_uint8(z), PHYSFS_uint8(y)); + if (block->blockType() == 5) { + float bz = slope_height_offset(block->slopeType(), eye.x - x, eye.z - z); + //INFO << eye.y << ", " << y << " bz " << bz << std::endl; + if (eye.y - y - bz < 0.4f) { + Vector3D new_eye(eye); + new_eye.y = y + bz + 0.4; + delta_y = new_eye.y - eye.y; + //INFO << "setting " << new_eye.y << std::endl; + center.y = center.y - eye.y + new_eye.y; + eye.y = new_eye.y; + do_grav = 0; + } + } + } + + /* + if (camGravity && do_grav) { + Vector dn(0, -0.1f*do_grav, 0); + eye += dn; + center += dn; + }*/ + /* + if (camGravity && do_grav) { + center.y -= 0.1f * ticks/40.0f; + eye.y -= 0.1f * ticks/40.0f; + }*/ + + if (interpolateStart) { + float as_one = float(interpolateStart - 1) / interpolateEnd; + Vector3D now = (1 - as_one) * interpolateFrom + as_one * interpolateTo; + center = center - eye + now; + eye = now; + + interpolateStart += ticks; + if (interpolateStart > interpolateEnd) + interpolateStart = 0; + } + else { + + if (speed > 0.01f || speed < -0.01f) { + + Vector3D v = center - eye; + if (camGravity) + v.y = delta_y; + v = v.Normalized(); + center += speed * ticks/40.0f * v; + eye += speed * ticks/40.0f * v; + //INFO << v.y << std::endl; + } + + if (doRotate) + rotateAround(Vector3D(center.x, 0, center.z), 0, 0.01f, 0); + } + gluLookAt(eye.x, eye.y, eye.z, center.x, center.y, center.z, + up.x, up.y, up.z); + } + + void Camera::setRotating(bool demo) { + doRotate = demo; + } + + void Camera::setCamGravity(bool demo) { + camGravity = demo; + } + + void Camera::setVectors(const Vector3D & e, const Vector3D & c, const Vector3D & u) { + eye = e; + center = c; + up = u; + } + + void Camera::setSpeed(float new_speed) { + speed = new_speed; + } + + void Camera::translateBy(const Vector3D & t) { + eye += t; + center += t; + } + + void Camera::translateTo(const Vector3D & e) { + Vector3D rel = eye - e; + translateBy(rel); + } + + void Camera::rotateView(float x, float y, float z) { + Vector3D v = center - eye; + if (x) { + center.z = eye.z + sin(x) * v.y + cos(x) * v.z; + center.y = eye.y + cos(x) * v.y - sin(x) * v.z; + } + if (y) { + center.z = eye.z + sin(y) * v.x + cos(y) * v.z; + center.x = eye.x + cos(y) * v.x - sin(y) * v.z; + } + if (z) { + center.x = eye.x + sin(z) * v.y + cos(z) * v.x; + center.y = eye.y + cos(z) * v.y - sin(z) * v.x; + } + } + + void Camera::rotateAround(const Vector3D & lookAt, float x, float y, float z) { + Vector3D v = eye - lookAt; + if (x) { + eye.z = lookAt.z + sin(x) * v.y + cos(x) * v.z; + eye.y = lookAt.y + cos(x) * v.y - sin(x) * v.z; + } + if (y) { + eye.z = lookAt.z + sin(y) * v.x + cos(y) * v.z; + eye.x = lookAt.x + cos(y) * v.x - sin(y) * v.z; + } + if (z) { + eye.x = lookAt.x + sin(z) * v.y + cos(z) * v.x; + eye.y = lookAt.y + cos(z) * v.y - sin(z) * v.x; + } + } + + void Camera::moveByMouse() { + Screen & screen = ScreenHolder::Instance(); + int w, h; + w = screen.getWidth() / 2; + h = screen.getHeight() / 2; + int mx, my; + SDL_GetMouseState(&mx, &my); + SDL_WarpMouse(w, h); + if ((mx == w) && (my == h)) + return; + float rot_x = (float(w) - mx) / 100; + float rot_y = (float(h) - my) / 100; + center.y += rot_y * 8; + if (center.y - eye.y > 15) + center.y = eye.y + 15; + else if (center.y - eye.y < -15) + center.y = eye.y - 15; + rotateView(0, -rot_x, 0); + } + + void Camera::interpolate(const Vector3D & to, const Uint32 & start, const Uint32 & end) { + interpolateFrom = eye; + interpolateTo = to; + interpolateStart = start; + interpolateEnd = end; + } + +#if 0 + void QuaternionCamera::update(Uint32 ticks) { + float cur_ratio = 0; + quat step = exp(sampleLinear(ln_start, ln_end, cur_ratio)); + mtx44 m = quatToRotationMatrix(step); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glMultMatrixf((float*)m.x); + } +#endif +} diff --git a/gl_camera.h b/gl_camera.h new file mode 100644 index 0000000..f315462 --- /dev/null +++ b/gl_camera.h @@ -0,0 +1,51 @@ +#ifndef GL_CAMERA_H +#define GL_CAMERA_H +#include +#include +#include "math3d.h" +#include "Singleton.h" + +namespace OpenGL { + + class Camera { + public: + Camera(); + void setSpeed(float forward_is_positive); + void setRotating(bool demo); + void setCamGravity(bool demo); + void rotateView(float x, float y, float z); + void rotateAround(const Vector3D & c, float x, float y, float z); + void translateBy(const Vector3D & t); + void translateTo(const Vector3D & e); + void setVectors(const Vector3D & e, const Vector3D & c, const Vector3D & u); + void moveByMouse(); + void interpolate(const Vector3D & to, const Uint32 & start, const Uint32 & end); + void setFollowMode(const Vector3D & target); + void releaseFollowMode(); + + void update(Uint32 dt); + Vector3D & getEye() { return eye; } + Vector3D & getCenter() { return center; } + Vector3D & getUp() { return up; } + private: + void update_game(Uint32 dt); + Vector3D eye; + Vector3D center; + Vector3D up; + float speed; + bool doRotate; + bool camGravity; + bool gameCamMode; + Vector3D const * followTarget; + + Vector3D interpolateFrom; + Vector3D interpolateTo; + Uint32 interpolateStart; + Uint32 interpolateEnd; + }; + + using namespace Loki; + typedef SingletonHolder CameraHolder; +} +#endif diff --git a/gl_cityview.cpp b/gl_cityview.cpp new file mode 100644 index 0000000..d6f2560 --- /dev/null +++ b/gl_cityview.cpp @@ -0,0 +1,1113 @@ +/************************************************************************ +* Copyright (c) 2005-2006 tok@openlinux.org.uk * +* * +* This software is provided as-is, without any express or implied * +* warranty. In no event will the authors be held liable for any * +* damages arising from the use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute * +* it freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must * +* not claim that you wrote the original software. If you use this * +* software in a product, an acknowledgment in the product documentation * +* would be appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must * +* not be misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source * +* distribution. * +************************************************************************/ +#include +#include +#include "gl_cityview.h" +#include "gl_spritecache.h" +#include "spritemanager.h" +#include "buffercache.h" +#include "gl_camera.h" +#include "dataholder.h" +#include "math3d.h" +#include "localplayer.h" +#include "log.h" +#include "gl_screen.h" +#include "blockdata.h" +#include "image_loader.h" + +float slope_height_offset(unsigned char slope_type, float dx, float dz); +namespace OpenGTA { + +/* + float slope_raw_data[45][5][4][3] = { +#include "slope1_data.h" + }; + float slope_tex_data[45][4][4][2] = { +#include "slope1_tcoords.h" + }; + + float lid_normal_data[45][3] = { +#include "lid_normal_data.h" + }; +*/ + +/* + GLuint createGLTexture(GLsizei w, GLsizei h, bool rgba, const void* pixels) { + GLuint tex; + glGenTextures(1, &tex); + glBindTexture(GL_TEXTURE_2D, tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + if (rgba) + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + //gluBuild2DMipmaps(GL_TEXTURE_2D, 4, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + else + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels); + //gluBuild2DMipmaps(GL_TEXTURE_2D, 3, w, h, GL_RGB, GL_UNSIGNED_BYTE, pixels); + + GL_CHECKERROR; + return tex; + } +*/ + + CityView::CityView() { + setNull(); + /* + Pedestrian p(Vector3D(0.5f, 0.5f, 0.5f), Vector3D(4, 5.01f, 4), 0xffffffff); + p.m_control = &LocalPlayer::Instance(); + SpriteManagerHolder::Instance().addPed(p); + */ + } + void CityView::setNull() { + loadedMap = NULL; + sideCache = NULL; + lidCache = NULL; + camVec[0] = 0.0f; + camVec[1] = 1.0f; + camVec[2] = 0.0f; + zoomLevel = 1.0f; + visibleRange = 15; + topDownView = true; + drawTextured = true; + drawLines = false; + setPosition(0.0f, 0.0f, 20.0f); + + scene_display_list = 0; + texFlipTest = 0; + drawHeadingMarkers = false; + current_sector = NULL; + } + CityView::~CityView() { + cleanup(); + } + void CityView::resetTextures() { + sideCache->clearAll(); + lidCache->clearAll(); + } + void CityView::setVisibleRange(int r) { + visibleRange = r; + scene_is_dirty = true; + } + int CityView::getVisibleRange() { + return visibleRange; + } + bool CityView::getDrawTextured() { return drawTextured; } + bool CityView::getDrawLines() { return drawLines; } + void CityView::setDrawTextured(bool v) { drawTextured = v; } + void CityView::setDrawLines(bool v) { drawLines= v; } + void CityView::cleanup() { + //if (loadedMap) + // delete loadedMap; + if (sideCache) + delete sideCache; + if (lidCache) + delete lidCache; + if (scene_display_list) + glDeleteLists(scene_display_list, 1); + setNull(); + } + void CityView::setViewMode(bool topDown) { + topDownView = topDown; + } + void CityView::loadMap(const std::string &map, const std::string &style_f) { + cleanup(); + //loadedMap = new Map(map); + MapHolder::Instance().load(map); + loadedMap = &MapHolder::Instance().get(); + StyleHolder::Instance().load(style_f); + style = &StyleHolder::Instance().get(); + sideCache = new OpenGL::TextureCache("SideCache"); + lidCache = new OpenGL::TextureCache("LidCache"); + sideCache->setClearMagic(5); + lidCache->setClearMagic(8); + scene_is_dirty = true; + lastCacheEmptyTicks = 0; + + scene_display_list = glGenLists(1); + + SpriteManagerHolder::Instance().clear(); + for (PHYSFS_uint16 oc = 0; oc < loadedMap->numObjects; oc++) { + createLevelObject(&loadedMap->objects[oc]); + } + } + void CityView::createLevelObject(OpenGTA::Map::ObjectPosition *obj) { + SpriteManager & s_man = SpriteManagerHolder::Instance(); + if (obj->remap >= 128) { + Car car(*obj); + s_man.addCar(car); + } + else { + GameObject gobj(*obj); + s_man.addObject(gobj); + } + } + void CityView::setZoom(const GLfloat zoom) { + zoomLevel = zoom; + } + void CityView::setPosition(const GLfloat & x, const GLfloat & y, const GLfloat & z) { + camPos[0] = x; + camPos[1] = y; + camPos[2] = z; + scene_is_dirty = true; + //INFO << "Position: " << x << ", " << z << " (" << y << ")" << std::endl; + if (loadedMap) { + PHYSFS_uint8 _x = PHYSFS_uint8((x >= 1.0f) ? ((x < 255.0f) ? x : 254) : 1); // FIXME: crashes on 0 or 255 + PHYSFS_uint8 _y = PHYSFS_uint8((z >= 1.0f) ? ((z < 255.0f) ? z : 254) : 1); // why??? + NavData::Sector* in_sector = loadedMap->nav->getSectorAt(_x, _y); + assert(in_sector); + if (in_sector != current_sector) { + current_sector = in_sector; + } + } + + //if (loadedMap && !topDownView) + // getTerrainHeight(camPos[0], camPos[1], camPos[2]); + } + + NavData::Sector* CityView::getCurrentSector() { + return current_sector; + } + +/* + void CityView::setCamVector(const GLfloat & x, const GLfloat & y, const GLfloat & z) { + camVec[1] = x; + camVec[2] = y; + camVec[3] = z; + scene_is_dirty = true; + } +*/ + + void CityView::setTopDownView(const GLfloat & height) { + camPos[1] = height; + scene_is_dirty = true; + setViewMode(true); + } + + void CityView::getTerrainHeight(GLfloat & x, GLfloat & y, GLfloat & z) { + int xi = int(x); + int yi = int(z); + //int zi = int(z); + float h = 0.5f; + PHYSFS_uint16 emptycount = loadedMap->getNumBlocksAt(xi, yi); + for (int c=6-emptycount; c >= 1; c--) { + OpenGTA::Map::BlockInfo* bi = loadedMap->getBlockAt(xi, yi, c); + if (bi->blockType() == 0) { + //camPos[1] = h; + break; + } + + /*INFO << "block " << c << " lid: " << int(bi->lid) << std::endl; + if (bi->blockType() > 0) { + INFO << "not air but: " << int(bi->blockType()) << std::endl; + }*/ + h += 1.0f; + } + y = h; + } + + OpenGL::PagedTexture CityView::renderMap2Texture() { + uint32_t width = OpenGL::ScreenHolder::Instance().getWidth(); + uint32_t height = OpenGL::ScreenHolder::Instance().getHeight(); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + OpenGL::ScreenHolder::Instance().set3DProjection(); + glRotatef(180, 0, 0, 1); + gluLookAt(128, 230, 128, 128, 0, 128, 0.0f, 0.0f, 1.0f); + glTranslatef(-42, 0, 0); + for (int i = 0; i <= 255; i++) { + for (int j= 0; j <= 255; j++) { + glPushMatrix(); + glTranslatef(1.0f*j, 0.0f, 1.0f*i); + PHYSFS_uint16 maxcount = loadedMap->getNumBlocksAtNew(j,i); + for (int c=0; c < maxcount; ++c) { + glPushMatrix(); + drawBlock(loadedMap->getBlockAtNew(j, i, c)); + glPopMatrix(); + glTranslatef(0.0f, 1.0f, 0.0f); + } + glPopMatrix(); + } + } + GL_CHECKERROR; + + uint32_t gl_h = 1; + while (gl_h < height) + gl_h <<= 1; + uint32_t img_size = gl_h * gl_h * 3; + uint8_t *img_buf = Util::BufferCacheHolder::Instance().requestBuffer(img_size); + + glReadBuffer(GL_BACK); + for (uint32_t i = 0; i < gl_h; i++) { + glReadPixels(0, i, gl_h, 1, GL_RGB, GL_UNSIGNED_BYTE, (GLvoid*)(img_buf + gl_h * 3 * i)); + } + GL_CHECKERROR; + + sideCache->sink(); + sideCache->sink(); + lidCache->sink(); + lidCache->sink(); + sideCache->clear(); + lidCache->clear(); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + GLuint tex = ImageUtil::createGLTexture(gl_h, gl_h, false, img_buf); + float f_h = float(height) / gl_h; + float f_w = float(width) / gl_h; + //float horiz_corr = (1.0f - f_w) / 2.0f; + //return OpenGL::PagedTexture(tex, 0+horiz_corr, 0, f_w+horiz_corr, f_h); + return OpenGL::PagedTexture(tex, 0, 0, f_w, f_h); + } + + void CityView::draw(Uint32 ticks) { + GL_CHECKERROR; + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + /* + sideCache->clearStats(); + lidCache->clearStats(); + */ + glLoadIdentity(); + int x1, y1, x2, y2; + if (topDownView) { + glRotatef(180, 0, 0, 1); + gluLookAt(camPos[0], camPos[1], camPos[2], camPos[0], 0.0f, camPos[2], 0.0f, 0.0f, 1.0f); + + x1 = int(camPos[0]) - visibleRange; + y1 = int(camPos[2]) - visibleRange; + x2 = int(camPos[0]) + visibleRange; + y2 = int(camPos[2]) + visibleRange; + + } + else { + OpenGL::Camera & cam = OpenGL::CameraHolder::Instance(); + //gluLookAt(camPos[0], camPos[1], camPos[2], camPos[0]+5, 0, (camPos[2])+5, camVec[0], camVec[1], camVec[2]); + cam.update(ticks); + Vector3D & e = cam.getEye(); + //INFO << "eye: " << e.x << ", " << e.y << ", " << e.z << std::endl; + setPosition(e.x, e.y, e.z); + x1 = int(e.x) - visibleRange; + y1 = int(e.z) - visibleRange; + x2 = int(e.x) + visibleRange; + y2 = int(e.z) + visibleRange; + //PHYSFS_uint16 emptycount = loadedMap->getNumBlocksAt(int(e.x),int(e.z)); + //INFO << "cam-h is " << int(e.y)<< " ec: " << emptycount << std::endl; +/* + int cc = 0; + for (int i = 6 - emptycount; i >= 0; i--) { + OpenGTA::Map::BlockInfo* bi = loadedMap->getBlockAt(int(e.x), int(e.z), i); + //INFO << i << " type: "<< int(bi->blockType()) << " lid: " << int(bi->lid)<< std::endl; + if (bi->blockType() >=2 && bi->blockType() <= 4) { + c.y = c.y - e.y + float(cc)+1.0f; + e.y = float(cc)+1.0f; + break; + } + cc++; + } +*/ + + //getTerrainHeight(e.x, e.y, e.z); + } + frustum.CalculateFrustum(); + + if (x1 < 0) + x1 = 0; + if (y1 < 0) + y1 = 0; + if (x2 > 255) + x2 = 255; + if (y2 > 255) + y2 = 255; + + bool use_display_list = false; + + GL_CHECKERROR; + if ((!scene_is_dirty) && (use_display_list)) { + glCallList(scene_display_list); + } + else { + scene_rendered_blocks = scene_rendered_vertices = 0; + + if (use_display_list) + glNewList(scene_display_list, GL_COMPILE); + for (int i = y1; i <= y2; i++) { + //glPushMatrix(); + //glTranslatef(0.0f, 0.0f, 1.0f*i); + for (int j= x1; j <= x2; j++) { + if (!frustum.BlockInFrustum(0.5f+j, 0.5f+i, 0.5f)) + continue; + glPushMatrix(); + glTranslatef(1.0f*j, 0.0f, 1.0f*i); + //PHYSFS_uint16 emptycount = loadedMap->getNumBlocksAt(j,i); + PHYSFS_uint16 maxcount = loadedMap->getNumBlocksAtNew(j,i); + //for (int c=6-emptycount; c >= 1; c--) { + for (int c=0; c < maxcount; ++c) { + ++scene_rendered_blocks; + glPushMatrix(); + drawBlock(loadedMap->getBlockAtNew(j, i, c)); + glPopMatrix(); + glTranslatef(0.0f, 1.0f, 0.0f); + } + glPopMatrix(); + } + //glPopMatrix(); + } + if (use_display_list) { + glEndList(); + glCallList(scene_display_list); + scene_is_dirty = false; + } + //INFO << scene_rendered_blocks << " blocks drawn with " << scene_rendered_vertices << " vertices" << std::endl; + } + GL_CHECKERROR; + /* + for (PHYSFS_uint16 oc = 0; oc < loadedMap->numObjects; oc++) { + if (frustum.BlockInFrustum((loadedMap->objects[oc].x >> 6) + 0.5f, + (loadedMap->objects[oc].y >> 6) + 0.5f, 0.5f)) + drawObject(&loadedMap->objects[oc]); + }*/ + SDL_Rect r; + r.x = x1; + r.y = y1; + r.w = x2 - x1; + r.h = y2 - y1; + //INFO << "active rect: " << r.x << "," < 4000) { + lidCache->sink(); + lidCache->sink(); + sideCache->sink(); + sideCache->clear(); + lidCache->clear(); + lastCacheEmptyTicks = 0; + //lidCache->status(); + //sideCache->status(); + } + GL_CHECKERROR; + } + +#if 0 + OpenGL::PagedTexture CityView::createSprite(size_t sprite_num, GraphicsBase::SpriteInfo* info) { + unsigned char* src = style->getSpriteBitmap(sprite_num, -1 , 0); + unsigned int glwidth = 1; + unsigned int glheight = 1; + + while(glwidth < info->w) + glwidth <<= 1; + while(glheight < info->h) + glheight <<= 1; + unsigned char* dst = Util::BufferCacheHolder::Instance().requestBuffer(glwidth * glheight * 4); + Util::BufferCacheHolder::Instance().unlockBuffer(src); + assert(dst != NULL); + unsigned char * t = dst; + unsigned char * r = src; + for (unsigned int i = 0; i < info->h; i++) { + memcpy(t, r, info->w * 4); + t += glwidth * 4; + r += info->w * 4; + } + GLuint texid; + glGenTextures(1, &texid); + glBindTexture(GL_TEXTURE_2D, texid); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, glwidth, glheight, 0, GL_RGBA, GL_UNSIGNED_BYTE, dst); + return OpenGL::PagedTexture(texid, 0, 0, + float(info->w)/float(glwidth), float(info->h)/float(glheight)); + } +#endif + + void CityView::drawObject(OpenGTA::Map::ObjectPosition* obj) { + + float w, h; + float x = float(obj->x >> 6) + float(obj->x % 64)/64.0f; + float y = float(obj->y >> 6) + float(obj->y % 64)/64.0f; + float z = float(obj->z >> 6) + float(obj->z % 64)/64.0f; + size_t spriteNumAbs, sprNum; + GraphicsBase::SpriteInfo *info = NULL; + GraphicsBase::SpriteNumbers::SpriteTypes st; + if (obj->remap >= 128) { // car + GraphicsBase::CarInfo* cinfo = style->findCarByModel(obj->type); + assert(cinfo); + sprNum = cinfo->sprNum; + spriteNumAbs = style->spriteNumbers.reIndex(cinfo->sprNum, GraphicsBase::SpriteNumbers::CAR); + info = style->getSprite(spriteNumAbs); + w = float(info->w) / 64.0f; + h = float(info->h) / 64.0f; + st = GraphicsBase::SpriteNumbers::CAR; + } + else { + spriteNumAbs = style->spriteNumbers.reIndex(style->objectInfos[obj->type]->sprNum, GraphicsBase::SpriteNumbers::OBJECT); + sprNum = style->objectInfos[obj->type]->sprNum; + info = style->getSprite(spriteNumAbs); + assert(info); + w = float(info->w) / 64.0f; + h = float(info->h) / 64.0f; + st = GraphicsBase::SpriteNumbers::OBJECT; + } + + OpenGL::PagedTexture t; + if (OpenGL::SpriteCacheHolder::Instance().has(spriteNumAbs)) + t = OpenGL::SpriteCacheHolder::Instance().get(spriteNumAbs); + else { + //t = createSprite(spriteNum, info); + //OpenGL::SpriteCacheHolder::Instance().add(spriteNum, t); + t = OpenGL::SpriteCacheHolder::Instance().create(sprNum, st, -1); + } + + glPushMatrix(); + glTranslatef(x, 6.1f - z, y); // ups, seems: up<->down + glRotatef(obj->rotation / 1035.0f * 360.0f, 0, 1, 0); // 1035 is guesswork! + //glDisable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, t.inPage); + glBegin(GL_QUADS); + glTexCoord2f(t.coords[0].u, t.coords[1].v); + glVertex3f(-w/2, 0.0f, h/2); + glTexCoord2f(t.coords[1].u, t.coords[1].v); + glVertex3f(w/2, 0.0f, h/2); + glTexCoord2f(t.coords[1].u, t.coords[0].v); + glVertex3f(w/2, 0.0f, -h/2); + glTexCoord2f(t.coords[0].u, t.coords[0].v); + glVertex3f(-w/2, 0.0f, -h/2); + glEnd(); + glPopMatrix(); + //glEnable(GL_TEXTURE_2D); + } + + void CityView::drawBlock(OpenGTA::Map::BlockInfo* bi) { + + GL_CHECKERROR; + /* + std::cout << "is flat: " << bi->isFlat() << " up ok: " << bi->upOk() << + " down " << bi->downOk() << " left " << bi->leftOk() << " right " << bi->rightOk() << std::endl; + std::cout << "block type: "<< int(bi->blockType()) << " slope " << int(bi->slopeType()) << + " rotation: " << int(bi->rotation()) << " remap: " << int(bi->remapIndex()) << + " TB " << bi->flipTopBottom() << " LR " << bi->flipLeftRight() << std::endl; + */ + uint8_t which = bi->slopeType(); + float nx = LID_NORMAL_DATA[which][0]; + float ny = LID_NORMAL_DATA[which][1]; + float nz = LID_NORMAL_DATA[which][2]; + + int jj = 0; + GLfloat lidTex[8] = {0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f}; + //GLfloat sideTex1_bak[8] = {1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f}; + GLfloat sideTex1[8] = {1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f}; + +#ifdef MSWAP +#undef MSWAP +#endif +#define MSWAP(a,b) { GLfloat tmp = sideTex1[a]; sideTex1[a] = sideTex1[b]; sideTex1[b] = tmp; } + +#define SLOPE_TEX_CP(k) memcpy(sideTex1, SLOPE_TEX_DATA[which][k], 8 * sizeof(GLfloat)) +#ifdef GLTEX_HELPER +#undef GLTEX_HELPER +#endif +#define GLTEX_HELPER \ + glTexCoord2f(lidTex[jj], lidTex[jj+1]); jj += 2; if (jj > 6) { jj = 0; } +#define GLTEX1_HELPER \ + glTexCoord2f(sideTex1[jj], sideTex1[jj+1]); jj += 2; if (jj > 6) { jj = 0; } + + bool is_flat = bi->isFlat(); // transparency in the texture ? + GLuint lid_tex = 0, left_tex = 0, right_tex = 0, top_tex = 0, bottom_tex = 0; + + // FIXME: no remaps used! + if (bi->lid) { + if (!lidCache->hasTexture(bi->lid)) { + lid_tex = ImageUtil::createGLTexture(64, 64, is_flat, + style->getLid(static_cast(bi->lid), 0, is_flat)); + lidCache->addTexture(bi->lid, lid_tex); + } + else + lid_tex = lidCache->getTextureWithId(bi->lid); + } + if (bi->left) { + if (!sideCache->hasTexture(bi->left)) { + left_tex = ImageUtil::createGLTexture(64, 64, is_flat, + style->getSide(static_cast(bi->left), 0, is_flat)); + sideCache->addTexture(bi->left, left_tex); + } + else + left_tex = sideCache->getTextureWithId(bi->left); + } + if (bi->right) { + if (!sideCache->hasTexture(bi->right)) { + right_tex = ImageUtil::createGLTexture(64, 64, is_flat, + style->getSide(static_cast(bi->right), 0, is_flat)); + sideCache->addTexture(bi->right, right_tex); + } + else + right_tex = sideCache->getTextureWithId(bi->right); + } + if (bi->top) { + if (!sideCache->hasTexture(bi->top)) { + top_tex = ImageUtil::createGLTexture(64, 64, is_flat, + style->getSide(static_cast(bi->top), 0, is_flat)); + sideCache->addTexture(bi->top, top_tex); + } + else + top_tex = sideCache->getTextureWithId(bi->top); + } + if (bi->bottom) { + if (!sideCache->hasTexture(bi->bottom)) { + bottom_tex = ImageUtil::createGLTexture(64, 64, is_flat, + style->getSide(static_cast(bi->bottom), 0, is_flat)); + sideCache->addTexture(bi->bottom, bottom_tex); + } + else + bottom_tex = sideCache->getTextureWithId(bi->bottom); + } + + // handle flat/transparent case + if (is_flat) { + if (bi->lid) { + jj = 0; + if (bi->typeMap & 16384) { + jj += 2; + } + if (bi->typeMap & 32768) { + jj += 4; + } + if(drawTextured) { + glBindTexture(GL_TEXTURE_2D, lid_tex); + glBegin(GL_QUADS); + glNormal3f(nx, ny, nz); + for (int j=0; j < 4; j++) { + GLTEX_HELPER; + scene_rendered_vertices += 1; + glVertex3f(SLOPE_RAW_DATA[which][0][j][0], + SLOPE_RAW_DATA[which][0][j][1], + SLOPE_RAW_DATA[which][0][j][2]); + } + glEnd(); + } + // lines + if (drawLines) { + glDisable(GL_TEXTURE_2D); + glBegin(GL_LINE_STRIP); + for (int j=0; j < 4; j++) { + glVertex3f(SLOPE_RAW_DATA[which][0][j][0], + SLOPE_RAW_DATA[which][0][j][1], + SLOPE_RAW_DATA[which][0][j][2]); + } + glVertex3f(SLOPE_RAW_DATA[which][0][0][0], + SLOPE_RAW_DATA[which][0][0][1], + SLOPE_RAW_DATA[which][0][0][2]); + glEnd(); + glEnable(GL_TEXTURE_2D); + // end-of-lines + } + } +#undef MSWAP +#define MSWAP(a, b) { sideTex1[a] = 1.0f - sideTex1[a]; sideTex1[b] = 1.0f - sideTex1[b]; } + jj = 0; // only 'lid' rotated + if (bi->top) { + SLOPE_TEX_CP(1); + if (bi->flipTopBottom()) { + MSWAP(0, 2); + MSWAP(4, 6); + } + if (drawTextured) { + glBindTexture(GL_TEXTURE_2D, top_tex); + glBegin(GL_QUADS); + for (int j=0; j < 4; j++) { + GLTEX1_HELPER; + scene_rendered_vertices += 1; + glVertex3f(SLOPE_RAW_DATA[which][2][j][0], + SLOPE_RAW_DATA[which][2][j][1], + SLOPE_RAW_DATA[which][2][j][2]); + } + jj = 0; + SLOPE_TEX_CP(0); + if (!bi->flipTopBottom()) { + MSWAP(0, 2); + MSWAP(4, 6); + } + for (int j=0; j < 4; j++) { + GLTEX1_HELPER; + scene_rendered_vertices += 1; + glVertex3f(SLOPE_RAW_DATA[which][1][j][0], + SLOPE_RAW_DATA[which][1][j][1], + SLOPE_RAW_DATA[which][1][j][2]-1.001f); // -1.001f + // offset by .001 to fix NYC bridges; polygon z-fighting + } + glEnd(); + } + // lines + if (drawLines) { + glDisable(GL_TEXTURE_2D); + glBegin(GL_LINE_STRIP); + for (int j=0; j < 4; j++) { + glVertex3f(SLOPE_RAW_DATA[which][2][j][0], + SLOPE_RAW_DATA[which][2][j][1], + SLOPE_RAW_DATA[which][2][j][2]); + } + glVertex3f(SLOPE_RAW_DATA[which][2][0][0], + SLOPE_RAW_DATA[which][2][0][1], + SLOPE_RAW_DATA[which][2][0][2]); + for (int j=0; j < 4; j++) { + glVertex3f(SLOPE_RAW_DATA[which][1][j][0], + SLOPE_RAW_DATA[which][1][j][1], + SLOPE_RAW_DATA[which][1][j][2]-1.001f); + } + glVertex3f(SLOPE_RAW_DATA[which][1][0][0], + SLOPE_RAW_DATA[which][1][0][1], + SLOPE_RAW_DATA[which][1][0][2]-1.001f); + glEnd(); + glEnable(GL_TEXTURE_2D); + // end-of-lines + } + } + jj = 0; + //memcpy(sideTex1, sideTex1_bak, 8*sizeof(GLfloat)); + if (bi->left) { + SLOPE_TEX_CP(2); + if (bi->flipLeftRight()) { + MSWAP(0, 2); + MSWAP(4, 6); + } + if (drawTextured) { + glBindTexture(GL_TEXTURE_2D, left_tex); + glBegin(GL_QUADS); + for (int j=0; j < 4; j++) { + GLTEX1_HELPER; + scene_rendered_vertices += 1; + glVertex3f(SLOPE_RAW_DATA[which][3][j][0], + SLOPE_RAW_DATA[which][3][j][1], + SLOPE_RAW_DATA[which][3][j][2]); + } + jj = 0; + SLOPE_TEX_CP(3); + if (!bi->flipLeftRight()) { + MSWAP(0, 2); + MSWAP(4, 6); + } + for (int j=0; j < 4; j++) { + GLTEX1_HELPER; + scene_rendered_vertices += 1; + glVertex3f(SLOPE_RAW_DATA[which][4][j][0]-1.001f, // -1.001f + SLOPE_RAW_DATA[which][4][j][1], + SLOPE_RAW_DATA[which][4][j][2]); + } + glEnd(); + } + // lines + if (drawLines) { + glDisable(GL_TEXTURE_2D); + glBegin(GL_LINE_STRIP); + for (int j=0; j < 4; j++) { + glVertex3f(SLOPE_RAW_DATA[which][3][j][0], + SLOPE_RAW_DATA[which][3][j][1], + SLOPE_RAW_DATA[which][3][j][2]); + } + glVertex3f(SLOPE_RAW_DATA[which][3][0][0], + SLOPE_RAW_DATA[which][3][0][1], + SLOPE_RAW_DATA[which][3][0][2]); + for (int j=0; j < 4; j++) { + glVertex3f(SLOPE_RAW_DATA[which][4][j][0]-1.001f, + SLOPE_RAW_DATA[which][4][j][1], + SLOPE_RAW_DATA[which][4][j][2]); + } + glVertex3f(SLOPE_RAW_DATA[which][4][0][0]-1.001f, + SLOPE_RAW_DATA[which][4][0][1], + SLOPE_RAW_DATA[which][4][0][2]); + glEnd(); + glEnable(GL_TEXTURE_2D); + // end-of-lines + } + } + } + else { + if (bi->lid) { + if (drawTextured) { + glBindTexture(GL_TEXTURE_2D, lid_tex); + jj = 0; + if (bi->typeMap & 16384) { + jj += 2; + } + if (bi->typeMap & 32768) { + jj += 4; + } + glBegin(GL_QUADS); + glNormal3f(nx, ny, nz); + for (int j=0; j < 4; j++) { + GLTEX_HELPER; + scene_rendered_vertices += 1; + glVertex3f(SLOPE_RAW_DATA[which][0][j][0], + SLOPE_RAW_DATA[which][0][j][1], + SLOPE_RAW_DATA[which][0][j][2]); + } + glEnd(); + } + // lines + if (drawLines) { + glDisable(GL_TEXTURE_2D); + glBegin(GL_LINE_STRIP); + for (int j=0; j < 4; j++) { + glVertex3f(SLOPE_RAW_DATA[which][0][j][0], + SLOPE_RAW_DATA[which][0][j][1], + SLOPE_RAW_DATA[which][0][j][2]); + } + glVertex3f(SLOPE_RAW_DATA[which][0][0][0], + SLOPE_RAW_DATA[which][0][0][1], + SLOPE_RAW_DATA[which][0][0][2]); + glEnd(); + glEnable(GL_TEXTURE_2D); + // end-of-lines + } + } + jj = 0; // only 'lid' rotated +#undef MSWAP +#define MSWAP(a, b) { sideTex1[a] = 1.0f - sideTex1[a]; sideTex1[b] = 1.0f - sideTex1[b]; } + /* + if (bi->flipTopBottom()) { + //MSWAP(1, 5); + //MSWAP(3, 7); + + MSWAP(0, 2); + MSWAP(4, 6); + }*/ + if (bi->top && (which != 42)) { + if (drawTextured) { + glBindTexture(GL_TEXTURE_2D, top_tex); + glBegin(GL_QUADS); + SLOPE_TEX_CP(1); + if (bi->flipTopBottom()) { + MSWAP(0, 2); + MSWAP(4, 6); + } + for (int j=0; j < 4; j++) { + //if (!bi->flipTopBottom()) + GLTEX1_HELPER; + /* + glTexCoord2f(SLOPE_TEX_DATA[which][1][j][0], + SLOPE_TEX_DATA[which][1][j][1]); + */ + scene_rendered_vertices += 1; + glVertex3f(SLOPE_RAW_DATA[which][2][j][0], + SLOPE_RAW_DATA[which][2][j][1], + SLOPE_RAW_DATA[which][2][j][2]); + } + glEnd(); + } + // lines + if (drawLines) { + glDisable(GL_TEXTURE_2D); + glBegin(GL_LINE_STRIP); + for (int j=0; j < 4; j++) { + glVertex3f(SLOPE_RAW_DATA[which][2][j][0], + SLOPE_RAW_DATA[which][2][j][1], + SLOPE_RAW_DATA[which][2][j][2]); + } + glVertex3f(SLOPE_RAW_DATA[which][2][0][0], + SLOPE_RAW_DATA[which][2][0][1], + SLOPE_RAW_DATA[which][2][0][2]); + glEnd(); + glEnable(GL_TEXTURE_2D); + // end-of-lines + } + } + jj = 0; + if (bi->bottom && (which != 41)) { + if (drawTextured) { + glBindTexture(GL_TEXTURE_2D, bottom_tex); + glBegin(GL_QUADS); + SLOPE_TEX_CP(0); + if (bi->flipTopBottom()) { + MSWAP(0, 2); + MSWAP(4, 6); + } + + for (int j=0; j < 4; j++) { + //if (!bi->flipTopBottom()) + GLTEX1_HELPER; + /* + glTexCoord2f(SLOPE_TEX_DATA[which][0][j][0], + SLOPE_TEX_DATA[which][0][j][1]); + */ + scene_rendered_vertices += 1; + glVertex3f(SLOPE_RAW_DATA[which][1][j][0], + SLOPE_RAW_DATA[which][1][j][1], + SLOPE_RAW_DATA[which][1][j][2]); + } + glEnd(); + } + // lines + if (drawLines) { + glDisable(GL_TEXTURE_2D); + glBegin(GL_LINE_STRIP); + for (int j=0; j < 4; j++) { + glVertex3f(SLOPE_RAW_DATA[which][1][j][0], + SLOPE_RAW_DATA[which][1][j][1], + SLOPE_RAW_DATA[which][1][j][2]); + } + glVertex3f(SLOPE_RAW_DATA[which][1][0][0], + SLOPE_RAW_DATA[which][1][0][1], + SLOPE_RAW_DATA[which][1][0][2]); + glEnd(); + glEnable(GL_TEXTURE_2D); + // end-of-lines + } + } + /*memcpy(sideTex1, sideTex1_bak, 8 * sizeof(GLfloat)); + if (bi->flipLeftRight()) { + MSWAP(0, 2); + MSWAP(4, 6); + }*/ + + jj = 0; + if (bi->left && (which != 44)) { + if (drawTextured) { + glBindTexture(GL_TEXTURE_2D, left_tex); + glBegin(GL_QUADS); + SLOPE_TEX_CP(2); + if (bi->flipLeftRight()) { + MSWAP(0, 2); + MSWAP(4, 6); + } + + for (int j=0; j < 4; j++) { + //if (!bi->flipLeftRight()) + GLTEX1_HELPER; + /* + glTexCoord2f(SLOPE_TEX_DATA[which][2][j][0], + SLOPE_TEX_DATA[which][2][j][1]); + */ + scene_rendered_vertices += 1; + glVertex3f(SLOPE_RAW_DATA[which][3][j][0], + SLOPE_RAW_DATA[which][3][j][1], + SLOPE_RAW_DATA[which][3][j][2]); + } + glEnd(); + } + // lines + if (drawLines) { + glDisable(GL_TEXTURE_2D); + glBegin(GL_LINE_STRIP); + for (int j=0; j < 4; j++) { + glVertex3f(SLOPE_RAW_DATA[which][3][j][0], + SLOPE_RAW_DATA[which][3][j][1], + SLOPE_RAW_DATA[which][3][j][2]); + } + glVertex3f(SLOPE_RAW_DATA[which][3][0][0], + SLOPE_RAW_DATA[which][3][0][1], + SLOPE_RAW_DATA[which][3][0][2]); + glEnd(); + glEnable(GL_TEXTURE_2D); + // end-of-lines + } + } + jj = 0; + if (bi->right && (which != 43)) { + if (drawTextured) { + glBindTexture(GL_TEXTURE_2D, right_tex); + glBegin(GL_QUADS); + SLOPE_TEX_CP(3); + if (bi->flipLeftRight()) { + MSWAP(0, 2); + MSWAP(4, 6); + } + + for (int j=0; j < 4; j++) { + //if (!bi->flipLeftRight()) + GLTEX1_HELPER; + /* + glTexCoord2f(SLOPE_TEX_DATA[which][3][j][0], + SLOPE_TEX_DATA[which][3][j][1]); + */ + scene_rendered_vertices += 1; + glVertex3f(SLOPE_RAW_DATA[which][4][j][0], + SLOPE_RAW_DATA[which][4][j][1], + SLOPE_RAW_DATA[which][4][j][2]); + } + glEnd(); + } + // lines + if (drawLines) { + glDisable(GL_TEXTURE_2D); + glBegin(GL_LINE_STRIP); + for (int j=0; j < 4; j++) { + glVertex3f(SLOPE_RAW_DATA[which][4][j][0], + SLOPE_RAW_DATA[which][4][j][1], + SLOPE_RAW_DATA[which][4][j][2]); + } + glVertex3f(SLOPE_RAW_DATA[which][4][0][0], + SLOPE_RAW_DATA[which][4][0][1], + SLOPE_RAW_DATA[which][4][0][2]); + glEnd(); + glEnable(GL_TEXTURE_2D); + // end-of-lines + } + } + + +#if 0 + + for (int i = 0; i < 5; ++i) { // RESEARCH ME: is this correct? needed above as well? + if ((which == 41) && (i == 1)) + continue; + if ((which == 42) && (i == 2)) + continue; + if ((which == 43) && (i == 4)) + continue; + if ((which == 44) && (i == 3)) + continue; + + jj = 0; + if (i == 0) + if (!bi->lid) + continue; + else { + glBindTexture(GL_TEXTURE_2D, lidCache->getTextureWithId(bi->lid)); + if (bi->typeMap & 16384) { + jj += 2; + } + if (bi->typeMap & 32768) { + jj += 4; + } + } + + if (i == 2) + if (!bi->top) + continue; + else + glBindTexture(GL_TEXTURE_2D, sideCache->getTextureWithId(bi->top)); + if (i == 1) + if (!bi->bottom) + continue; + else + glBindTexture(GL_TEXTURE_2D, sideCache->getTextureWithId(bi->bottom)); + if (i == 4) + if (!bi->right) + continue; + else + glBindTexture(GL_TEXTURE_2D, sideCache->getTextureWithId(bi->right)); + if (i == 3) + if (!bi->left) + continue; + else + glBindTexture(GL_TEXTURE_2D, sideCache->getTextureWithId(bi->left)); + + glBegin(GL_QUADS); + // FIXME: normals are completely fucked up + switch(i) { + case 0: + glNormal3f(nx, ny, nz); + break; + case 1: + glNormal3f(0.0f, 0.0f, 1.0f); + break; + case 2: + glNormal3f(0.0f, 0.0f, -1.0f); + break; + case 3: + glNormal3f(-1.0f, 0.0f, 0.0f); + break; + case 4: + glNormal3f(1.0f, 0.0f, 0.0f); + break; + } + for (int j=0; j < 4; j++) { + GLTEX_HELPER; + scene_rendered_vertices += 3; + glVertex3f(SLOPE_RAW_DATA[which][i][j][0], + SLOPE_RAW_DATA[which][i][j][1], + SLOPE_RAW_DATA[which][i][j][2]); + } + glEnd(); + } +#endif + } // else + + if (!drawHeadingMarkers) + return; + float offset_correction = 0.1f; + if (which == 0) + offset_correction = -0.9f; + //glDisable(GL_LIGHTING); + glDisable(GL_TEXTURE_2D); +#define HEIGHT_VERTEX(a, b, c) glVertex3f(a, offset_correction + slope_height_offset(which, a, c), c) + if (bi->downOk()) { + glBegin(GL_LINES); + HEIGHT_VERTEX(0.5f, 0.1f, 0.2f); + HEIGHT_VERTEX(0.5f, 0.1f, 0.8f); + HEIGHT_VERTEX(0.45f, 0.1f, 0.7f); + HEIGHT_VERTEX(0.5f, 0.1f, 0.8f); + HEIGHT_VERTEX(0.55f, 0.1f, 0.7f); + HEIGHT_VERTEX(0.5f, 0.1f, 0.8f); + glEnd(); + } + if (bi->upOk()) { + glBegin(GL_LINES); + HEIGHT_VERTEX(0.5f, 0.1f, 0.8f); + HEIGHT_VERTEX(0.5f, 0.1f, 0.2f); + HEIGHT_VERTEX(0.45f, 0.1f, 0.3f); + HEIGHT_VERTEX(0.5f, 0.1f, 0.2f); + HEIGHT_VERTEX(0.55f, 0.1f, 0.3f); + HEIGHT_VERTEX(0.5f, 0.1f, 0.2f); + glEnd(); + } + if (bi->leftOk()) { + glBegin(GL_LINES); + HEIGHT_VERTEX(0.2f, 0.1f, 0.5f); + HEIGHT_VERTEX(0.8f, 0.1f, 0.5f); + HEIGHT_VERTEX(0.3f, 0.1f, 0.45f); + HEIGHT_VERTEX(0.2f, 0.1f, 0.5f); + HEIGHT_VERTEX(0.3f, 0.1f, 0.55f); + HEIGHT_VERTEX(0.2f, 0.1f, 0.5f); + glEnd(); + } + if (bi->rightOk()) { + glBegin(GL_LINES); + HEIGHT_VERTEX(0.8f, 0.1f, 0.5f); + HEIGHT_VERTEX(0.2f, 0.1f, 0.5f); + HEIGHT_VERTEX(0.7f, 0.1f, 0.45f); + HEIGHT_VERTEX(0.8f, 0.1f, 0.5f); + HEIGHT_VERTEX(0.7f, 0.1f, 0.55f); + HEIGHT_VERTEX(0.8f, 0.1f, 0.5f); + glEnd(); + } + glEnable(GL_TEXTURE_2D); + // block lid normals +#if 0 +#define NORMAL_POS(a, b) glVertex3f(a, slope_height_offset(which, a, b), b) +#define NORMAL_POS2(a, b) glVertex3f(a + nx, slope_height_offset(which, a, b) + ny, b + nz) + glBegin(GL_LINES); + if (bi->lid) { + NORMAL_POS(0.5f, 0.5f); + NORMAL_POS2(0.5f, 0.5f); + } + glEnd(); + glEnable(GL_TEXTURE_2D); +#endif + + GL_CHECKERROR; + } +} diff --git a/gl_cityview.h b/gl_cityview.h new file mode 100644 index 0000000..b8d7f3f --- /dev/null +++ b/gl_cityview.h @@ -0,0 +1,96 @@ +/************************************************************************ +* Copyright (c) 2005-2006 tok@openlinux.org.uk * +* * +* This software is provided as-is, without any express or implied * +* warranty. In no event will the authors be held liable for any * +* damages arising from the use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute * +* it freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must * +* not claim that you wrote the original software. If you use this * +* software in a product, an acknowledgment in the product documentation * +* would be appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must * +* not be misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source * +* distribution. * +************************************************************************/ +#ifndef GL_CITYVIEW_H +#define GL_CITYVIEW_H + +#include "opengta.h" +#include "navdata.h" +#include "gl_texturecache.h" +#include "gl_frustum.h" +#include "gl_pagedtexture.h" + +namespace OpenGTA { + + class CityView { + public: + CityView(); + ~CityView(); + void loadMap(const std::string &map, const std::string &style); + void createLevelObject(OpenGTA::Map::ObjectPosition *obj); + void setPosition(const GLfloat & x, const GLfloat & y, const GLfloat & z); + void setTopDownView(const GLfloat & height); + //void setCamVector(const GLfloat & x, const GLfloat & y, const GLfloat & z); + void setZoom(const GLfloat zoom); + void setViewMode(bool topDown); + bool getViewMode() { return topDownView; } + void setDrawHeadingArrows(bool yes) { drawHeadingMarkers = yes; } + void setTexFlipTest(int v) { texFlipTest = v; } + GLfloat* getCamPos() { return (GLfloat*)&camPos; } + void setVisibleRange(int); + int getVisibleRange(); + void getTerrainHeight(GLfloat & x, GLfloat & y, GLfloat & z); + void draw(Uint32 ticks); + NavData::Sector* getCurrentSector(); + OpenGL::PagedTexture renderMap2Texture(); + + bool CityView::getDrawTextured(); + bool CityView::getDrawLines(); + void CityView::setDrawTextured(bool v); + void CityView::setDrawLines(bool v); + + void resetTextures(); + + protected: + void setNull(); + void cleanup(); + void drawBlock(OpenGTA::Map::BlockInfo* bi); + void drawObject(OpenGTA::Map::ObjectPosition*); + //OpenGL::PagedTexture createSprite(size_t sprNum, GraphicsBase::SpriteInfo* info); + Util::CFrustum frustum; + OpenGL::TextureCache* sideCache; + OpenGL::TextureCache* lidCache; + Map* loadedMap; + OpenGTA::GraphicsBase* style; + GLfloat zoomLevel; + GLfloat camPos[3]; + GLfloat camVec[3]; + int visibleRange; + bool topDownView; + bool drawTextured; + bool drawLines; + + int scene_rendered_vertices; + int scene_rendered_blocks; + + GLuint scene_display_list; + bool scene_is_dirty; + bool drawHeadingMarkers; + int texFlipTest; + + Uint32 lastCacheEmptyTicks; + + NavData::Sector *current_sector; + }; +} + +#endif diff --git a/gl_font.cpp b/gl_font.cpp new file mode 100644 index 0000000..669a6b6 --- /dev/null +++ b/gl_font.cpp @@ -0,0 +1,175 @@ +/************************************************************************ +* Copyright (c) 2005-2006 tok@openlinux.org.uk * +* * +* This software is provided as-is, without any express or implied * +* warranty. In no event will the authors be held liable for any * +* damages arising from the use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute * +* it freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must * +* not claim that you wrote the original software. If you use this * +* software in a product, an acknowledgment in the product documentation * +* would be appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must * +* not be misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source * +* distribution. * +************************************************************************/ +#include +#include +#include "gl_font.h" +#include "log.h" +#include "buffercache.h" +#include "m_exceptions.h" + +namespace OpenGL { + DrawableFont::DrawableFont() { + fontSource = NULL; + texCache = NULL; + scale = 1; + } + DrawableFont::~DrawableFont() { + cleanup(); + } + void DrawableFont::setScale(unsigned int newScale) { + scale = newScale; + clearCached(); + } + void DrawableFont::clearCached() { + std::map::const_iterator j = drawables.begin(); + while (j != drawables.end()) { + delete j->second; + ++j; + } + drawables.clear(); + } + void DrawableFont::resetTextures() { + clearCached(); + texCache->clearAll(); + } + void DrawableFont::loadFont(const std::string & filename) { + cleanup(); + fontSource = new OpenGTA::Font(filename); + texCache = new TextureCache(("FontTextures: " + filename).c_str()); + srcName.clear(); + srcName = filename; + } + void DrawableFont::cleanup() { + clearCached(); + if (fontSource != NULL) + delete fontSource; + if (texCache != NULL) + delete texCache; + fontSource = NULL; + texCache = NULL; + } + GLfloat DrawableFont::drawString(const std::string & text) { + assert(texCache != NULL); + assert(fontSource != NULL); + std::string::const_iterator i = text.begin(); + std::string::const_iterator e = text.end(); + GLfloat move = 0.0f; + while (i != e) { + + if (*i != ' ') { + FontQuad* character = NULL; + std::map::const_iterator j = drawables.find(*i); + if (j == drawables.end()) { + character = createDrawableCharacter(*i); + drawables[*i] = character; + } + else + character = j->second; + Renderer::draw(*character); + } + GLfloat mm = float(fontSource->getMoveWidth(*i)) * 1.1f * scale; + glTranslatef(mm, 0.0f, 0.0f); + move += mm; + i++; + } + return move; + } + + uint16_t DrawableFont::getHeight() { + return scale * fontSource->getCharHeight(); + } + + FontQuad* DrawableFont::createDrawableCharacter(const char & c) { + GLuint texid; + unsigned int w; + unsigned int h; + unsigned char * src = fontSource->getCharacterBitmap( + fontSource->getIdByChar(c), &w, &h); + if (src == NULL) { + std::ostringstream o; + o << "Failed to load bitmap for: " << c; + throw E_UNKNOWNKEY(o.str()); + //throw std::string("Failed to load bitmap for character: " + c); + } + unsigned int glwidth = 1; + unsigned int glheight = 1; + + while(glwidth < w) + glwidth <<= 1; + + while(glheight < h) + glheight <<= 1; + + Util::BufferCache & bc = Util::BufferCacheHolder::Instance(); + + unsigned char* dst = bc.requestBuffer(glwidth * glheight * 4); + assert(dst != NULL); + unsigned char * t = dst; + unsigned char * r = src; + for (unsigned int i = 0; i < h; i++) { + memcpy(t, r, w * 4); + t += glwidth * 4; + r += w * 4; + } + glGenTextures(1, &texid); + glBindTexture(GL_TEXTURE_2D, texid); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, glwidth, glheight, 0, GL_RGBA, GL_UNSIGNED_BYTE, dst); + texCache->addTexture(c, texid); + + FontQuad* res = new FontQuad(); + res->vertices[0][0] = res->vertices[0][1] = 0; + res->vertices[1][0] = w * scale; + res->vertices[1][1] = 0; + res->vertices[2][0] = w * scale; + res->vertices[2][1] = h * scale; + res->vertices[3][0] = 0; + res->vertices[3][1] = h * scale; + + float glw = float(w) / float(glwidth); + float glh = float(h) / float(glheight); + res->texCoords[0][0] = 0.0f; + res->texCoords[0][1] = glh; + res->texCoords[1][0] = glw; + res->texCoords[1][1] = glh; + res->texCoords[2][0] = glw; + res->texCoords[2][1] = 0.0f; + res->texCoords[3][0] = 0.0f; + res->texCoords[3][1] = 0.0f; + + res->texId = texid; + + return res; + } +} + +#if 0 +int main() { + OpenGL::DrawableFont *f = new OpenGL::DrawableFont(); + f->drawString("hello world"); + delete f; +} +#endif diff --git a/gl_font.h b/gl_font.h new file mode 100644 index 0000000..985337d --- /dev/null +++ b/gl_font.h @@ -0,0 +1,54 @@ +/************************************************************************ +* Copyright (c) 2005-2006 tok@openlinux.org.uk * +* * +* This software is provided as-is, without any express or implied * +* warranty. In no event will the authors be held liable for any * +* damages arising from the use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute * +* it freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must * +* not claim that you wrote the original software. If you use this * +* software in a product, an acknowledgment in the product documentation * +* would be appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must * +* not be misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source * +* distribution. * +************************************************************************/ +#ifndef M_OPENGL_FONT_H +#define M_OPENGL_FONT_H +#include +#include +#include "opengta.h" +#include "gl_base.h" +#include "gl_texturecache.h" + +namespace OpenGL { + + class DrawableFont { + public: + DrawableFont(); + ~DrawableFont(); + void loadFont(const std::string & filename); + GLfloat drawString(const std::string & text) ; + void setScale(unsigned int newScale); + uint16_t getHeight(); + void resetTextures(); + private: + void cleanup(); + void clearCached(); + FontQuad* createDrawableCharacter(const char & c); + OpenGTA::Font *fontSource; + std::string srcName; + TextureCache *texCache; + std::map drawables; + unsigned int scale; + }; +} + +#endif diff --git a/gl_frustum.cpp b/gl_frustum.cpp new file mode 100644 index 0000000..e0f699e --- /dev/null +++ b/gl_frustum.cpp @@ -0,0 +1,302 @@ +#include +#include +#include "gl_frustum.h" + +namespace Util { + // We create an enum of the sides so we don't have to call each side 0 or 1. + // This way it makes it more understandable and readable when dealing with frustum sides. + enum FrustumSide + { + RIGHT = 0, // The RIGHT side of the frustum + LEFT = 1, // The LEFT side of the frustum + BOTTOM = 2, // The BOTTOM side of the frustum + TOP = 3, // The TOP side of the frustum + BACK = 4, // The BACK side of the frustum + FRONT = 5 // The FRONT side of the frustum + }; + + // Like above, instead of saying a number for the ABC and D of the plane, we + // want to be more descriptive. + enum PlaneData + { + A = 0, // The X value of the plane's normal + B = 1, // The Y value of the plane's normal + C = 2, // The Z value of the plane's normal + D = 3 // The distance the plane is from the origin + }; + + ///////////////////////////////// NORMALIZE PLANE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* + ///// + ///// This normalizes a plane (A side) from a given frustum. + ///// + ///////////////////////////////// NORMALIZE PLANE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* + void NormalizePlane(float frustum[6][4], int side) + { + // Here we calculate the magnitude of the normal to the plane (point A B C) + // Remember that (A, B, C) is that same thing as the normal's (X, Y, Z). + // To calculate magnitude you use the equation: magnitude = sqrt( x^2 + y^2 + z^2) + float magnitude = (float)sqrt( frustum[side][A] * frustum[side][A] + + frustum[side][B] * frustum[side][B] + + frustum[side][C] * frustum[side][C] ); + + // Then we divide the plane's values by it's magnitude. + // This makes it easier to work with. + frustum[side][A] /= magnitude; + frustum[side][B] /= magnitude; + frustum[side][C] /= magnitude; + frustum[side][D] /= magnitude; + } + + ///////////////////////////////// CALCULATE FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* + ///// + ///// This extracts our frustum from the projection and modelview matrix. + ///// + ///////////////////////////////// CALCULATE FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* + void CFrustum::CalculateFrustum() + { + float proj[16]; // This will hold our projection matrix + float modl[16]; // This will hold our modelview matrix + float clip[16]; // This will hold the clipping planes + + // glGetFloatv() is used to extract information about our OpenGL world. + // Below, we pass in GL_PROJECTION_MATRIX to abstract our projection matrix. + // It then stores the matrix into an array of [16]. + glGetFloatv( GL_PROJECTION_MATRIX, proj ); + + // By passing in GL_MODELVIEW_MATRIX, we can abstract our model view matrix. + // This also stores it in an array of [16]. + glGetFloatv( GL_MODELVIEW_MATRIX, modl ); + + // Now that we have our modelview and projection matrix, if we combine these 2 matrices, + // it will give us our clipping planes. To combine 2 matrices, we multiply them. + + clip[ 0] = modl[ 0] * proj[ 0] + modl[ 1] * proj[ 4] + modl[ 2] * proj[ 8] + modl[ 3] * proj[12]; + clip[ 1] = modl[ 0] * proj[ 1] + modl[ 1] * proj[ 5] + modl[ 2] * proj[ 9] + modl[ 3] * proj[13]; + clip[ 2] = modl[ 0] * proj[ 2] + modl[ 1] * proj[ 6] + modl[ 2] * proj[10] + modl[ 3] * proj[14]; + clip[ 3] = modl[ 0] * proj[ 3] + modl[ 1] * proj[ 7] + modl[ 2] * proj[11] + modl[ 3] * proj[15]; + + clip[ 4] = modl[ 4] * proj[ 0] + modl[ 5] * proj[ 4] + modl[ 6] * proj[ 8] + modl[ 7] * proj[12]; + clip[ 5] = modl[ 4] * proj[ 1] + modl[ 5] * proj[ 5] + modl[ 6] * proj[ 9] + modl[ 7] * proj[13]; + clip[ 6] = modl[ 4] * proj[ 2] + modl[ 5] * proj[ 6] + modl[ 6] * proj[10] + modl[ 7] * proj[14]; + clip[ 7] = modl[ 4] * proj[ 3] + modl[ 5] * proj[ 7] + modl[ 6] * proj[11] + modl[ 7] * proj[15]; + + clip[ 8] = modl[ 8] * proj[ 0] + modl[ 9] * proj[ 4] + modl[10] * proj[ 8] + modl[11] * proj[12]; + clip[ 9] = modl[ 8] * proj[ 1] + modl[ 9] * proj[ 5] + modl[10] * proj[ 9] + modl[11] * proj[13]; + clip[10] = modl[ 8] * proj[ 2] + modl[ 9] * proj[ 6] + modl[10] * proj[10] + modl[11] * proj[14]; + clip[11] = modl[ 8] * proj[ 3] + modl[ 9] * proj[ 7] + modl[10] * proj[11] + modl[11] * proj[15]; + + clip[12] = modl[12] * proj[ 0] + modl[13] * proj[ 4] + modl[14] * proj[ 8] + modl[15] * proj[12]; + clip[13] = modl[12] * proj[ 1] + modl[13] * proj[ 5] + modl[14] * proj[ 9] + modl[15] * proj[13]; + clip[14] = modl[12] * proj[ 2] + modl[13] * proj[ 6] + modl[14] * proj[10] + modl[15] * proj[14]; + clip[15] = modl[12] * proj[ 3] + modl[13] * proj[ 7] + modl[14] * proj[11] + modl[15] * proj[15]; + + // Now we actually want to get the sides of the frustum. To do this we take + // the clipping planes we received above and extract the sides from them. + + // This will extract the RIGHT side of the frustum + m_Frustum[RIGHT][A] = clip[ 3] - clip[ 0]; + m_Frustum[RIGHT][B] = clip[ 7] - clip[ 4]; + m_Frustum[RIGHT][C] = clip[11] - clip[ 8]; + m_Frustum[RIGHT][D] = clip[15] - clip[12]; + + // Now that we have a normal (A,B,C) and a distance (D) to the plane, + // we want to normalize that normal and distance. + + // Normalize the RIGHT side + NormalizePlane(m_Frustum, RIGHT); + // This will extract the LEFT side of the frustum + m_Frustum[LEFT][A] = clip[ 3] + clip[ 0]; + m_Frustum[LEFT][B] = clip[ 7] + clip[ 4]; + m_Frustum[LEFT][C] = clip[11] + clip[ 8]; + m_Frustum[LEFT][D] = clip[15] + clip[12]; + + // Normalize the LEFT side + NormalizePlane(m_Frustum, LEFT); + + // This will extract the BOTTOM side of the frustum + m_Frustum[BOTTOM][A] = clip[ 3] + clip[ 1]; + m_Frustum[BOTTOM][B] = clip[ 7] + clip[ 5]; + m_Frustum[BOTTOM][C] = clip[11] + clip[ 9]; + m_Frustum[BOTTOM][D] = clip[15] + clip[13]; + + // Normalize the BOTTOM side + NormalizePlane(m_Frustum, BOTTOM); + + // This will extract the TOP side of the frustum + m_Frustum[TOP][A] = clip[ 3] - clip[ 1]; + m_Frustum[TOP][B] = clip[ 7] - clip[ 5]; + m_Frustum[TOP][C] = clip[11] - clip[ 9]; + m_Frustum[TOP][D] = clip[15] - clip[13]; + + // Normalize the TOP side + NormalizePlane(m_Frustum, TOP); + + // This will extract the BACK side of the frustum + m_Frustum[BACK][A] = clip[ 3] - clip[ 2]; + m_Frustum[BACK][B] = clip[ 7] - clip[ 6]; + m_Frustum[BACK][C] = clip[11] - clip[10]; + m_Frustum[BACK][D] = clip[15] - clip[14]; + + // Normalize the BACK side + NormalizePlane(m_Frustum, BACK); + + // This will extract the FRONT side of the frustum + m_Frustum[FRONT][A] = clip[ 3] + clip[ 2]; + m_Frustum[FRONT][B] = clip[ 7] + clip[ 6]; + m_Frustum[FRONT][C] = clip[11] + clip[10]; + m_Frustum[FRONT][D] = clip[15] + clip[14]; + + // Normalize the FRONT side + NormalizePlane(m_Frustum, FRONT); + } + + // The code below will allow us to make checks within the frustum. For example, + // if we want to see if a point, a sphere, or a cube lies inside of the frustum. + // Because all of our planes point INWARDS (The normals are all pointing inside the frustum) + // we then can assume that if a point is in FRONT of all of the planes, it's inside. + + ///////////////////////////////// POINT IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* + ///// + ///// This determines if a point is inside of the frustum + ///// + ///////////////////////////////// POINT IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* + bool CFrustum::PointInFrustum( float x, float y, float z ) + { + // If you remember the plane equation (A*x + B*y + C*z + D = 0), then the rest + // of this code should be quite obvious and easy to figure out yourself. + // In case don't know the plane equation, it might be a good idea to look + // at our Plane Collision tutorial at www.GameTutorials.com in OpenGL Tutorials. + // I will briefly go over it here. (A,B,C) is the (X,Y,Z) of the normal to the plane. + // They are the same thing... but just called ABC because you don't want to say: + // (x*x + y*y + z*z + d = 0). That would be wrong, so they substitute them. + // the (x, y, z) in the equation is the point that you are testing. The D is + // The distance the plane is from the origin. The equation ends with "= 0" because + // that is true when the point (x, y, z) is ON the plane. When the point is NOT on + // the plane, it is either a negative number (the point is behind the plane) or a + // positive number (the point is in front of the plane). We want to check if the point + // is in front of the plane, so all we have to do is go through each point and make + // sure the plane equation goes out to a positive number on each side of the frustum. + // The result (be it positive or negative) is the distance the point is front the plane. + + // Go through all the sides of the frustum + for(int i = 0; i < 6; i++ ) + { + // Calculate the plane equation and check if the point is behind a side of the frustum + if(m_Frustum[i][A] * x + m_Frustum[i][B] * y + m_Frustum[i][C] * z + m_Frustum[i][D] <= 0) + { + // The point was behind a side, so it ISN'T in the frustum + return false; + } + } + + // The point was inside of the frustum (In front of ALL the sides of the frustum) + return true; + } + + ///////////////////////////////// SPHERE IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* + ///// + ///// This determines if a sphere is inside of our frustum by it's center and radius. + ///// + ///////////////////////////////// SPHERE IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* + bool CFrustum::SphereInFrustum( float x, float y, float z, float radius ) + { + // Now this function is almost identical to the PointInFrustum(), except we + // now have to deal with a radius around the point. The point is the center of + // the radius. So, the point might be outside of the frustum, but it doesn't + // mean that the rest of the sphere is. It could be half and half. So instead of + // checking if it's less than 0, we need to add on the radius to that. Say the + // equation produced -2, which means the center of the sphere is the distance of + // 2 behind the plane. Well, what if the radius was 5? The sphere is still inside, + // so we would say, if(-2 < -5) then we are outside. In that case it's false, + // so we are inside of the frustum, but a distance of 3. This is reflected below. + + // Go through all the sides of the frustum + for(int i = 0; i < 6; i++ ) + { + // If the center of the sphere is farther away from the plane than the radius + if( m_Frustum[i][A] * x + m_Frustum[i][B] * y + m_Frustum[i][C] * z + m_Frustum[i][D] <= -radius ) + { + // The distance was greater than the radius so the sphere is outside of the frustum + return false; + } + } + + // The sphere was inside of the frustum! + return true; + } + + ///////////////////////////////// CUBE IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* + ///// + ///// This determines if a cube is in or around our frustum by it's center and 1/2 it's length + ///// + ///////////////////////////////// CUBE IN FRUSTUM \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* + bool CFrustum::CubeInFrustum( float x, float y, float z, float size ) + { + // This test is a bit more work, but not too much more complicated. + // Basically, what is going on is, that we are given the center of the cube, + // and half the length. Think of it like a radius. Then we checking each point + // in the cube and seeing if it is inside the frustum. If a point is found in front + // of a side, then we skip to the next side. If we get to a plane that does NOT have + // a point in front of it, then it will return false. + + // *Note* - This will sometimes say that a cube is inside the frustum when it isn't. + // This happens when all the corners of the bounding box are not behind any one plane. + // This is rare and shouldn't effect the overall rendering speed. + + for(int i = 0; i < 6; i++ ) + { + if(m_Frustum[i][A] * (x - size) + m_Frustum[i][B] * (y - size) + m_Frustum[i][C] * (z - size) + m_Frustum[i][D] > 0) + continue; + if(m_Frustum[i][A] * (x + size) + m_Frustum[i][B] * (y - size) + m_Frustum[i][C] * (z - size) + m_Frustum[i][D] > 0) + continue; + if(m_Frustum[i][A] * (x - size) + m_Frustum[i][B] * (y + size) + m_Frustum[i][C] * (z - size) + m_Frustum[i][D] > 0) + continue; + if(m_Frustum[i][A] * (x + size) + m_Frustum[i][B] * (y + size) + m_Frustum[i][C] * (z - size) + m_Frustum[i][D] > 0) + continue; + if(m_Frustum[i][A] * (x - size) + m_Frustum[i][B] * (y - size) + m_Frustum[i][C] * (z + size) + m_Frustum[i][D] > 0) + continue; + if(m_Frustum[i][A] * (x + size) + m_Frustum[i][B] * (y - size) + m_Frustum[i][C] * (z + size) + m_Frustum[i][D] > 0) + continue; + if(m_Frustum[i][A] * (x - size) + m_Frustum[i][B] * (y + size) + m_Frustum[i][C] * (z + size) + m_Frustum[i][D] > 0) + continue; + if(m_Frustum[i][A] * (x + size) + m_Frustum[i][B] * (y + size) + m_Frustum[i][C] * (z + size) + m_Frustum[i][D] > 0) + continue; + + // If we get here, it isn't in the frustum + return false; + } + + return true; + } + + bool CFrustum::BlockInFrustum(float x, float z, float size) { + + const float b_height = 6.0f; + for(int i = 0; i < 6; i++ ) + { + if(m_Frustum[i][A] * (x - size) + m_Frustum[i][B] * 0.0f + m_Frustum[i][C] * (z - size) + m_Frustum[i][D] > 0) + continue; + if(m_Frustum[i][A] * (x + size) + m_Frustum[i][B] * 0.0f + m_Frustum[i][C] * (z - size) + m_Frustum[i][D] > 0) + continue; + if(m_Frustum[i][A] * (x - size) + m_Frustum[i][B] * b_height + m_Frustum[i][C] * (z - size) + m_Frustum[i][D] > 0) + continue; + if(m_Frustum[i][A] * (x + size) + m_Frustum[i][B] * b_height + m_Frustum[i][C] * (z - size) + m_Frustum[i][D] > 0) + continue; + if(m_Frustum[i][A] * (x - size) + m_Frustum[i][B] * 0.0f + m_Frustum[i][C] * (z + size) + m_Frustum[i][D] > 0) + continue; + if(m_Frustum[i][A] * (x + size) + m_Frustum[i][B] * 0.0f + m_Frustum[i][C] * (z + size) + m_Frustum[i][D] > 0) + continue; + if(m_Frustum[i][A] * (x - size) + m_Frustum[i][B] * b_height + m_Frustum[i][C] * (z + size) + m_Frustum[i][D] > 0) + continue; + if(m_Frustum[i][A] * (x + size) + m_Frustum[i][B] * b_height + m_Frustum[i][C] * (z + size) + m_Frustum[i][D] > 0) + continue; + + // If we get here, it isn't in the frustum + return false; + } + + return true; + + } + +} diff --git a/gl_frustum.h b/gl_frustum.h new file mode 100644 index 0000000..8f4103a --- /dev/null +++ b/gl_frustum.h @@ -0,0 +1,47 @@ +//***********************************************************************// +// // +// - "Talk to me like I'm a 3 year old!" Programming Lessons - // +// // +// $Author: DigiBen digiben@gametutorials.com // +// // +// $Program: Frustum Culling // +// // +// $Description: Demonstrates checking if shapes are in view // +// // +// $Date: 8/28/01 // +// // +//***********************************************************************// + +#ifndef DIGIBEN_FRUSTUM +#define DIGIBEN_FRUSTUM + +namespace Util { + + // This will allow us to create an object to keep track of our frustum + class CFrustum { + + public: + + // Call this every time the camera moves to update the frustum + void CalculateFrustum(); + + // This takes a 3D point and returns TRUE if it's inside of the frustum + bool PointInFrustum(float x, float y, float z); + + // This takes a 3D point and a radius and returns TRUE if the sphere is inside of the frustum + bool SphereInFrustum(float x, float y, float z, float radius); + + // This takes the center and half the length of the cube. + bool CubeInFrustum( float x, float y, float z, float size ); + + bool BlockInFrustum(float x, float z, float size); + + private: + + // This holds the A B C and D values for each side of our frustum. + float m_Frustum[6][4]; + }; + +} + +#endif diff --git a/gl_pagedtexture.h b/gl_pagedtexture.h new file mode 100644 index 0000000..44dcae0 --- /dev/null +++ b/gl_pagedtexture.h @@ -0,0 +1,68 @@ +/************************************************************************ +* Copyright (c) 2005-2006 tok@openlinux.org.uk * +* * +* This software is provided as-is, without any express or implied * +* warranty. In no event will the authors be held liable for any * +* damages arising from the use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute * +* it freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must * +* not claim that you wrote the original software. If you use this * +* software in a product, an acknowledgment in the product documentation * +* would be appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must * +* not be misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source * +* distribution. * +************************************************************************/ +#ifndef PAGED_TEXTURE_H +#define PAGED_TEXTURE_H + +#include + +namespace OpenGL { + + struct TexCoord { + TexCoord(GLfloat a, GLfloat b) : u(a), v(b) {} + TexCoord() : u(0.0f), v(0.0f) {} + GLfloat u; + GLfloat v; + }; + + struct PagedTexture { + private: + void _copyCoords(const PagedTexture & other) { + for (int i=0; i < 2; ++i) { + coords[i].u = other.coords[i].u; + coords[i].v = other.coords[i].v; + } + + } + public: + PagedTexture(GLuint p, GLfloat a, GLfloat b, GLfloat c, GLfloat d) : + inPage(p) { + coords[0].u = a; + coords[0].v = b; + coords[1].u = c; + coords[1].v = d; + } + PagedTexture() : inPage(0) {} + PagedTexture(const PagedTexture & other) : inPage(other.inPage) { + _copyCoords(other); + } + PagedTexture & operator = (const PagedTexture & other) { + inPage = other.inPage; + _copyCoords(other); + return *this; + } + GLuint inPage; + TexCoord coords[2]; + }; + +} +#endif diff --git a/gl_screen.cpp b/gl_screen.cpp new file mode 100644 index 0000000..1282bbc --- /dev/null +++ b/gl_screen.cpp @@ -0,0 +1,185 @@ +/************************************************************************ +* Copyright (c) 2005-2006 tok@openlinux.org.uk * +* * +* This software is provided as-is, without any express or implied * +* warranty. In no event will the authors be held liable for any * +* damages arising from the use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute * +* it freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must * +* not claim that you wrote the original software. If you use this * +* software in a product, an acknowledgment in the product documentation * +* would be appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must * +* not be misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source * +* distribution. * +************************************************************************/ +#include +#include "gl_screen.h" +#include "log.h" +#include "buffercache.h" +#include "m_exceptions.h" + +namespace OpenGL { +#ifndef DEFAULT_SCREEN_WIDTH +#define DEFAULT_SCREEN_WIDTH 640 +#endif +#ifndef DEFAULT_SCREEN_HEIGHT +#define DEFAULT_SCREEN_HEIGHT 480 +#endif + + Screen::Screen() { + surface = NULL; + videoFlags = defaultVideoFlags; + width = DEFAULT_SCREEN_WIDTH; + height = DEFAULT_SCREEN_HEIGHT; + bpp = 32; + fieldOfView = 60.0f; + nearPlane = 0.1f; + farPlane = 250.0f; + } + + void Screen::activate(Uint32 w, Uint32 h) { + if (w) + width = w; + if (h) + height = h; + initSDL(); + resize(width, height); + initGL(); + setSystemMouseCursor(false); + } + + void Screen::setSystemMouseCursor(bool visible) { + SDL_ShowCursor((visible ? SDL_ENABLE : SDL_DISABLE)); + } + + Uint32 Screen::getWidth() { + return width; + } + + Uint32 Screen::getHeight() { + return height; + } + + bool Screen::getFullscreen() { + return (videoFlags & SDL_FULLSCREEN); + } + + void Screen::setFullScreenFlag(bool v) { + if (v && getFullscreen()) + return; + else if (!v && !getFullscreen()) + return; + if (v) + videoFlags |= SDL_FULLSCREEN; + else + videoFlags ^= SDL_FULLSCREEN; + } + + Screen::~Screen() { + setSystemMouseCursor(true); + if (SDL_WasInit(SDL_INIT_VIDEO)) + SDL_Quit(); + surface = NULL; + } + + void Screen::toggleFullscreen() { + if (videoFlags & SDL_FULLSCREEN) + videoFlags ^= SDL_FULLSCREEN; + else + videoFlags |= SDL_FULLSCREEN; + resize(width, height); + } + + void Screen::initSDL() { + int err = SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER); + if (err) + //throw "SDL_Init failed: " + std::string(SDL_GetError()); + throw E_INVALIDFORMAT("SDL_Init failed: " + std::string(SDL_GetError())); + SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8); + SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8); + SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8); + SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16); + SDL_GL_SetAttribute( SDL_GL_SWAP_CONTROL, 1); + } + + void Screen::initGL() { + //GLfloat LightAmbient[] = { 0.8f, 0.8f, 0.8f, 1.0f }; + //GLfloat LightDiffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f }; + //GLfloat LightPosition[] = { 128.0f, 200.0f, 128.0f, 1.0f }; + + //glShadeModel( GL_SMOOTH ); + glClearColor( 0.0f, 0.0f, 0.0f, 0.0f ); + glEnable( GL_DEPTH_TEST ); + //glEnable( GL_LIGHTING ); + glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST ); + //glLightfv( GL_LIGHT0, GL_AMBIENT, LightAmbient ); + //glLightfv( GL_LIGHT0, GL_DIFFUSE, LightDiffuse ); + //glLightfv( GL_LIGHT0, GL_POSITION, LightPosition ); + //glEnable( GL_LIGHT0 ); + glEnable( GL_COLOR_MATERIAL); + glCullFace(GL_BACK); + //glPolygonMode(GL_FRONT, GL_FILL); + //glPolygonMode(GL_BACK, GL_LINE); + } + + void Screen::resize(Uint32 w, Uint32 h) { + if (h == 0) + h = 1; + surface = SDL_SetVideoMode(w, h, bpp, videoFlags); + glViewport(0, 0, w, h); + width = w; + height = h; + } + + void Screen::set3DProjection() { + float ratio = float(width) / float(height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective( fieldOfView, ratio, nearPlane, farPlane); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + } + + void Screen::setFlatProjection() { + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, width, 0, height, -1, 1); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + } + + void Screen::makeScreenshot(const char* filename) { + INFO << "saving screen as: " << filename << std::endl; + uint8_t *pixels = Util::BufferCacheHolder::Instance().requestBuffer(width * height * 3); + + glReadBuffer(GL_FRONT); + glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, reinterpret_cast(pixels)); + + SDL_Surface* image = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 24, + 255U << (0), + 255U << (8), + 255U << (16), + 0); + SDL_LockSurface(image); + + uint8_t *imagepixels = reinterpret_cast(image->pixels); + for (int y = (height - 1); y >= 0; --y) { + uint8_t *row_begin = pixels + y * width * 3; + uint8_t *row_end = row_begin + width * 3; + + std::copy(row_begin, row_end, imagepixels); + imagepixels += image->pitch; + } + SDL_UnlockSurface(image); + SDL_SaveBMP(image, filename); + SDL_FreeSurface( image ); + } +} diff --git a/gl_screen.h b/gl_screen.h new file mode 100644 index 0000000..27229a8 --- /dev/null +++ b/gl_screen.h @@ -0,0 +1,66 @@ +/************************************************************************ +* Copyright (c) 2005-2006 tok@openlinux.org.uk * +* * +* This software is provided as-is, without any express or implied * +* warranty. In no event will the authors be held liable for any * +* damages arising from the use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute * +* it freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must * +* not claim that you wrote the original software. If you use this * +* software in a product, an acknowledgment in the product documentation * +* would be appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must * +* not be misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source * +* distribution. * +************************************************************************/ +#ifndef GL_SCREEN_H +#define GL_SCREEN_H + +#include +#include + +#include "Singleton.h" + +namespace OpenGL { + class Screen { + public: + Screen(); + ~Screen(); + void set3DProjection(); + void setFlatProjection(); + void setFullScreenFlag(bool v); + void toggleFullscreen(); + void activate(Uint32 w = 0, Uint32 h = 0); + void resize(Uint32 w, Uint32 h); + void setSystemMouseCursor(bool visible); + Uint32 getWidth(); + Uint32 getHeight(); + bool getFullscreen(); + void makeScreenshot(const char* filename); + private: + void initGL(); + void initSDL(); + Uint32 width, height; + Uint32 bpp; + Uint32 videoFlags; + float fieldOfView; + float nearPlane; + float farPlane; + static const Uint32 defaultVideoFlags = + SDL_OPENGL | SDL_GL_DOUBLEBUFFER | SDL_HWPALETTE | SDL_HWACCEL; + + SDL_Surface *surface; + }; + + using namespace Loki; + typedef SingletonHolder ScreenHolder; +} + +#endif diff --git a/gl_spritecache.cpp b/gl_spritecache.cpp new file mode 100644 index 0000000..a824f8a --- /dev/null +++ b/gl_spritecache.cpp @@ -0,0 +1,261 @@ +/************************************************************************ +* Copyright (c) 2005-2006 tok@openlinux.org.uk * +* * +* This software is provided as-is, without any express or implied * +* warranty. In no event will the authors be held liable for any * +* damages arising from the use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute * +* it freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must * +* not claim that you wrote the original software. If you use this * +* software in a product, an acknowledgment in the product documentation * +* would be appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must * +* not be misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source * +* distribution. * +************************************************************************/ +#include +#include +#include "gl_spritecache.h" +#include "opengta.h" +#include "dataholder.h" +#include "buffercache.h" +#include "log.h" + +namespace OpenGL { + SpriteIdentifier::SpriteIdentifier() : sprNum(0), remap(-1), delta(0) {} + SpriteIdentifier::SpriteIdentifier(PHYSFS_uint16 num, PHYSFS_sint16 map, PHYSFS_uint32 d) : + sprNum(num), remap(map), delta(d) {} + SpriteIdentifier::SpriteIdentifier(const SpriteIdentifier & other) : + sprNum(other.sprNum), remap(other.remap), delta(other.delta) {} + + bool SpriteIdentifier::operator ==(const SpriteIdentifier & other) const { + if ((sprNum == other.sprNum) && + (remap == other.remap) && + (delta == other.delta)) + return true; + return false; + } + bool SpriteIdentifier::operator <(const SpriteIdentifier & other) const { + if (sprNum < other.sprNum) + return true; + else if (sprNum > other.sprNum) + return false; + if (remap < other.remap) + return true; + else if (remap > other.remap) + return false; + if (delta < other.delta) + return true; + return false; + } + + SpriteCache::SpriteCache() { +#ifdef DO_SCALE2X + doScale2x = true; +#else + doScale2x = false; +#endif + } + + void SpriteCache::setScale2x(bool enabled) { +#ifndef DO_SCALE2X + if (enabled) + // FIXME: for some reason I can not catch this exception, thus it only prints + //throw E_NOTSUPPORTED("Scale2x feature disabled at compile time"); + ERROR << "scale2x feature disabled at compile time - ignoring request" << std::endl; +#endif + if (loadedSprites.begin() == loadedSprites.end()) { + doScale2x = enabled; + } + else { + ERROR << "scale2x cannot be set during game - ignoring request" << std::endl; + } + } + + bool SpriteCache::getScale2x() { + return doScale2x; + } + + SpriteCache::~SpriteCache() { + clearAll(); + } + + void SpriteCache::clearAll() { + SpriteMapType::iterator i = loadedSprites.begin(); + while (i != loadedSprites.end()) { + glDeleteTextures(1, &(*i).second.inPage); + ++i; + } + loadedSprites.clear(); + } + + bool SpriteCache::has(PHYSFS_uint16 sprNum) { + SpriteMapType::iterator i = loadedSprites.find(SpriteIdentifier(sprNum, -1, 0)); + if (i != loadedSprites.end()) + return true; + INFO << "sprite not loaded sprnum: " << sprNum <second; + } + + PagedTexture & SpriteCache::get(PHYSFS_uint16 sprNum, PHYSFS_sint16 remap) { + SpriteMapType::iterator i = loadedSprites.find(SpriteIdentifier(sprNum, remap, 0)); + assert(i != loadedSprites.end()); + return i->second; + } + + PagedTexture & SpriteCache::get(const SpriteIdentifier & si) { + SpriteMapType::iterator i = loadedSprites.find(si); + assert(i != loadedSprites.end()); + return i->second; + } + + void SpriteCache::add(PHYSFS_uint16 sprNum, PHYSFS_sint16 remap, PagedTexture & t) { + loadedSprites.insert( + std::make_pair( + SpriteIdentifier(sprNum, remap, 0), t)); + } + + void SpriteCache::add(const SpriteIdentifier & si, PagedTexture & t) { + loadedSprites.insert(std::make_pair(si, t)); + } + + PagedTexture SpriteCache::create(PHYSFS_uint16 sprNum, + OpenGTA::GraphicsBase::SpriteNumbers::SpriteTypes st, PHYSFS_sint16 remap = -1 ) { + /* + OpenGTA::GraphicsBase & style = OpenGTA::StyleHolder::Instance().get(); + PHYSFS_uint16 real_num = style.spriteNumbers.reIndex(sprNum, st); + + OpenGTA::GraphicsBase::SpriteInfo* info = style.getSprite(real_num); + assert(info); + + OpenGL::PagedTexture t = createSprite(real_num, remap, info); + add(real_num, remap, t); + return t; + */ + return create(sprNum, st, remap, 0); + } + + PagedTexture SpriteCache::create(PHYSFS_uint16 sprNum, + OpenGTA::GraphicsBase::SpriteNumbers::SpriteTypes st, + PHYSFS_sint16 remap, PHYSFS_uint32 delta) { + OpenGTA::GraphicsBase & style = OpenGTA::StyleHolder::Instance().get(); + PHYSFS_uint16 real_num = style.spriteNumbers.reIndex(sprNum, st); + + OpenGTA::GraphicsBase::SpriteInfo* info = style.getSprite(real_num); + assert(info); + + OpenGL::PagedTexture t = createSprite(real_num, remap, delta, info); + SpriteIdentifier si(real_num, remap, delta); + add(si, t); + return t; + } + + OpenGL::PagedTexture SpriteCache::createSprite(size_t sprite_num, PHYSFS_sint16 remap, + PHYSFS_uint32 delta, OpenGTA::GraphicsBase::SpriteInfo* info) { + INFO << "creating new sprite: " << sprite_num << " remap: " << remap << std::endl; + unsigned char* src = OpenGTA::StyleHolder::Instance().get(). + getSpriteBitmap(sprite_num, remap , delta); + unsigned int glwidth = 1; + unsigned int glheight = 1; + + while(glwidth < info->w) + glwidth <<= 1; + while(glheight < info->h) + glheight <<= 1; + unsigned char* dst = Util::BufferCacheHolder::Instance().requestBuffer(glwidth * glheight * 4); + Util::BufferCacheHolder::Instance().unlockBuffer(src); + assert(dst != NULL); + unsigned char * t = dst; + unsigned char * r = src; + for (unsigned int i = 0; i < info->h; i++) { + memcpy(t, r, info->w * 4); + t += glwidth * 4; + r += info->w * 4; + } +#ifdef DO_SCALE2X + if (doScale2x) { +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) + + const int srcpitch = glwidth * 4; + const int dstpitch = glwidth * 8; + Uint8* srcpix = dst; + Util::BufferCacheHolder::Instance().lockBuffer(dst); + Uint8* dstpix = Util::BufferCacheHolder::Instance().requestBuffer(glwidth * glheight * 4 * 4); + Uint32 E0, E1, E2, E3, B, D, E, F, H; + for(unsigned int looph = 0; looph < glheight; ++looph) + { + for(unsigned int loopw = 0; loopw < glwidth; ++ loopw) + { + B = *(Uint32*)(srcpix + (MAX(0,looph-1)*srcpitch) + (4*loopw)); + D = *(Uint32*)(srcpix + (looph*srcpitch) + (4*MAX(0,loopw-1))); + E = *(Uint32*)(srcpix + (looph*srcpitch) + (4*loopw)); + F = *(Uint32*)(srcpix + (looph*srcpitch) + (4*MIN(glwidth-1,loopw+1))); + H = *(Uint32*)(srcpix + (MIN(glheight-1,looph+1)*srcpitch) + (4*loopw)); + + E0 = D == B && B != F && D != H ? D : E; + E1 = B == F && B != D && F != H ? F : E; + E2 = D == H && D != B && H != F ? D : E; + E3 = H == F && D != H && B != F ? F : E; + + *(Uint32*)(dstpix + looph*2*dstpitch + loopw*2*4) = E0; + *(Uint32*)(dstpix + looph*2*dstpitch + (loopw*2+1)*4) = E1; + *(Uint32*)(dstpix + (looph*2+1)*dstpitch + loopw*2*4) = E2; + *(Uint32*)(dstpix + (looph*2+1)*dstpitch + (loopw*2+1)*4) = E3; + } + } + Util::BufferCacheHolder::Instance().unlockBuffer(dst); + dst = dstpix; + } +#endif + + GLuint texid; + glGenTextures(1, &texid); + glBindTexture(GL_TEXTURE_2D, texid); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +#ifdef DO_SCALE2X + if (doScale2x) + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, glwidth*2, glheight*2, 0, GL_RGBA, GL_UNSIGNED_BYTE, dst); + else + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, glwidth, glheight, 0, GL_RGBA, GL_UNSIGNED_BYTE, dst); +#else + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, glwidth, glheight, 0, GL_RGBA, GL_UNSIGNED_BYTE, dst); +#endif + return OpenGL::PagedTexture(texid, 0, 0, + float(info->w)/float(glwidth), float(info->h)/float(glheight)); + } + +} diff --git a/gl_spritecache.h b/gl_spritecache.h new file mode 100644 index 0000000..574108b --- /dev/null +++ b/gl_spritecache.h @@ -0,0 +1,81 @@ +/************************************************************************ +* Copyright (c) 2005-2006 tok@openlinux.org.uk * +* * +* This software is provided as-is, without any express or implied * +* warranty. In no event will the authors be held liable for any * +* damages arising from the use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute * +* it freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must * +* not claim that you wrote the original software. If you use this * +* software in a product, an acknowledgment in the product documentation * +* would be appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must * +* not be misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source * +* distribution. * +************************************************************************/ +#ifndef SPRITE_CACHE_H +#define SPRITE_CACHE_H +#include +#include "Singleton.h" +#include "gl_pagedtexture.h" +#include "gl_texturecache.h" +#include "opengta.h" + +namespace OpenGL { + + struct SpriteIdentifier; + struct SpriteIdentifier { + PHYSFS_uint16 sprNum; + PHYSFS_sint16 remap; + PHYSFS_uint32 delta; + SpriteIdentifier(); + SpriteIdentifier(PHYSFS_uint16, PHYSFS_sint16, PHYSFS_uint32); + SpriteIdentifier(const SpriteIdentifier & other); + bool operator ==(const SpriteIdentifier & other) const; + bool operator <(const SpriteIdentifier & other) const; + }; + + class SpriteCache { + public: + SpriteCache(); + ~SpriteCache(); + + void clearAll(); + bool getScale2x(); + void setScale2x(bool enabled); + bool has(PHYSFS_uint16 sprNum); + bool has(PHYSFS_uint16 sprNum, PHYSFS_sint16 remap); + bool has(const SpriteIdentifier & si); + PagedTexture & get(PHYSFS_uint16 sprNum); + PagedTexture & get(PHYSFS_uint16 sprNum, PHYSFS_sint16 remap); + PagedTexture & get(const SpriteIdentifier & si); + void add(PHYSFS_uint16 sprNum, PagedTexture & t); + void add(PHYSFS_uint16 sprNum, PHYSFS_sint16 remap, PagedTexture & t); + void add(const SpriteIdentifier & si, PagedTexture & t); + PagedTexture create(PHYSFS_uint16 sprNum, + OpenGTA::GraphicsBase::SpriteNumbers::SpriteTypes, PHYSFS_sint16 remap); + PagedTexture create(PHYSFS_uint16 sprNum, + OpenGTA::GraphicsBase::SpriteNumbers::SpriteTypes, + PHYSFS_sint16 remap, PHYSFS_uint32 delta); + + OpenGL::PagedTexture SpriteCache::createSprite(size_t sprite_num, PHYSFS_sint16 remap, + PHYSFS_uint32 delta, OpenGTA::GraphicsBase::SpriteInfo* info); + private: + + typedef std::map SpriteMapType; + SpriteMapType loadedSprites; + bool doScale2x; + }; + + typedef Loki::SingletonHolder SpriteCacheHolder; +} + +#endif diff --git a/gl_texturecache.cpp b/gl_texturecache.cpp new file mode 100644 index 0000000..0cc812b --- /dev/null +++ b/gl_texturecache.cpp @@ -0,0 +1,250 @@ +/************************************************************************ +* Copyright (c) 2005-2006 tok@openlinux.org.uk * +* * +* This software is provided as-is, without any express or implied * +* warranty. In no event will the authors be held liable for any * +* damages arising from the use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute * +* it freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must * +* not claim that you wrote the original software. If you use this * +* software in a product, an acknowledgment in the product documentation * +* would be appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must * +* not be misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source * +* distribution. * +************************************************************************/ +#include +#include +#include +#include "gl_texturecache.h" +#include "log.h" + +namespace OpenGL { + template + TextureCache::TextureCache(const char* with_name) : m_name(with_name) { + instance_id = instance_count++; + clearMagic = 0; + has_cached_query = false; + last_query_result = 0; + minClearElements = 50; + } + template + TextureCache::TextureCache() { + instance_id = instance_count++; + std::ostringstream stream; + stream << "TextureCache_" << instance_count; + m_name = stream.str(); + has_cached_query = false; + clearMagic = 0; + minClearElements = 50; + } + + template + TextureCache::~TextureCache() { + unsigned int ts = cached.size(); + clearAll(); + INFO << m_name << " exited - " << ts << " textures recycled" << std::endl; + m_name.clear(); + instance_count--; + } + + template + void TextureCache::clearAll() { + typename std::map::iterator i = cached.begin(); + while (i != cached.end()) { + GLuint tid = (i->second->texId); + glDeleteTextures(1, &tid); + delete i->second; + i++; + } + cached.clear(); + } + + template + void TextureCache::status() { + std::cout << "* " << m_name << " status: " << cached.size() << " textures total" << std::endl + << "position = game_id : usage_count" << std::endl; + printStats(); + } + + template + void TextureCache::sink() { + typename std::map::iterator i = cached.begin(); + while (i != cached.end()) { + if (i->second->refCount <= 1) + i->second->refCount = 0; + else if (i->second->refCount < _max_4) + i->second->refCount = i->second->refCount >> 1; + else if (i->second->refCount < _max_2) { + INFO << m_name << " texture id " << int(i->first) << + " -- half-count reached" << std::endl; + i->second->refCount = i->second->refCount >> 2; + } + else { + WARN << m_name << " texture id " << int(i->first) << + " -- going critical" << std::endl; + i->second->refCount = i->second->refCount >> 3; + } + i++; + } + } + + template + void TextureCache::clear() { + if (clearMagic == 0) + return; + if (cached.size() < minClearElements) + return; + typename std::map::iterator i = cached.begin(); + uint32_t numCleared = 0; + while (i != cached.end()) { + if (i->second->refCount < clearMagic) { + //INFO <<"## " << m_name << " clearing: " << int(i->first) << " count: " << i->second->refCount << std::endl; + GLuint tid = (i->second->texId); + glDeleteTextures(1, &tid); + delete i->second; + cached.erase(i); + numCleared++; + } + i++; + } + INFO << m_name << " " << numCleared << " textures recycled" << std::endl; + } + + template + void TextureCache::clearStats() { + typename std::map::iterator i = cached.begin(); + while (i != cached.end()) { + i->second->refCount = 0; + i++; + } + } + + template + void TextureCache::printStats() { + typename std::map::iterator i = cached.begin(); + size_t c = 1; + size_t c_active = 0; + while (i != cached.end()) { + if (i->second->refCount > 0) { + std::cout << c << " = " << uint32_t(i->first) << " : " << i->second->refCount << std::endl; + c_active++; + } + i++; + c++; + } + std::cout << c_active << " different textures used" << std::endl; + } + + template + GLuint TextureCache::getTextureWithId(key_type id) { + if (matchingCachedQuery(id)) { + last_query_result->refCount++; + return last_query_result->texId; + } + typename std::map::iterator i = cached.find(id); + if (i == cached.end()) { + ERROR << m_name << " failed to find texture " << int(id) << std::endl; + return 0; + } + else { + cacheQuery(id, i->second); + i->second->refCount++; + } + /* + * if (i->second->isAnimated) { + AnimControl->lookup(i->second) + * } + */ + return i->second->texId; + } + + template + bool TextureCache::hasTexture(key_type id) { + if (matchingCachedQuery(id)) + return true; // last_query_result; + typename std::map::iterator i = cached.find(id); + if (i == cached.end()) + return false; + cacheQuery(id, i->second); + return true; + } + + template + void TextureCache::setToAlpha(key_type id) { + typename std::map::iterator i = cached.find(id); + if (i == cached.end()) { + ERROR << m_name << " texture not found when trying to set alpha" << std::endl; + return; + } + i->second->hasAlpha = true; + } + + template + void TextureCache::setToAnimated(key_type id) { + typename std::map::iterator i = cached.find(id); + if (i == cached.end()) { + ERROR << m_name << " texture not found when trying to set animation" << std::endl; + return; + } + i->second->isAnimated = true; + } + + template + void TextureCache::addTexture(key_type id, GLuint texId) { + /* + std::map::iterator i = cached.find(id); + if (i == cached.end()) + return;*/ + texTuple* tt = new texTuple(); + tt->texId = texId; + tt->refCount = 1; + tt->hasAlpha = false; + tt->isAnimated = false; + cached[id] = tt; + INFO << m_name << " GL texture " << texId << " added for key: " << int(id) << std::endl; + } + + template + void TextureCache::cacheQuery(key_type id, texTuple *pos) { + has_cached_query = true; + last_query_id = id; + last_query_result = pos; + } + + template + bool TextureCache::matchingCachedQuery(key_type id) { + return ((has_cached_query) && (id == last_query_id)); + } + + template + void TextureCache::setClearMagic(uint32_t removeLesser) { + clearMagic = removeLesser; + } + + template + void TextureCache::setMinClearElements(uint32_t minElements) { + minClearElements = minElements; + } + + template + unsigned int TextureCache::instance_count = 0; + + std::numeric_limits _countInfo; + template + const uint32_t TextureCache::_max_4 = _countInfo.max() / 4; + template + const uint32_t TextureCache::_max_2 = _countInfo.max() / 2; + + template class TextureCache; + template class TextureCache; + template class TextureCache; + template class TextureCache; +} diff --git a/gl_texturecache.h b/gl_texturecache.h new file mode 100644 index 0000000..75f60d9 --- /dev/null +++ b/gl_texturecache.h @@ -0,0 +1,142 @@ +/************************************************************************ +* Copyright (c) 2005-2006 tok@openlinux.org.uk * +* * +* This software is provided as-is, without any express or implied * +* warranty. In no event will the authors be held liable for any * +* damages arising from the use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute * +* it freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must * +* not claim that you wrote the original software. If you use this * +* software in a product, an acknowledgment in the product documentation * +* would be appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must * +* not be misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source * +* distribution. * +************************************************************************/ +#ifndef TEXTURECACHE_H +#define TEXTURECACHE_H +#include +#include +#include + +namespace OpenGL { + + /** Helper for the render code. + * + * This class handles the storage of GLuint texture ids and + * provides a mapping from the internal ids used by the map files. + */ + template class TextureCache { + public: + /** Simple constructor. + * + * Name will be set to TextureCache_N, where N is the current instance count. + */ + TextureCache(); + + /** Constructor with a name. + * + * Set a name for this instance; to clarify status output with multiple caches. + */ + TextureCache(const char * with_name); + + /** Just a simple destructor, nothing see here... move along. + * + * As a sidenote: this calls 'glDeleteTextures' on the images + * stored inside. + */ + ~TextureCache(); + + /** Check if texture is already cached. + * + * @param id key from map/block info + * @return true if texture is found + * @return false otherwise + */ + bool hasTexture(key_type id); + + /** Maps internal id to GLuint texture id. + * + * @param id from map/block info + * @return texture id + */ + GLuint getTextureWithId(key_type id); + + /** Adds a texture to the cache and maps to internal id. + * + * @param id requested internal id from map/block info + * @param texId texture id that contains the image + */ + void addTexture(key_type id, GLuint texId); + + /** Set specified texture to hasAlpha. + * + * This doesn't do anything; you can just check for hasAlpha later on. + */ + void setToAlpha(key_type id); + + /** probably stupid idea/going to go away + */ + void setToAnimated(key_type id); + + /** Dumps some status info to stdout. + */ + void status(); + + /** Iterate over stored textures and modify refCount. + * + * This is optional functionality; you may skip this. If you don't, + * call this *before* each rendering pass. + */ + void sink(); + + /** Remove unused textures from cache and video memory. + * + * Call this *after* a rendering pass, but not every frame! + * + * Handle with care, this code is experimental. + */ + void clear(); + + void clearAll(); + + void clearStats(); + void printStats(); + + void setClearMagic(uint32_t removeLesser); + void setMinClearElements(uint32_t minElements); + + protected: + unsigned int clearMagic; + unsigned int minClearElements; + + typedef struct texTuple { + GLuint texId; + uint32_t refCount; + bool hasAlpha; + bool isAnimated; + } texTuple; + typedef std::map CacheMapType; + CacheMapType cached; + std::string m_name; + static unsigned int instance_count; + unsigned int instance_id; + const static uint32_t _max_4; + const static uint32_t _max_2; + bool has_cached_query; + key_type last_query_id; + texTuple* last_query_result; + bool matchingCachedQuery(key_type id); + void cacheQuery(key_type id, texTuple *pos); + }; + +} + +#endif diff --git a/license.txt b/license.txt new file mode 100644 index 0000000..05ff6a4 --- /dev/null +++ b/license.txt @@ -0,0 +1,64 @@ +Copyright (c) 2005-2006 tok@openlinux.org.uk + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from +the use of this software. + +The act of running this software is not restricted; you may redistribute +it and the corresponding machine-readable source code freely, subject to +the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. + +2. You may not use the executable form in a commercial product. + +3. A derived work must either inherit the non-commercial restriction or + all the data structures copyrighted by DMA Design have to be replaced. + Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + +5. This notice may not be removed or altered from any distribution. + +*************************************************************************** +The file formats of the data files are described in documentation released +by DMA Design; this software would not exist without it. + +It is my understanding that this documentation was meant for fans of the +original game, thus I cannot place the implementing code under a free +license. +This restriction only applies to a few of the code files (marked as such). + +*************************************************************************** +This software includes a number of libraries and smaller code blocks; +these remain under their respective licenses. + +* SDL - Simple DirectMedia Layer + Copyright (C) 1997-2006 Sam Lantinga + GNU Lesser General Public License + +* SDL_image + Copyright (C) 1999-2004 Sam Lantinga + GNU Library General Public License + +* PhysicsFS + Copyright (c) 2003 Ryan C. Gordon and others + This library is distributed under the terms of the zlib license + +* The Loki Library + Copyright (c) 2001 by Andrei Alexandrescu (and others) + MIT license as well as the following: + + Permission to use, copy, modify, distribute and sell this software for any + purpose is hereby granted without fee, provided that the above copyright + notice appear in all copies and that both that copyright notice and this + permission notice appear in supporting documentation. + +*************************************************************************** +Please do not redistribute the data files of the original game together +with this software. + +GTA is a trademark of Take 2 Interactive Software Inc. + +This project is not affiliated with either Take 2 Interactive or DMA +Design. diff --git a/licenses/LGPL-2.1.gz b/licenses/LGPL-2.1.gz new file mode 100644 index 0000000..ceeafe1 Binary files /dev/null and b/licenses/LGPL-2.1.gz differ diff --git a/licenses/LGPL-2.gz b/licenses/LGPL-2.gz new file mode 100644 index 0000000..3946e1d Binary files /dev/null and b/licenses/LGPL-2.gz differ diff --git a/licenses/mit.txt b/licenses/mit.txt new file mode 100644 index 0000000..b75018b --- /dev/null +++ b/licenses/mit.txt @@ -0,0 +1,21 @@ +The MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/licenses/readme.txt b/licenses/readme.txt new file mode 100644 index 0000000..2a18086 --- /dev/null +++ b/licenses/readme.txt @@ -0,0 +1,5 @@ +This directory contains a copy of the licenses covering the libraries +used in this project. + +To comply with the SDL license at least the GNU licenses have to be +present in any distribution. diff --git a/licenses/zlib.txt b/licenses/zlib.txt new file mode 100644 index 0000000..3536eb3 --- /dev/null +++ b/licenses/zlib.txt @@ -0,0 +1,21 @@ +The zlib/libpng License + +Copyright (c) + +This software is provided as-is, without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software +in a product, an acknowledgment in the product documentation would be +appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not +be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source distribution. diff --git a/lid_normal_data.h b/lid_normal_data.h new file mode 100644 index 0000000..97c953f --- /dev/null +++ b/lid_normal_data.h @@ -0,0 +1,90 @@ + // slope 0 + { 0.000000, 1.000000, -0.000000 }, + // slope 1 + { 0.000000, 0.894427, 0.447214 }, + // slope 2 + { 0.000000, 0.894427, 0.447214 }, + // slope 3 + { 0.000000, 0.894427, -0.447214 }, + // slope 4 + { 0.000000, 0.894427, -0.447214 }, + // slope 5 + { 0.447214, 0.894427, -0.000000 }, + // slope 6 + { 0.447214, 0.894427, -0.000000 }, + // slope 7 + { -0.447214, 0.894427, 0.000000 }, + // slope 8 + { -0.447214, 0.894427, 0.000000 }, + // slope 9 + { 0.000000, 0.991656, 0.128915 }, + // slope 10 + { 0.000000, 0.992877, 0.119145 }, + // slope 11 + { 0.000000, 0.991656, 0.128915 }, + // slope 12 + { 0.000000, 0.992877, 0.119145 }, + // slope 13 + { 0.000000, 0.991656, 0.128915 }, + // slope 14 + { 0.000000, 0.992877, 0.119145 }, + // slope 15 + { 0.000000, 0.991656, 0.128915 }, + // slope 16 + { 0.000000, 0.992877, 0.119145 }, + // slope 17 + { 0.000000, 0.991656, -0.128915 }, + // slope 18 + { 0.000000, 0.992877, -0.119145 }, + // slope 19 + { 0.000000, 0.991656, -0.128915 }, + // slope 20 + { 0.000000, 0.992877, -0.119145 }, + // slope 21 + { 0.000000, 0.991656, -0.128915 }, + // slope 22 + { 0.000000, 0.992877, -0.119145 }, + // slope 23 + { 0.000000, 0.991656, -0.128915 }, + // slope 24 + { 0.000000, 0.992877, -0.119145 }, + // slope 25 + { 0.128915, 0.991656, -0.000000 }, + // slope 26 + { 0.119145, 0.992877, -0.000000 }, + // slope 27 + { 0.128915, 0.991656, -0.000000 }, + // slope 28 + { 0.119145, 0.992877, -0.000000 }, + // slope 29 + { 0.128915, 0.991656, -0.000000 }, + // slope 30 + { 0.119145, 0.992877, -0.000000 }, + // slope 31 + { 0.128915, 0.991656, -0.000000 }, + // slope 32 + { 0.119145, 0.992877, -0.000000 }, + // slope 33 + { -0.128915, 0.991656, 0.000000 }, + // slope 34 + { -0.119145, 0.992877, 0.000000 }, + // slope 35 + { -0.128915, 0.991656, 0.000000 }, + // slope 36 + { -0.119145, 0.992877, 0.000000 }, + // slope 37 + { -0.128915, 0.991656, 0.000000 }, + // slope 38 + { -0.119145, 0.992877, 0.000000 }, + // slope 39 + { -0.128915, 0.991656, 0.000000 }, + // slope 40 + { -0.119145, 0.992877, 0.000000 }, + // slope 41 + { 0.000000, 0.707107, 0.707107 }, + // slope 42 + { 0.000000, 0.707107, -0.707107 }, + // slope 43 + { 0.707107, 0.707107, -0.000000 }, + // slope 44 + { -0.707107, 0.707107, 0.000000 } diff --git a/localplayer.h b/localplayer.h new file mode 100644 index 0000000..044dfa9 --- /dev/null +++ b/localplayer.h @@ -0,0 +1,17 @@ +#ifndef OGTA_LOCAL_PLAYER_H +#define OGTA_LOCAL_PLAYER_H +#include "Singleton.h" +#include "pedestrian.h" + +namespace OpenGTA { + + class PlayerController : public Pedestrian::Controller { + public: + PlayerController() { turn = 0; move = 0; } + }; + + typedef Loki::SingletonHolder LocalPlayer; +} + +#endif diff --git a/loki.make b/loki.make new file mode 100644 index 0000000..1fc3462 --- /dev/null +++ b/loki.make @@ -0,0 +1,15 @@ +VER = 0.1.5 + +all: loki/lib/libloki.a + +loki-$(VER).tar.gz: + wget http://surfnet.dl.sourceforge.net/sourceforge/loki-lib/loki-$(VER).tar.gz + +loki-$(VER): loki-$(VER).tar.gz + tar zxf loki-$(VER).tar.gz + +loki: loki-$(VER) + mv loki-$(VER) loki + +loki/lib/libloki.a: loki + make -C loki build-static diff --git a/loki.make.w32_cross b/loki.make.w32_cross new file mode 100644 index 0000000..2840d2c --- /dev/null +++ b/loki.make.w32_cross @@ -0,0 +1,19 @@ +VER = 0.1.5 + +all: loki/lib/libloki.a + +export CXX = i586-mingw32msvc-g++ +export AR = i586-mingw32msvc-ar + +loki-$(VER).tar.gz: + wget http://surfnet.dl.sourceforge.net/sourceforge/loki-lib/loki-$(VER).tar.gz + +loki-$(VER): loki-$(VER).tar.gz + tar zxf loki-$(VER).tar.gz + +loki: loki-$(VER) + mv loki-$(VER) loki + +loki/lib/libloki.a: loki + ./tools/replace_in_files.sh 's/export OS ?= .*/export OS = Windows/' loki/Makefile + make -C loki build-static build-shared diff --git a/lua_addon/lua.hpp b/lua_addon/lua.hpp new file mode 100644 index 0000000..3e3170d --- /dev/null +++ b/lua_addon/lua.hpp @@ -0,0 +1,10 @@ +#ifndef LUA_CPP_H +#define LUA_CPP_H + +extern "C" { +#include "lua.h" +#include "lauxlib.h" +#include "lualib.h" +} + +#endif diff --git a/lua_addon/lua_camera.cpp b/lua_addon/lua_camera.cpp new file mode 100644 index 0000000..0c94332 --- /dev/null +++ b/lua_addon/lua_camera.cpp @@ -0,0 +1,92 @@ +#include "lua_camera.h" +#define method(name) {#name, Camera::l_##name} + +namespace OpenGTA { + namespace Script { + +using namespace OpenGL; + + int Camera::l_setSpeed(lua_State *L) { + float tmp = float(luaL_checknumber(L, 1)); + CameraHolder::Instance().setSpeed(tmp); + return 0; + } + int Camera::l_setRotating(lua_State *L) { + bool b = lua_toboolean(L, 1); + CameraHolder::Instance().setRotating(b); + return 0; + } + int Camera::l_getEye(lua_State *L) { + Vector3D & e = CameraHolder::Instance().getEye(); + lua_pushnumber(L, e.x); + lua_pushnumber(L, e.y); + lua_pushnumber(L, e.z); + return 3; + } + int Camera::l_setEye(lua_State *L) { + Vector3D & e = CameraHolder::Instance().getEye(); + e.x = luaL_checknumber(L, 1); + e.y = luaL_checknumber(L, 2); + e.z = luaL_checknumber(L, 3); + return 0; + } + int Camera::l_getCenter(lua_State *L) { + Vector3D & e = CameraHolder::Instance().getCenter(); + lua_pushnumber(L, e.x); + lua_pushnumber(L, e.y); + lua_pushnumber(L, e.z); + return 3; + } + int Camera::l_setCenter(lua_State *L) { + Vector3D & e = CameraHolder::Instance().getCenter(); + e.x = luaL_checknumber(L, 1); + e.y = luaL_checknumber(L, 2); + e.z = luaL_checknumber(L, 3); + return 0; + } + int Camera::l_getUp(lua_State *L) { + Vector3D & e = CameraHolder::Instance().getUp(); + lua_pushnumber(L, e.x); + lua_pushnumber(L, e.y); + lua_pushnumber(L, e.z); + return 0; + } + int Camera::l_setUp(lua_State *L) { + Vector3D & e = CameraHolder::Instance().getUp(); + e.x = luaL_checknumber(L, 1); + e.y = luaL_checknumber(L, 2); + e.z = luaL_checknumber(L, 3); + return 0; + } + int Camera::l_setGravityOn(lua_State *L) { + bool v = lua_toboolean(L, 1); + CameraHolder::Instance().setCamGravity(v); + return 0; + } + int Camera::l_interpolateToPosition(lua_State *L) { + float x, y, z; + x = float(luaL_checknumber(L, 1)); + y = float(luaL_checknumber(L, 2)); + z = float(luaL_checknumber(L, 3)); + Uint32 msecInterval = Uint32(luaL_checkinteger(L, 4)); + CameraHolder::Instance().interpolate(Vector3D(x, y, z), 1, msecInterval); + return 0; + } + + + const luaL_reg Camera::methods[] = { + method(setSpeed), + method(setRotating), + method(setEye), + method(getEye), + method(setCenter), + method(getCenter), + method(getUp), + method(setUp), + method(setGravityOn), + method(interpolateToPosition), + {NULL, NULL} + }; + } +} + diff --git a/lua_addon/lua_camera.h b/lua_addon/lua_camera.h new file mode 100644 index 0000000..88d0aff --- /dev/null +++ b/lua_addon/lua_camera.h @@ -0,0 +1,31 @@ +#ifndef LUA_OGTA_CAMERA_H +#define LUA_OGTA_CAMERA_H + +#include "gl_camera.h" +#include "lua.hpp" + +namespace OpenGTA { + namespace Script { + + class Camera { + public: + static int l_setSpeed(lua_State *L); + static int l_setRotating(lua_State *L); + static int l_setGravityOn(lua_State *L); + static int l_interpolateToPosition(lua_State *L); + static int l_getEye(lua_State *L); + static int l_setEye(lua_State *L); + static int l_getCenter(lua_State *L); + static int l_setCenter(lua_State *L); + static int l_getUp(lua_State *L); + static int l_setUp(lua_State *L); + + /*static int mute(lua_State *L); + static int unmute(lua_State *L);*/ + + + static const luaL_reg methods[]; + }; + } +} +#endif diff --git a/lua_addon/lua_cityview.cpp b/lua_addon/lua_cityview.cpp new file mode 100644 index 0000000..c0ec7ec --- /dev/null +++ b/lua_addon/lua_cityview.cpp @@ -0,0 +1,71 @@ +#include "lua_cityview.h" + +namespace OpenGTA { + namespace Script { + + int CityView::setCamPosition(lua_State *L) { + float x = float(luaL_checknumber(L, 1)); + float y = float(luaL_checknumber(L, 2)); + float z = float(luaL_checknumber(L, 3)); + setPosition(x, y, z); + return 0; + } + + int CityView::getCamPosition(lua_State *L) { + lua_pushnumber(L, camPos[0]); + lua_pushnumber(L, camPos[1]); + lua_pushnumber(L, camPos[2]); + return 3; + } + +/* + int CityView::setCamVector(lua_State *L) { + float x = float(luaL_checknumber(L, 1)); + float y = float(luaL_checknumber(L, 2)); + float z = float(luaL_checknumber(L, 3)); + OpenGTA::CityView::setCamVector(x, y, z); + return 0; + } + + int CityView::getCamVector(lua_State *L) { + lua_pushnumber(L, camVec[0]); + lua_pushnumber(L, camVec[1]); + lua_pushnumber(L, camVec[2]); + return 3; + } +*/ + + int CityView::setTopDownView(lua_State *L) { + setViewMode(lua_toboolean(L, 1)); + return 0; + } + + int CityView::setDrawHeadingArrows(lua_State *L) { + OpenGTA::CityView::setDrawHeadingArrows(lua_toboolean(L, 1)); + return 0; + } + + int CityView::setVisibleRange(lua_State *L) { + OpenGTA::CityView::setVisibleRange(luaL_checkinteger(L, 1)); + return 0; + } + + int CityView::getVisibleRange(lua_State *L) { + lua_pushnumber(L, visibleRange); + return 1; + } + const char CityView::className[] = "CityView"; +#define method(name) {#name, &CityView::name} + Lunar::RegType CityView::methods[] = { + method(setCamPosition), + method(getCamPosition), +// method(setCamVector), +// method(getCamVector), + method(setTopDownView), + method(setVisibleRange), + method(getVisibleRange), + method(setDrawHeadingArrows), + {0, 0} + }; + } +} diff --git a/lua_addon/lua_cityview.h b/lua_addon/lua_cityview.h new file mode 100644 index 0000000..8e639ba --- /dev/null +++ b/lua_addon/lua_cityview.h @@ -0,0 +1,32 @@ +#ifndef OPENGTA_SCRIPT_CV_H +#define OPENGTA_SCRIPT_CV_H + +#include "gl_cityview.h" +#include "lunar.h" + +namespace OpenGTA { + namespace Script { + class CityView : public OpenGTA::CityView { + public: + int setCamPosition(lua_State *L); + int getCamPosition(lua_State *L); + +// int setCamVector(lua_State *L); +// int getCamVector(lua_State *L); + int setTopDownView(lua_State *L); + + int setZoom(lua_State *L); + int getZoom(lua_State *L); + + int setVisibleRange(lua_State *L); + int getVisibleRange(lua_State *L); + int setDrawHeadingArrows(lua_State *L); + + // -- + + static const char className[]; + static Lunar::RegType methods[]; + }; + } +} +#endif diff --git a/lua_addon/lua_screen.cpp b/lua_addon/lua_screen.cpp new file mode 100644 index 0000000..f0a61bb --- /dev/null +++ b/lua_addon/lua_screen.cpp @@ -0,0 +1,31 @@ +#include "lua_screen.h" + +namespace OpenGTA { + namespace Script { +using namespace OpenGL; + int Screen::getFullscreen(lua_State *L) { + bool b = ScreenHolder::Instance().getFullscreen(); + lua_pushboolean(L, b); + return 1; + } + int Screen::setFullscreen(lua_State *L) { + bool b = ScreenHolder::Instance().getFullscreen(); + bool v = lua_toboolean(L, 1); + if (b != v) + ScreenHolder::Instance().toggleFullscreen(); + return 0; + } + int Screen::makeScreenShot(lua_State *L) { + ScreenHolder::Instance().makeScreenshot(luaL_checkstring(L, 1)); + return 0; + } +#define method(name) {#name, Screen::name} + const luaL_reg Screen::methods[] = { + method(setFullscreen), + method(getFullscreen), + method(makeScreenShot), + {NULL, NULL} + }; + + } +} diff --git a/lua_addon/lua_screen.h b/lua_addon/lua_screen.h new file mode 100644 index 0000000..22ea655 --- /dev/null +++ b/lua_addon/lua_screen.h @@ -0,0 +1,20 @@ +#ifndef LUA_OGTA_SCREEN_H +#define LUA_OGTA_SCREEN_H + +#include "gl_screen.h" +#include "lua.hpp" + +namespace OpenGTA { + namespace Script { + + class Screen { + public: + static int getFullscreen(lua_State *L); + static int setFullscreen(lua_State *L); + static int makeScreenShot(lua_State *L); + + static const luaL_reg methods[]; + }; + } +} +#endif diff --git a/lua_addon/lua_stackguard.cpp b/lua_addon/lua_stackguard.cpp new file mode 100644 index 0000000..86da544 --- /dev/null +++ b/lua_addon/lua_stackguard.cpp @@ -0,0 +1,22 @@ +#include +#include "lua_stackguard.h" +#include "log.h" + +namespace Util { + LuaStackguard::LuaStackguard(const char* f, int l, lua_State *L) { + assert(L); + m_state = L; + i_file = f; + i_line = l; + m_top = lua_gettop(m_state); + } + + LuaStackguard::~LuaStackguard() { + int now_top = lua_gettop(m_state); + if (now_top > m_top) { + Util::Log::warn(i_file, i_line) << "Stack-balance: " << now_top << " > " << m_top << std::endl; + lua_settop(m_state, m_top); + } + } + +} diff --git a/lua_addon/lua_stackguard.h b/lua_addon/lua_stackguard.h new file mode 100644 index 0000000..9a71893 --- /dev/null +++ b/lua_addon/lua_stackguard.h @@ -0,0 +1,23 @@ +#ifndef LUA_STACK_GUARD_H +#define LUA_STACK_GUARD_H + +#include "lua.hpp" + +namespace Util { + + class LuaStackguard { + public: + LuaStackguard(const char* f, int l, lua_State *L); + ~LuaStackguard(); + private: + int m_top; + lua_State* m_state; + // line number and filename where this instance was created + int i_line; + const char* i_file; + }; + +#define LGUARD(L) LuaStackguard guard(__FILE__, __LINE__, L) + +} +#endif diff --git a/lua_addon/lua_vm.cpp b/lua_addon/lua_vm.cpp new file mode 100644 index 0000000..e437c1d --- /dev/null +++ b/lua_addon/lua_vm.cpp @@ -0,0 +1,126 @@ +#include +#include "lua_vm.h" +#include "lunar.h" +#include "lua_cityview.h" +#include "lua_stackguard.h" +#include "lua_camera.h" +#include "lua_screen.h" +#include "lua_spritecache.h" +#include "m_exceptions.h" + +using namespace Util; + +namespace OpenGTA { + namespace Script { + LuaVM::LuaVM() : L(NULL) { + L = lua_open(); + if (L == NULL) + throw E_SCRIPTERROR("Failed to create Lua state!"); + + luaopen_base(L); + luaopen_math(L); + _registered = false; + lua_settop(L, 0); + prepare(); + } + + LuaVM::~LuaVM() { + if (L != NULL) + lua_close(L); + L = NULL; + } + + void LuaVM::prepare() { + LGUARD(L); + if (!_registered) { + Lunar::Register2(L); + luaL_openlib(L, "camera", Camera::methods, 0); + luaL_openlib(L, "screen", Screen::methods, 0); + luaL_openlib(L, "spritecache", SpriteCache::methods, 0); + } + _registered = true; + } + + void LuaVM::setCityView(OpenGTA::CityView & cv) { + LGUARD(L); + CityView *scv = static_cast(&cv); + lua_gettable(L, LUA_GLOBALSINDEX); + int scv_ref = Lunar::push(L, scv, false); + lua_pushliteral(L, "city_view"); + lua_pushvalue(L, scv_ref); + lua_settable(L, LUA_GLOBALSINDEX); + } + + void LuaVM::runString(const char* _str) { + LGUARD(L); + if (!_str) + return; + if (luaL_loadbuffer(L, _str, strlen(_str), "cmd") || + lua_pcall(L, 0, 0, 0)) + throw E_SCRIPTERROR("Error running string: " + std::string(lua_tostring(L, -1))); + } + + void LuaVM::runFile(const char* filename) { + LGUARD(L); + if (luaL_loadfile(L, filename) || lua_pcall(L, 0, 0, 0)) + throw E_SCRIPTERROR("Error running file: " + std::string(lua_tostring(L, -1))); + } + + void LuaVM::callSimpleFunction(const char* func_name) { + LGUARD(L); + lua_getglobal(L, func_name); + if (lua_type(L, -1) == LUA_TFUNCTION) { + if (lua_pcall(L, 0, 0, 0) != 0) + throw E_SCRIPTERROR(("Exception calling function: ") + std::string(lua_tostring(L, -1))); + } + else + throw E_SCRIPTERROR("No such function: " + std::string(func_name)); + } + + int LuaVM::getGlobalInt(const char* key) { + LGUARD(L); + lua_getglobal(L, key); + if (!lua_isnumber(L, -1)) + throw E_SCRIPTERROR("Expected int value for key: " + std::string(key)); + int v = int(lua_tonumber(L, -1)); + return v; + } + + float LuaVM::getGlobalFloat(const char* key) { + LGUARD(L); + lua_getglobal(L, key); + if (!lua_isnumber(L, -1)) + throw E_SCRIPTERROR("Expected float value for key: " + std::string(key)); + float v = float(lua_tonumber(L, -1)); + return v; + } + + const char* LuaVM::getGlobalString(const char* key) { + LGUARD(L); + lua_getglobal(L, key); + if (!lua_isstring(L, -1)) + throw E_SCRIPTERROR("Expected string value for key: " + std::string(key)); + const char* v = lua_tostring(L, -1); + return v; + } + + void LuaVM::setGlobalInt(const char* key, int v) { + LGUARD(L); + lua_pushnumber(L, v); + lua_setglobal(L, key); + } + + void LuaVM::setGlobalFloat(const char* key, float v) { + LGUARD(L); + lua_pushnumber(L, v); + lua_setglobal(L, key); + } + + void LuaVM::setGlobalString(const char* key, const char* v) { + LGUARD(L); + lua_pushstring(L, v); + lua_setglobal(L, key); + } + + } +} diff --git a/lua_addon/lua_vm.h b/lua_addon/lua_vm.h new file mode 100644 index 0000000..be00c4a --- /dev/null +++ b/lua_addon/lua_vm.h @@ -0,0 +1,34 @@ +#ifndef OPENGTA_SCRIPT_VM_H +#define OPENGTA_SCRIPT_VM_H + +#include "lua.hpp" +#include "Singleton.h" +#include "gl_cityview.h" + +namespace OpenGTA { + namespace Script { + class LuaVM { + public: + LuaVM(); + ~LuaVM(); + void runString(const char*); + void runFile(const char*); + void callSimpleFunction(const char*); + void prepare(); + void setCityView(OpenGTA::CityView &); + int getGlobalInt(const char*); + float getGlobalFloat(const char*); + const char* getGlobalString(const char*); + void setGlobalInt(const char*, int); + void setGlobalFloat(const char*, float); + void setGlobalString(const char*, const char*); + protected: + lua_State *L; + private: + bool _registered; + }; + typedef Loki::SingletonHolder LuaVMHolder; + } +} +#endif diff --git a/lua_addon/lunar.h b/lua_addon/lunar.h new file mode 100644 index 0000000..a886f06 --- /dev/null +++ b/lua_addon/lunar.h @@ -0,0 +1,259 @@ +#ifndef LUNAR_H +#define LUNAR_H + +/** See: http://lua-users.org/wiki/CppBindingWithLunar . + * + * # include-guards + * # fixed old-style cast + * # Register2 function + * + */ + +#include "lua.hpp" + +template class Lunar { + typedef struct { T *pT; } userdataType; + public: + typedef int (T::*mfp)(lua_State *L); + typedef struct { const char *name; mfp mfunc; } RegType; + + static void Register(lua_State *L) { + lua_newtable(L); + int methods = lua_gettop(L); + + luaL_newmetatable(L, T::className); + int metatable = lua_gettop(L); + + // store method table in globals so that + // scripts can add functions written in Lua. + lua_pushvalue(L, methods); + set(L, LUA_GLOBALSINDEX, T::className); + + // hide metatable from Lua getmetatable() + lua_pushvalue(L, methods); + set(L, metatable, "__metatable"); + + lua_pushvalue(L, methods); + set(L, metatable, "__index"); + + lua_pushcfunction(L, tostring_T); + set(L, metatable, "__tostring"); + + lua_pushcfunction(L, gc_T); + set(L, metatable, "__gc"); + + lua_newtable(L); // mt for method table + lua_pushcfunction(L, new_T); + lua_pushvalue(L, -1); // dup new_T function + set(L, methods, "new"); // add new_T to method table + set(L, -3, "__call"); // mt.__call = new_T + lua_setmetatable(L, methods); + + // fill method table with methods from class T + for (RegType *l = T::methods; l->name; l++) { + lua_pushstring(L, l->name); + lua_pushlightuserdata(L, static_cast(l)); + lua_pushcclosure(L, thunk, 1); + lua_settable(L, methods); + } + + lua_pop(L, 2); // drop metatable and method table + } + + static void Register2(lua_State *L) { + lua_newtable(L); + int methods = lua_gettop(L); + + luaL_newmetatable(L, T::className); + int metatable = lua_gettop(L); + + // store method table in globals so that + // scripts can add functions written in Lua. + lua_pushvalue(L, methods); + set(L, LUA_GLOBALSINDEX, T::className); + + // hide metatable from Lua getmetatable() + lua_pushvalue(L, methods); + set(L, metatable, "__metatable"); + + lua_pushvalue(L, methods); + set(L, metatable, "__index"); + + lua_pushcfunction(L, tostring_T); + set(L, metatable, "__tostring"); + + lua_pushcfunction(L, gc_T); + set(L, metatable, "__gc"); + + lua_newtable(L); // mt for method table + lua_setmetatable(L, methods); + + // fill method table with methods from class T + for (RegType *l = T::methods; l->name; l++) { + lua_pushstring(L, l->name); + lua_pushlightuserdata(L, static_cast(l)); + lua_pushcclosure(L, thunk, 1); + lua_settable(L, methods); + } + + lua_pop(L, 2); // drop metatable and method table + } + + + // call named lua method from userdata method table + static int call(lua_State *L, const char *method, + int nargs=0, int nresults=LUA_MULTRET, int errfunc=0) + { + int base = lua_gettop(L) - nargs; // userdata index + if (!luaL_checkudata(L, base, T::className)) { + lua_settop(L, base-1); // drop userdata and args + lua_pushfstring(L, "not a valid %s userdata", T::className); + return -1; + } + + lua_pushstring(L, method); // method name + lua_gettable(L, base); // get method from userdata + if (lua_isnil(L, -1)) { // no method? + lua_settop(L, base-1); // drop userdata and args + lua_pushfstring(L, "%s missing method '%s'", T::className, method); + return -1; + } + lua_insert(L, base); // put method under userdata, args + + int status = lua_pcall(L, 1+nargs, nresults, errfunc); // call method + if (status) { + const char *msg = lua_tostring(L, -1); + if (msg == NULL) msg = "(error with no message)"; + lua_pushfstring(L, "%s:%s status = %d\n%s", + T::className, method, status, msg); + lua_remove(L, base); // remove old message + return -1; + } + return lua_gettop(L) - base + 1; // number of results + } + + // push onto the Lua stack a userdata containing a pointer to T object + static int push(lua_State *L, T *obj, bool gc=false) { + if (!obj) { lua_pushnil(L); return 0; } + luaL_getmetatable(L, T::className); // lookup metatable in Lua registry + if (lua_isnil(L, -1)) luaL_error(L, "%s missing metatable", T::className); + int mt = lua_gettop(L); + subtable(L, mt, "userdata", "v"); + userdataType *ud = + static_cast(pushuserdata(L, obj, sizeof(userdataType))); + if (ud) { + ud->pT = obj; // store pointer to object in userdata + lua_pushvalue(L, mt); + lua_setmetatable(L, -2); + if (gc == false) { + lua_checkstack(L, 3); + subtable(L, mt, "do not trash", "k"); + lua_pushvalue(L, -2); + lua_pushboolean(L, 1); + lua_settable(L, -3); + lua_pop(L, 1); + } + } + lua_replace(L, mt); + lua_settop(L, mt); + return mt; // index of userdata containing pointer to T object + } + + // get userdata from Lua stack and return pointer to T object + static T *check(lua_State *L, int narg) { + userdataType *ud = + static_cast(luaL_checkudata(L, narg, T::className)); + if(!ud) luaL_typerror(L, narg, T::className); + return ud->pT; // pointer to T object + } + + private: + Lunar(); // hide default constructor + + // member function dispatcher + static int thunk(lua_State *L) { + // stack has userdata, followed by method args + T *obj = check(L, 1); // get 'self', or if you prefer, 'this' + lua_remove(L, 1); // remove self so member function args start at index 1 + // get member function from upvalue + RegType *l = static_cast(lua_touserdata(L, lua_upvalueindex(1))); + return (obj->*(l->mfunc))(L); // call member function + } + + // create a new T object and + // push onto the Lua stack a userdata containing a pointer to T object + static int new_T(lua_State *L) { + lua_remove(L, 1); // use classname:new(), instead of classname.new() + T *obj = new T(L); // call constructor for T objects + //push(L, obj, false); // gc_T will delete this object + push(L, obj, true); // gc_T will delete this object + return 1; // userdata containing pointer to T object + } + + // garbage collection metamethod + static int gc_T(lua_State *L) { + if (luaL_getmetafield(L, 1, "do not trash")) { + lua_pushvalue(L, 1); // dup userdata + lua_gettable(L, -2); + if (!lua_isnil(L, -1)) return 0; // do not delete object + } + userdataType *ud = static_cast(lua_touserdata(L, 1)); + T *obj = ud->pT; + if (obj) delete obj; // call destructor for T objects + return 0; + } + + static int tostring_T (lua_State *L) { + char buff[32]; + userdataType *ud = static_cast(lua_touserdata(L, 1)); + T *obj = ud->pT; + sprintf(buff, "%p", obj); + lua_pushfstring(L, "%s (%s)", T::className, buff); + return 1; + } + + static void set(lua_State *L, int table_index, const char *key) { + lua_pushstring(L, key); + lua_insert(L, -2); // swap value and key + lua_settable(L, table_index); + } + + static void weaktable(lua_State *L, const char *mode) { + lua_newtable(L); + lua_pushvalue(L, -1); // table is its own metatable + lua_setmetatable(L, -2); + lua_pushliteral(L, "__mode"); + lua_pushstring(L, mode); + lua_settable(L, -3); // metatable.__mode = mode + } + + static void subtable(lua_State *L, int tindex, const char *name, const char *mode) { + lua_pushstring(L, name); + lua_gettable(L, tindex); + if (lua_isnil(L, -1)) { + lua_pop(L, 1); + lua_checkstack(L, 3); + weaktable(L, mode); + lua_pushstring(L, name); + lua_pushvalue(L, -2); + lua_settable(L, tindex); + } + } + + static void *pushuserdata(lua_State *L, void *key, size_t sz) { + void *ud = 0; + lua_pushlightuserdata(L, key); + lua_gettable(L, -2); // lookup[key] + if (lua_isnil(L, -1)) { + lua_pop(L, 1); // drop nil + lua_checkstack(L, 3); + ud = lua_newuserdata(L, sz); // create new userdata + lua_pushlightuserdata(L, key); + lua_pushvalue(L, -2); // dup userdata + lua_settable(L, -4); // lookup[key] = userdata + } + return ud; + } +}; + +#endif diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..9ee6791 --- /dev/null +++ b/main.cpp @@ -0,0 +1,32 @@ +#include +#include + +void on_exit(); +void run_main(); +void initGL(); +void initVideo(int w, int h, int bpp); + +int global_EC = 0; +int global_Done = 0; +SDL_Surface* screen = NULL; +SDL_Surface* surface = NULL; + +int city_num = 0; + +int main(int argc, char* argv[]) { + if (SDL_Init(SDL_INIT_VIDEO) < 0) { + std::cerr << "Fatal error initialising SDL!" << std::endl; + global_EC = 1; + exit(1); + } + atexit(on_exit); + if (argc == 2) { + city_num = atoi(argv[1]); + } + SDL_EnableKeyRepeat( 100, SDL_DEFAULT_REPEAT_INTERVAL ); + initVideo(1024, 768, 32); + initGL(); + + run_main(); + exit(0); +} diff --git a/main2.cpp b/main2.cpp new file mode 100644 index 0000000..c4f88d4 --- /dev/null +++ b/main2.cpp @@ -0,0 +1,58 @@ +#include +#include +#include +#include "m_exceptions.h" +#include "log.h" + +extern void parse_args(int argc, char* argv[]); +extern void on_exit(); +extern void run_init(); +extern void run_main(); + +int global_EC = 0; +int global_Done = 0; + +#ifndef DONT_CATCH +bool catch_exceptions = true; +#else +bool catch_exceptions = false; +#endif + +using namespace std; + +int main(int argc, char* argv[]) { + if (argc > 1) + parse_args(argc, argv); + + atexit(on_exit); + + if (!catch_exceptions) + INFO << "ignoring exceptions" << std::endl; + + if (!catch_exceptions) + run_init(); + else { + try { + run_init(); + } + catch (Exception & e) { + ERROR << "Exception during startup: " << e.what() << endl; + global_EC = 1; + exit(1); + } + } + if (!catch_exceptions) + run_main(); + else { + try { + run_main(); + } + catch (const Exception & e) { + ERROR << "Exception during game: " << e.what() << endl; + global_EC = 1; + exit(1); + } + + } + std::exit(0); +} diff --git a/makefile b/makefile new file mode 100644 index 0000000..ec6a03a --- /dev/null +++ b/makefile @@ -0,0 +1,78 @@ +include src_list.make + +INC_EX_LIBS = $(PHYSFS_INC) $(SDL_INC) $(LUA_INC) +INC_INTERN = -I. -Iloki/include/loki -Iutil -Icoldet -Imath + +INC = $(INC_INTERN) $(INC_EX_LIBS) + +FLAGS = $(WARN) $(DEBUG) $(OPT) + +BUILD_FOR = $(shell if [ -n "$(HOST)" ]; then echo $(HOST) ; else echo "LINUX"; fi) + +#SDL_GL_LIB = -lGL -lGLU + +LOKI_LIB = -Wl,-Rloki/lib -Lloki/lib -lloki +#COLDET_LIB = -Wl,-Rcoldet -Lcoldet -lcoldet + +CXXFLAGS=$(FLAGS) $(DEFS) \ +$(INC) + +src_list.make: prepare_build.sh + ./prepare_build.sh $(BUILD_FOR) + $(MAKE) depend + +%.o: %.cpp + $(CXX) $(FLAGS) $(DEFS) \ + $(INC) \ + -c -o $@ $< + +%.o: %.c + $(CC) $(CCFLAGS) \ + -c -o $@ $< + +loki: +ifeq ($(BUILD_FOR), LINUX) + make -f loki.make +else + make -f loki.make.w32_cross +endif + +ctags: + ctags *.cpp util/*.cpp + +coldet/libcoldet.a: + make -C coldet -f makefile.g++ all + +clean: + rm -f tags depend *.o tools/*.o coldet/*.o lua_addon/*.o math/*.o util/*.o \ + spriteplayer gfxextract viewer $(TOOLS) objdump objdump_map minimap slopeview luaviewer g24 + +html: doxy_main.h + doxygen +doxygen: doxy_main.h + doxygen + +doxy_main.h: doc/hacking.txt tools/doxy_doc.sh + ./tools/doxy_doc.sh > doxy_main.h + +doxyclean: + $(RM) doxy_main.h -r doc/html + +package: + (cd .. && tar jvcf ogta_src_`date +%F`.tar.bz2 -T ogta/release_files_sorted) + @echo "saved as: ogta_src_`date +%F.tar.bz2`" + +rclean: + make libclean + make clean + make doxyclean + rm -f src_list.make + +libclean: + make -C coldet -f makefile.g++ clean + + +depend: loki src_list.make + $(RM) depend + $(CXX) $(CXXFLAGS) -DGCC -E -MM $(GL_SRC) $(OGTA_SRC) $(UTIL_SRC) > depend + diff --git a/math/basis.hpp b/math/basis.hpp new file mode 100644 index 0000000..7757d25 --- /dev/null +++ b/math/basis.hpp @@ -0,0 +1,114 @@ +#ifndef GOMEZ_Basis_H +#define GOMEZ_Basis_H + +/** + * Taken from: + * http://www.gamasutra.com/features/19990702/data_structures_01.htm + * http://www.gamasutra.com/features/19991018/Gomez_1.htm + * + * Both by Miguel Gomez + */ + + +#include "matrix.hpp" + +namespace GomezMath { +// An orthonormal basis with respect to a parent +// +class Basis +{ +public: + Matrix R; +public: + Basis () + { + } + Basis (const Vector & v0, + const Vector & v1, const Vector & v2):R (v0, v1, v2) + { + } + Basis (const Matrix & m):R (m) + { + } + const Vector & operator [] (long i) const + { + return R.C[i]; + } + const Vector & x () const + { + return R.C[0]; + } + const Vector & y () const + { + return R.C[1]; + } + const Vector & z () const + { + return R.C[2]; + } + const Matrix & basis () const + { + return R; + } + void basis (const Vector & v0, const Vector & v1, const Vector & v2) + { + this->R[0] = v0; + this->R[1] = v1; + this->R[2] = v2; + } +// Right-Handed Rotations + void rotateAboutX (const Scalar & a) + { + if (0 != a) //don’t rotate by 0 + { + Vector b1 = this->y () * cos (a) + this->z () * sin (a); + Vector b2 = -this->y () * sin (a) + this->z () * cos (a); +//set basis + this->R[1] = b1; + this->R[2] = b2; +//x is unchanged + } + } + void rotateAboutY (const Scalar & a) + { + if (0 != a) //don’t rotate by 0 + { + Vector b2 = this->z () * cos (a) + this->x () * sin (a); //rotate z + Vector b0 = -this->z () * sin (a) + this->x () * cos (a); //rotate x +//set basis + this->R[2] = b2; + this->R[0] = b0; +//y is unchanged + } + } + void rotateAboutZ (const Scalar & a) + { + if (0 != a) //don’t rotate by 0 + { +//don’t over-write basis before calculation is done + Vector b0 = this->x () * cos (a) + this->y () * sin (a); //rotate x + Vector b1 = -this->x () * sin (a) + this->y () * cos (a); //rotate y +//set basis + this->R[0] = b0; + this->R[1] = b1; +//z is unchanged + } + } +//rotate the basis about the unit axis u by theta (radians) + void rotate (const Scalar & theta, const Vector & u); +//rotate, length of da is theta, unit direction of da is u + void rotate (const Vector & da); + +// Transformations + const Vector transformVectorToLocal (const Vector & v) const + { + return Vector (R.C[0].dot (v), R.C[1].dot (v), R.C[2].dot (v)); + } + const Point transformVectorToParent (const Vector & v) const + { + return R.C[0] * v.x + R.C[1] * v.y + R.C[2] * v.z; + } +}; + +} +#endif diff --git a/math/coord_frame.hpp b/math/coord_frame.hpp new file mode 100644 index 0000000..e5ea112 --- /dev/null +++ b/math/coord_frame.hpp @@ -0,0 +1,60 @@ +#ifndef GOMEZ_COORDFRAME_H +#define GOMEZ_COORDFRAME_H + +/** + * Taken from: + * http://www.gamasutra.com/features/19990702/data_structures_01.htm + * http://www.gamasutra.com/features/19991018/Gomez_1.htm + * + * Both by Miguel Gomez + */ + + +#include "basis.hpp" +namespace GomezMath { + +// A coordinate frame (basis and origin) with respect to a parent +// +class CoordFrame:public Basis +{ +public: + Point O; //this coordinate frame’s origin, relative to its parent frame +public: + CoordFrame () + { + } + CoordFrame (const Point & o, + const Vector & v0, + const Vector & v1, const Vector & v2): Basis (v0, v1, v2), O(o) + { + } + CoordFrame (const Point & o, const Basis & b): Basis (b), O(o) + { + } + const Point & position () const + { + return O; + } + void position (const Point & p) + { + O = p; + } + const Point transformPointToLocal (const Point & p) const + { +//translate to this frame’s origin, then project onto this basis + return transformVectorToLocal (p - O); + } + const Point transformPointToParent (const Point & p) const + { +//transform the coordinate vector and translate by this origin + return transformVectorToParent (p) + O; + } +//translate the origin by the given vector + void translate (const Vector & v) + { + O += v; + } +}; +} + +#endif diff --git a/math/makefile b/math/makefile new file mode 100644 index 0000000..ce32003 --- /dev/null +++ b/math/makefile @@ -0,0 +1,11 @@ +GMATH_FILES = obb.cpp +GMATH_OBJ = $(GMATH_FILES:%.cpp=%.o) + +all: libgomezmath.a + +libgomezmath.a: $(GMATH_OBJ) + rm -f $@ + ar cq $@ $(GMATH_OBJ) + +clean: + rm -f libgomezmath.a $(GMATH_OBJ) diff --git a/math/matrix.hpp b/math/matrix.hpp new file mode 100644 index 0000000..0216a56 --- /dev/null +++ b/math/matrix.hpp @@ -0,0 +1,151 @@ +#ifndef GOMEZ_Matrix_H +#define GOMEZ_Matrix_H + +#include "vector.hpp" +namespace GomezMath { +/** + * Taken from: + * http://www.gamasutra.com/features/19990702/data_structures_01.htm + * http://www.gamasutra.com/features/19991018/Gomez_1.htm + * + * Both by Miguel Gomez + */ + + +// A 3x3 matrix +// +class Matrix +{ +public: + Vector C[3]; //column vectors +public: + Matrix () + { +//identity matrix + C[0].x = 1; + C[1].y = 1; + C[2].z = 1; + } + Matrix (const Vector & c0, const Vector & c1, const Vector & c2) + { + C[0] = c0; + C[1] = c1; + C[2] = c2; + } +//index a column, allow assignment +//NOTE: using this index operator along with the vector index +//gives you M[column][row], not the standard M[row][column] + Vector & operator [](long i) + { + return C[i]; + } +//compare + const bool operator == (const Matrix & m) const + { + return C[0] == m.C[0] && C[1] == m.C[1] && C[2] == m.C[2]; + } + const bool operator != (const Matrix & m) const + { + return !(m == *this); + } +//assign + const Matrix & operator = (const Matrix & m) + { + C[0] = m.C[0]; + C[1] = m.C[1]; + C[2] = m.C[2]; + return *this; + } +//increment + const Matrix & operator += (const Matrix & m) + { + C[0] += m.C[0]; + C[1] += m.C[1]; + C[2] += m.C[2]; + return *this; + } +//decrement + const Matrix & operator -= (const Matrix & m) + { + C[0] -= m.C[0]; + C[1] -= m.C[1]; + C[2] -= m.C[2]; + return *this; + } +//self-multiply by a scalar + const Matrix & operator *= (const Scalar & s) + { + C[0] *= s; + C[1] *= s; + C[2] *= s; + return *this; + } +//self-multiply by a matrix + const Matrix & operator *= (const Matrix & m) + { +//NOTE: don’t change the columns +//in the middle of the operation + Matrix temp = (*this); + C[0] = temp * m.C[0]; + C[1] = temp * m.C[1]; + C[2] = temp * m.C[2]; + return *this; + } +//add + const Matrix operator + (const Matrix & m) const + { + return Matrix (C[0] + m.C[0], C[1] + m.C[1], C[2] + m.C[2]); + } +//subtract + const Matrix operator - (const Matrix & m) const + { + return Matrix (C[0] - m.C[0], C[1] - m.C[1], C[2] - m.C[2]); + } +//post-multiply by a scalar + const Matrix operator * (const Scalar & s) const + { + return Matrix (C[0] * s, C[1] * s, C[2] * s); + } +//pre-multiply by a scalar + friend inline const Matrix operator * (const Scalar & s, const Matrix & m) + { + return m * s; + } + +//post-multiply by a vector + const Vector operator * (const Vector & v) const + { + return (C[0] * v.x + C[1] * v.y + C[2] * v.z); + } +//pre-multiply by a vector + inline friend const Vector operator * (const Vector & v, const Matrix & m) + { + return Vector (m.C[0].dot (v), m.C[1].dot (v), m.C[2].dot (v)); + } +//post-multiply by a matrix + const Matrix operator * (const Matrix & m) const + { + return Matrix ((*this) * m.C[0], (*this) * m.C[1], (*this) * m.C[2]); + } +//transpose + Matrix transpose () const + { +//turn columns on their side + return Matrix (Vector (C[0].x, C[1].x, C[2].x), //column 0 + Vector (C[0].y, C[1].y, C[2].y), //column 1 + Vector (C[0].z, C[1].z, C[2].z) //column 2 + ); + } +//scalar determinant + const Scalar determinant () const + { +//Lang, "Linear Algebra", p. 143 + return C[0].dot (C[1].cross (C[2])); + } +//matrix inverse + const Matrix inverse () const; +}; + +} + +#endif diff --git a/math/obb.cpp b/math/obb.cpp new file mode 100644 index 0000000..b1f13c3 --- /dev/null +++ b/math/obb.cpp @@ -0,0 +1,126 @@ +#ifndef GOMEZ_OBB_H +#define GOMEZ_OBB_H + +/** + * Taken from: + * http://www.gamasutra.com/features/19990702/data_structures_01.htm + * http://www.gamasutra.com/features/19991018/Gomez_1.htm + * + * Both by Miguel Gomez + */ + +#include "obb.hpp" +#include "vector.hpp" + +namespace GomezMath { + + //check if two oriented bounding boxes overlap + const bool OBBOverlap ( + //A + Vector & a, //extents + Vector & Pa, //position + Vector * A, //orthonormal basis + //B + Vector & b, //extents + Vector & Pb, //position + Vector * B //orthonormal basis + ) + { + //translation, in parent frame + Vector v = Pb - Pa; + //translation, in A's frame + Vector T (v.dot (A[0]), v.dot (A[1]), v.dot (A[2])); + //B's basis with respect to A's local frame + Scalar R[3][3]; + float ra, rb, t; + long i, k; + //calculate rotation matrix + for (i = 0; i < 3; i++) + for (k = 0; k < 3; k++) + R[i][k] = A[i].dot (B[k]); + /*ALGORITHM: Use the separating axis test for all 15 potential + separating axes. If a separating axis could not be found, the two + boxes overlap. */ + //A's basis vectors + for (i = 0; i < 3; i++) + { + ra = a[i]; + rb = + b[0] * fabs (R[i][0]) + b[1] * fabs (R[i][1]) + b[2] * fabs (R[i][2]); + t = fabs (T[i]); + if (t > ra + rb) + return false; + } + //B's basis vectors + for (k = 0; k < 3; k++) + { + ra = + a[0] * fabs (R[0][k]) + a[1] * fabs (R[1][k]) + a[2] * fabs (R[2][k]); + rb = b[k]; + t = fabs (T[0] * R[0][k] + T[1] * R[1][k] + T[2] * R[2][k]); + if (t > ra + rb) + return false; + } + //9 cross products + //L = A0 x B0 + ra = a[1] * fabs (R[2][0]) + a[2] * fabs (R[1][0]); + rb = b[1] * fabs (R[0][2]) + b[2] * fabs (R[0][1]); + t = fabs (T[2] * R[1][0] - T[1] * R[2][0]); + if (t > ra + rb) + return false; + //L = A0 x B1 + ra = a[1] * fabs (R[2][1]) + a[2] * fabs (R[1][1]); + rb = b[0] * fabs (R[0][2]) + b[2] * fabs (R[0][0]); + t = fabs (T[2] * R[1][1] - T[1] * R[2][1]); + if (t > ra + rb) + return false; + //L = A0 x B2 + ra = a[1] * fabs (R[2][2]) + a[2] * fabs (R[1][2]); + rb = b[0] * fabs (R[0][1]) + b[1] * fabs (R[0][0]); + t = fabs (T[2] * R[1][2] - T[1] * R[2][2]); + if (t > ra + rb) + return false; + //L = A1 x B0 + ra = a[0] * fabs (R[2][0]) + a[2] * fabs (R[0][0]); + rb = b[1] * fabs (R[1][2]) + b[2] * fabs (R[1][1]); + t = fabs (T[0] * R[2][0] - T[2] * R[0][0]); + if (t > ra + rb) + return false; + //L = A1 x B1 + ra = a[0] * fabs (R[2][1]) + a[2] * fabs (R[0][1]); + rb = b[0] * fabs (R[1][2]) + b[2] * fabs (R[1][0]); + t = fabs (T[0] * R[2][1] - T[2] * R[0][1]); + if (t > ra + rb) + return false; + //L = A1 x B2 + ra = a[0] * fabs (R[2][2]) + a[2] * fabs (R[0][2]); + rb = b[0] * fabs (R[1][1]) + b[1] * fabs (R[1][0]); + t = fabs (T[0] * R[2][2] - T[2] * R[0][2]); + if (t > ra + rb) + return false; + //L = A2 x B0 + ra = a[0] * fabs (R[1][0]) + a[1] * fabs (R[0][0]); + rb = b[1] * fabs (R[2][2]) + b[2] * fabs (R[2][1]); + t = fabs (T[1] * R[0][0] - T[0] * R[1][0]); + if (t > ra + rb) + return false; + //L = A2 x B1 + ra = a[0] * fabs (R[1][1]) + a[1] * fabs (R[0][1]); + rb = b[0] * fabs (R[2][2]) + b[2] * fabs (R[2][0]); + t = fabs (T[1] * R[0][1] - T[0] * R[1][1]); + if (t > ra + rb) + return false; + //L = A2 x B2 + ra = a[0] * fabs (R[1][2]) + a[1] * fabs (R[0][2]); + rb = b[0] * fabs (R[2][1]) + b[1] * fabs (R[2][0]); + t = fabs (T[1] * R[0][2] - T[0] * R[1][2]); + if (t > ra + rb) + return false; + /*no separating axis found, + the two boxes overlap */ + return true; + } + +} + +#endif diff --git a/math/obb.hpp b/math/obb.hpp new file mode 100644 index 0000000..a21fd4c --- /dev/null +++ b/math/obb.hpp @@ -0,0 +1,42 @@ +#ifndef GOMEZ_OBB_H +#define GOMEZ_OBB_H + +/** + * Taken from: + * http://www.gamasutra.com/features/19990702/data_structures_01.htm + * http://www.gamasutra.com/features/19991018/Gomez_1.htm + * + * Both by Miguel Gomez + */ + +#include "coord_frame.hpp" + +namespace GomezMath { + class OBB : public CoordFrame + { + public: + Vector E; //extents + OBB (const Vector & e) : E (e) + { + } + OBB (const Vector & e, const Vector & origin, + const Vector & v0, const Vector & v1, const Vector & v2) : + CoordFrame(origin, v0, v1, v2), E(e) + { + } + }; + + const bool OBBOverlap ( + //A + Vector & a, //extents + Vector & Pa, //position + Vector * A, //orthonormal basis + //B + Vector & b, //extents + Vector & Pb, //position + Vector * B //orthonormal basis + ); + +} + +#endif diff --git a/math/obox.cpp b/math/obox.cpp new file mode 100644 index 0000000..ab647e7 --- /dev/null +++ b/math/obox.cpp @@ -0,0 +1,164 @@ +#include "obox.h" +// -------------------------- +// +// Oriented Bounding Box Class +// +// -------------------------- + +// +// Check if a point is in this bounding box +// +bool OBox::IsPointInBox(const Vector3D &InP) +{ + // Rotate the point into the box's coordinates + Vector3D P = Transform(InP, m_M.Inverse()); + + // Now just use an axis-aligned check + if ( fabs(P.x) < m_Extent.x && fabs(P.y) < m_Extent.y && fabs(P.z) < m_Extent.z ) + return true; + + return false; +} + +// +// Check if a sphere overlaps any part of this bounding box +// +bool OBox::IsSphereInBox( const Vector3D &InP, float fRadius) +{ + float fDist; + float fDistSq = 0; + Vector3D P = Transform(InP, m_M.Inverse()); + + // Add distance squared from sphere centerpoint to box for each axis + for ( int i = 0; i < 3; i++ ) + { + if ( fabs(P[i]) > m_Extent[i] ) + { + fDist = fabs(P[i]) - m_Extent[i]; + fDistSq += fDist*fDist; + } + } + return ( fDistSq <= fRadius*fRadius ); +} + +// +// Check if the bounding box is completely behind a plane( defined by a normal and a point ) +// +bool OBox::BoxOutsidePlane( const Vector3D &InNorm, const Vector3D &InP ) +{ + // Plane Normal in Box Space + Vector3D Norm = rotateVector(InNorm, m_M.Inverse() ); + Norm = Vector3D( fabs( Norm.x ), fabs( Norm.y ), fabs( Norm.z ) ); + + float Extent = Norm * m_Extent; //Norm.Dot( m_Extent ); // Box Extent along the plane normal + //float Distance = InNorm.Dot( GetCenterPoint() - InP ); // Distance from Box Center to the Plane + float Distance = InNorm * (GetCenterPoint() - InP); + + // If Box Centerpoint is behind the plane further than its extent, the Box is outside the plane + if ( Distance < -Extent ) return true; + return false; +} + +// +// Does the Line (L1, L2) intersect the Box? +// +bool OBox::IsLineInBox( const Vector3D& L1, const Vector3D& L2 ) +{ + // Put line in box space + Matrix3D MInv = m_M.Inverse(); + Vector3D LB1 = Transform(L1, MInv); + Vector3D LB2 = Transform(L2, MInv); + + // Get line midpoint and extent + Vector3D LMid = (LB1 + LB2) * 0.5f; + Vector3D L = (LB1 - LMid); + Vector3D LExt = Vector3D( fabs(L.x), fabs(L.y), fabs(L.z) ); + + // Use Separating Axis Test + // Separation vector from box center to line center is LMid, since the line is in box space + if ( fabs( LMid.x ) > m_Extent.x + LExt.x ) return false; + if ( fabs( LMid.y ) > m_Extent.y + LExt.y ) return false; + if ( fabs( LMid.z ) > m_Extent.z + LExt.z ) return false; + // Crossproducts of line and each axis + if ( fabs( LMid.y * L.z - LMid.z * L.y) > (m_Extent.y * LExt.z + m_Extent.z * LExt.y) ) return false; + if ( fabs( LMid.x * L.z - LMid.z * L.x) > (m_Extent.x * LExt.z + m_Extent.z * LExt.x) ) return false; + if ( fabs( LMid.x * L.y - LMid.y * L.x) > (m_Extent.x * LExt.y + m_Extent.y * LExt.x) ) return false; + // No separating axis, the line intersects + return true; +} + +// +// Returns a 3x3 rotation matrix as vectors +// +inline void OBox::GetInvRot( Vector3D *pvRot ) +{ + pvRot[0] = Vector3D( m_M.m[0][0], m_M.m[0][1], m_M.m[0][2] ); + pvRot[1] = Vector3D( m_M.m[1][0], m_M.m[1][1], m_M.m[1][2] ); + pvRot[2] = Vector3D( m_M.m[2][0], m_M.m[2][1], m_M.m[2][2] ); +} + +// +// Check if any part of a box is inside any part of another box +// Uses the separating axis test. +// +bool OBox::IsBoxInBox( OBox &BBox ) +{ + Vector3D SizeA = m_Extent; + Vector3D SizeB = BBox.m_Extent; + Vector3D RotA[3], RotB[3]; + GetInvRot( RotA ); + BBox.GetInvRot( RotB ); + + float R[3][3]; // Rotation from B to A + float AR[3][3]; // absolute values of R matrix, to use with box extents + float ExtentA, ExtentB, Separation; + int i, k; + + // Calculate B to A rotation matrix + for( i = 0; i < 3; i++ ) + for( k = 0; k < 3; k++ ) + { + R[i][k] = RotA[i] * RotB[k]; + AR[i][k] = fabs(R[i][k]); + } + + // Vector separating the centers of Box B and of Box A + Vector3D vSepWS = BBox.GetCenterPoint() - GetCenterPoint(); + // Rotated into Box A's coordinates + Vector3D vSepA( vSepWS * RotA[0], vSepWS * RotA[1], vSepWS * RotA[2] ); + + // Test if any of A's basis vectors separate the box + for( i = 0; i < 3; i++ ) + { + ExtentA = SizeA[i]; + ExtentB = SizeB * Vector3D( AR[i][0], AR[i][1], AR[i][2] ); + Separation = fabs( vSepA[i] ); + + if( Separation > ExtentA + ExtentB ) return false; + } + + // Test if any of B's basis vectors separate the box + for( k = 0; k < 3; k++ ) + { + ExtentA = SizeA * Vector3D( AR[0][k], AR[1][k], AR[2][k] ); + ExtentB = SizeB[k]; + Separation = fabs( vSepA * Vector3D(R[0][k],R[1][k],R[2][k]) ); + + if( Separation > ExtentA + ExtentB ) return false; + } + + // Now test Cross Products of each basis vector combination ( A[i], B[k] ) + for( i=0 ; i<3 ; i++ ) + for( k=0 ; k<3 ; k++ ) + { + int i1 = (i+1)%3, i2 = (i+2)%3; + int k1 = (k+1)%3, k2 = (k+2)%3; + ExtentA = SizeA[i1] * AR[i2][k] + SizeA[i2] * AR[i1][k]; + ExtentB = SizeB[k1] * AR[i][k2] + SizeB[k2] * AR[i][k1]; + Separation = fabs( vSepA[i2] * R[i1][k] - vSepA[i1] * R[i2][k] ); + if( Separation > ExtentA + ExtentB ) return false; + } + + // No separating axis found, the boxes overlap + return true; +} diff --git a/math/obox.h b/math/obox.h new file mode 100644 index 0000000..ef66a8b --- /dev/null +++ b/math/obox.h @@ -0,0 +1,43 @@ +#include "math3d.h" + +// see: from: http://www.3dkingdoms.com/weekly/weekly.php?a=21 + +// basically the same as bbox.h/.cpp but using coldet math + +class OBox +{ + public: + OBox() {} + OBox( const Matrix3D & m, const Vector3D & extent ) + { Set( m, extent ); } + OBox( const Matrix3D & m, const Vector3D & low, const Vector3D & high ) + { Set( m, low, high ); } + + void Set( const Matrix3D & m, const Vector3D & extent ) + { + m_M = m; + m_Extent = extent; + } + void Set( const Matrix3D & m, const Vector3D & low, const Vector3D & high ) + { + m_M = m; + m_M.Translate( 0.5f * (low + high) ); + m_Extent = 0.5f * (high - low); + } + + Vector3D GetSize() + { return 2.0f * m_Extent; } + Vector3D GetCenterPoint() + { return m_M.GetTranslate(); } + void GetInvRot( Vector3D *pvRot ); + + bool IsPointInBox( const Vector3D & p ); + bool IsBoxInBox( OBox & box ); + bool IsSphereInBox( const Vector3D & p, float fRadius ); + bool IsLineInBox( const Vector3D & l1, const Vector3D & l2 ); + bool BoxOutsidePlane( const Vector3D & normal, const Vector3D & p ); + + // Data + Matrix3D m_M; + Vector3D m_Extent; +}; diff --git a/math/quaternion.h b/math/quaternion.h new file mode 100644 index 0000000..5e38e85 --- /dev/null +++ b/math/quaternion.h @@ -0,0 +1,184 @@ +#ifndef QUATERNION_MATH +#define QUATERNION_MATH +#include + +namespace Util { namespace Math { + /*! + * \namespace Util::Math + * \brief Vector/Matrix/Quaternion interpolation + * + * Found at http://www.flipcode.com/files/code/vquats.cpp + * + * + * I think this is very sleek, so I kept it. + * + * Editor's note: + * COTD Entry: Vector Math & Quaternions by Tim Sweeney [tim@epicgames.com] + + This is some fairly generic vector math code. Here I want to + illustrate two things: + + 1. The "vectorSpace" template is a nice way to represent the + general mathematical notion of vectors without regard to + particular data types. The idea is that you can declare + a vector type (like vec4 below), and the vectorSpace template + automatically generates the appropriate vector operators. + + I wrote this code for clarity and generality. For performance, you would + want to specialize the vectorSpace template for common types, like 4- + component floating-point vectors. You could implement these using + KNI and 3DNow instructions, for example. + + 2. Useful quaternion functions, most notably "exp" and "ln". + + The traditional and limited way programmers used quaternion + rotations in the past was to use "spherical linear interpolation", + interpolating a rotational arc between two quaternions at a constant + angular rate. Given quaternions q1 and q2, you might have used + a function lik "slerp(q1,q2,0.25)" to find a rotation + one-quarter between the two quaternions. This approach + was unsatisfactory because it was difficult to perform + smooth, higher order, i.e. hermite or bezier, interpolation + between quaternions. + + New approach: take the logarithm ("ln" function below) of + your quaternions, then use ordinary linear interpolation + (sampleLinear below) or any higher-order interpolation + scheme -- and then take the exponent ("exp") of the result. + The resulting rotations will have the desired smooth angular + movement rates. + + Why does this work? Read up on "geometric algebra" to find out. + Unit quaternions are just a special 3-dimensional case of + spinors, which are the most general representation of + n-dimensional rotations possible. + */ + + // Template implementing a "vector space" (in the precise mathematical sense). + // A vector space is a type "u", implemented as + // an array of "n" elements of type "t". + template struct vectorSpace { + t x[n]; + vectorSpace() {for(int i=0; i=0 && i=0 && i inline t sampleLinear(const t& a,const t& b,float s) { + return (1-s)*a+s*b; + } + template inline t sampleHermite(const t& a,const t& b,const t& at,const t& bt,float s) { + return + (+2*s*s*s -3*s*s +1)*a+ + (-2*s*s*s +3*s*s )*b+ + (+1*s*s*s -2*s*s +s )*at+ + (+1*s*s*s -1*s*s )*bt; + } + template inline t sampleBezier(const t& a,const t& b,const t& ac,const t& bc,float s) { + return + (-1*s*s*s +3*s*s -3*s +1)*a+ + (+3*s*s*s -6*s*s +3*s )*b+ + (-3*s*s*s +3*s*s )*ac+ + (+1*s*s*s )*bc; + } + + // 4-component floating point vectors. + struct vec4: public vectorSpace { + inline vec4() {x[0]=x[1]=x[2]=x[3]=0;} + inline vec4(float _x,float _y,float _z,float _w) {x[0]=_x; x[1]=_y; x[2]=_z; x[3]=_w;} + }; + + // 4x4 matrices. + struct mtx44: public vectorSpace { + mtx44() {} + mtx44(const vec4& _x,const vec4& _y, const vec4& _z,const vec4& _w) {x[0]=_x; x[1]=_y; x[2]=_z; x[3]=_w;} + }; + + // A 3-dimensional (4-component) quaternion. + struct quat: public vectorSpace { + quat() {x[0]=x[1]=x[2]=0; x[3]=0;} + quat(float _x,float _y,float _z,float _w) {x[0]=_x; x[1]=_y; x[2]=_z; x[3]=_w;} + }; + inline quat operator*(const quat& a,const quat& b) { + return quat( + +a[0]*b[3] +a[1]*b[2] -a[2]*b[1] +a[3]*b[0], + -a[0]*b[2] +a[1]*b[3] +a[2]*b[0] +a[3]*b[1], + +a[0]*b[1] -a[1]*b[0] +a[2]*b[3] +a[3]*b[2], + -a[0]*b[0] -a[1]*b[1] -a[2]*b[2] +a[3]*b[3] + ); + } + inline float norm(const quat& a) { + return a[0]*a[0] + a[1]*a[1] + a[2]*a[2] + a[3]*a[3]; + } + inline float mag(const quat& a) { + return sqrt(norm(a)); + } + inline quat normal(const quat& a) { + return a/mag(a); + } + inline quat conj(const quat& a) { + return quat(-a[0],-a[1],-a[2],a[3]); + } + inline quat inv(const quat& a) { + return 1.f/norm(a)*conj(a); + } + inline quat exp(const quat& a) { + float r = sqrt(a[0]*a[0]+a[1]*a[1]+a[2]*a[2]); + float et = std::exp(a[3]); + float s = r>=0.00001f? et*sin(r)/r: 0.f; + return quat(s*a[0],s*a[1],s*a[2],et*cos(r)); + } + inline quat ln(const quat& a) { + float r = sqrt(a[0]*a[0]+a[1]*a[1]+a[2]*a[2]); + float t = r>0.00001f? atan2(r,a[3])/r: 0.f; + return quat(t*a[0],t*a[1],t*a[2],0.5*log(norm(a))); + } + inline vec4 quatToRotationAxisAngle(const quat& a) { + float t = a[3]*a[3]; + float rsa = t<0.99999999? 1.f/sqrt(1.f-t): 1.f; + return vec4(a[0]*rsa,a[1]*rsa,a[2]*rsa,acos(a[3])*2.f); + } + inline quat quatFromRotationAxisAngle(const vec4& a) { + float s = sin(a[3]/2.f); + float c = cos(a[3]/2.f); + return quat(a[0]*s,a[1]*s,a[2]*s,c); + } + inline mtx44 quatToRotationMatrix(const quat& a) { + float xx=a[0]*a[0]; + float xy=a[0]*a[1], yy=a[1]*a[1]; + float xz=a[0]*a[2], yz=a[1]*a[2], zz=a[2]*a[2]; + float xw=a[0]*a[3], yw=a[1]*a[3], zw=a[2]*a[3], ww=a[3]*a[3]; + return mtx44( + vec4(+xx-yy-zz+ww, +xy+zw+xy+zw, +xz-yw+xz-yw, 0), + vec4(+xy-zw+xy-zw, -xx+yy-zz+ww, +yz+xw+yz+xw, 0), + vec4(+xz+yw+xz+yw, +yz-xw+yz-xw, -xx-yy+zz+ww, 0), + vec4(0 , 0 , 0 , 1) + ); + } + inline quat quatFromRotationMatrix(const mtx44& m) { + float w=0.5*sqrt(m[0][0]+m[1][1]+m[2][2]+m[3][3]); + float s=0.25/w; + return quat( + (m[1][2]-m[2][1])*s, + (m[2][0]-m[0][2])*s, + (m[0][1]-m[1][0])*s, + w + ); + } +} +} + +#endif diff --git a/math/vector.hpp b/math/vector.hpp new file mode 100644 index 0000000..62252e0 --- /dev/null +++ b/math/vector.hpp @@ -0,0 +1,159 @@ +#ifndef GOMEZ_Vector_H +#define GOMEZ_Vector_H +#include +/** + * Taken from: + * http://www.gamasutra.com/features/19990702/data_structures_01.htm + * http://www.gamasutra.com/features/19991018/Gomez_1.htm + * + * Both by Miguel Gomez + */ + +namespace GomezMath { + +// A floating point number +// +typedef float Scalar; + +// +// A 3D vector +// +class Vector +{ +public: + Scalar x, y, z; //x,y,z coordinates +public: + Vector ():x (0), y (0), z (0) + { + } + Vector (const Scalar & a, const Scalar & b, const Scalar & c):x (a), y (b), + z (c) + { + } +//index a component +//NOTE: returning a reference allows +//you to assign the indexed element + Scalar & operator [](const long i) + { + return *((&x) + i); + } +//compare + const bool operator == (const Vector & v) const + { + return (v.x == x && v.y == y && v.z == z); + } + const bool operator != (const Vector & v) const + { + return !(v == *this); + } +//negate + const Vector operator - () const + { + return Vector (-x, -y, -z); + } +//assign + const Vector & operator = (const Vector & v) + { + x = v.x; + y = v.y; + z = v.z; + return *this; + } +//increment + const Vector & operator += (const Vector & v) + { + x += v.x; + y += v.y; + z += v.z; + return *this; + } +//decrement + const Vector & operator -= (const Vector & v) + { + x -= v.x; + y -= v.y; + z -= v.z; + return *this; + } +//self-multiply + const Vector & operator *= (const Scalar & s) + { + x *= s; + y *= s; + z *= s; + return *this; + } +//self-divide + const Vector & operator /= (const Scalar & s) + { + const Scalar r = 1 / s; + x *= r; + y *= r; + z *= r; + return *this; + } +//add + const Vector operator + (const Vector & v) const + { + return Vector (x + v.x, y + v.y, z + v.z); + } +//subtract + const Vector operator - (const Vector & v) const + { + return Vector (x - v.x, y - v.y, z - v.z); + } +//post-multiply by a scalar + const Vector operator * (const Scalar & s) const + { + return Vector (x * s, y * s, z * s); + } +//pre-multiply by a scalar + friend inline const Vector operator * (const Scalar & s, const Vector & v) + { + return v * s; + } +//divide + const Vector operator / (Scalar s) const + { + s = 1 / s; + return Vector (s * x, s * y, s * z); + } +//cross product + const Vector cross (const Vector & v) const + { +//Davis, Snider, "Introduction to Vector Analysis", p. 44 + return Vector (y * v.z - z * v.y, z * v.x - x * v.z, x * v.y - y * v.x); + } +//scalar dot product + const Scalar dot (const Vector & v) const + { + return x * v.x + y * v.y + z * v.z; + } +//length + const Scalar length () const + { + return (Scalar) sqrt ((double) this->dot (*this)); + } +//unit vector + const Vector unit () const + { + return (*this) / length (); + } +//make this a unit vector + void normalize () + { + (*this) /= length (); + } +//equal within an error ‘e’ + const bool nearlyEquals (const Vector & v, const Scalar e) const + { + return fabs (x - v.x) < e && fabs (y - v.y) < e && fabs (z - v.z) < e; + } +}; +// +// A 3D position +// +typedef Vector Point; + +} +#endif diff --git a/navdata.cpp b/navdata.cpp new file mode 100644 index 0000000..3dbf252 --- /dev/null +++ b/navdata.cpp @@ -0,0 +1,184 @@ +/************************************************************************ +* Copyright (c) 2005-2006 tok@openlinux.org.uk * +* * +* This file contains code derived from information copyrighted by * +* DMA Design. It may not be used in a commercial product. * +* * +* See license.txt for details. * +* * +* This notice may not be removed or altered. * +************************************************************************/ +#include +#include +#include +#include "navdata.h" +#include "log.h" +#include "m_exceptions.h" + +namespace OpenGTA { + Rect2D::Rect2D() { + x = y = w = h = 0; + } + + Rect2D::Rect2D(PHYSFS_uint8 a, PHYSFS_uint8 b, PHYSFS_uint8 c, PHYSFS_uint8 d) { + x = a; + y = b; + w = c; + h = d; + } + + bool Rect2D::isInside(PHYSFS_uint8 _x, PHYSFS_uint8 _y) { + if ((_x >= x ) && (_y >= y) && + (PHYSFS_uint16(_x) <= PHYSFS_uint16(x) + w) && + (PHYSFS_uint16(_y) <= PHYSFS_uint16(y) + h)) { + lastSubLocation = subLocation(_x, _y); + return true; + } + return false; + } + + PHYSFS_uint16 Rect2D::getSize() { + PHYSFS_uint16 res = w; + res *= h; + return res; + } + + // 0 = central, 1 = north, 2 = south, 4 = east, 8 = west + PHYSFS_uint8 Rect2D::subLocation(PHYSFS_uint8 _x, PHYSFS_uint8 _y) { + PHYSFS_uint8 in_x = _x - x; // offset in rect; assume: x <= _x + PHYSFS_uint8 in_y = _y - y; + float rel_x = float(in_x)/w; + float rel_y = float(in_y)/h; + PHYSFS_uint8 res = 0; +#define ONE_THIRD 1.0f/3.0f +#define TWO_THIRDS 2.0f/3.0f + if (rel_x <= ONE_THIRD) + // INFO << "west" << std::endl; + res |= 8; + else if (rel_x >= TWO_THIRDS) + //INFO << "east" << std::endl; + res |= 4; + if (rel_y <= ONE_THIRD) + res |= 1; + //INFO << "north" << std::endl; + else if (rel_y >= TWO_THIRDS) + res |= 2; + //INFO << "south" << std::endl; + return res; + } + + NavData::Sector::Sector(PHYSFS_file* fd) : Rect2D() { + sam = 0; + isADummy = 0; + std::memset(&name, 0, 30); + assert(fd); + PHYSFS_read(fd, static_cast(&x), 1, 1); + PHYSFS_read(fd, static_cast(&y), 1, 1); + PHYSFS_read(fd, static_cast(&w), 1, 1); + PHYSFS_read(fd, static_cast(&h), 1, 1); + PHYSFS_read(fd, static_cast(&sam), 1, 1); + PHYSFS_read(fd, static_cast(&name), 30, 1); + } + + NavData::Sector::Sector() : Rect2D() { + x = 0; + y = 0; + w = 255; + h = 255; + sam = 0; + std::memset(&name, 0, 30); + isADummy = 1; + } + + const char* NavData::Sector::getFullName() { + if (isADummy) + return ""; + std::string n; + if (lastSubLocation == 0) + n.append("Central "); + else if (lastSubLocation == 1) + n.append("North "); + else if (lastSubLocation == 2) + n.append("South "); + else if (lastSubLocation == 4) + n.append("East "); + else if (lastSubLocation == 8) + n.append("West "); + else if (lastSubLocation == 9) + n.append("Northwest "); + else if (lastSubLocation == 10) + n.append("Southwest "); + else if (lastSubLocation == 5) + n.append("Northeast "); + else if (lastSubLocation == 6) + n.append("Southeast "); + + n.append(name); + return n.c_str(); + } + + NavData::NavData(PHYSFS_uint32 size, PHYSFS_file *fd) { + if (size % 35) { + std::ostringstream o; + o << "Navdata size: " << size << " % 35 != 0"; + throw E_INVALIDFORMAT(o.str()); + //throw std::string("Invalid NavData size in mapfile"); + } + PHYSFS_uint32 c = size / 35; + assert(fd); + for (PHYSFS_uint32 i = 0; i < c; ++i) { + Sector *sec = new Sector(fd); + if (sec->getSize() == 0) { // workaround for 'NYC.CMP' (empty sectors) + delete sec; + WARN << "skipping zero size sector" << std::endl; + continue; + } + else + areas.insert(std::pair(sec->getSize(), sec)); + } + // dummy catch-all sector for gta london maps + areas.insert(std::pair(255*255, new Sector())); + /* + std::cout << "map areas (by size)" << std::endl; + SectorMapType::iterator i = areas.begin(); + while (i != areas.end()) { + std::cout << " " << i->first << " : " << i->second->name << " @ " << + int(i->second->x) << "," << int(i->second->y) << " " << int(i->second->w) << "x" << + int(i->second->h) << " sample " << int(i->second->sam) << std::endl; + ++i; + } + */ + } + NavData::~NavData() { + clear(); + } + + NavData::Sector* NavData::getSectorAt(PHYSFS_uint8 x, PHYSFS_uint8 y) { + SectorMapType::iterator it = areas.begin(); + while (it != areas.end()) { + if (it->second->isInside(x, y)) + return it->second; + ++it; + } + std::ostringstream o; + o << "Querying invalid sector at " << int(x) << ", " << int(y); + throw E_OUTOFRANGE(o.str()); + return NULL; + } + + void NavData::clear() { + SectorMapType::iterator it = areas.begin(); + while (it != areas.end()) { + delete it->second; + ++it; + } + areas.clear(); + } +} + +#if 0 +int main(int argc, char* argv[]) { + OpenGTA::Rect2D a(3, 4, 10, 20); + a.subLocation(atoi(argv[1]), atoi(argv[2])); +} +#endif diff --git a/navdata.h b/navdata.h new file mode 100644 index 0000000..4124ed2 --- /dev/null +++ b/navdata.h @@ -0,0 +1,97 @@ +/************************************************************************ +* Copyright (c) 2005-2006 tok@openlinux.org.uk * +* * +* This file contains code derived from information copyrighted by * +* DMA Design. It may not be used in a commercial product. * +* * +* See license.txt for details. * +* * +* This notice may not be removed or altered. * +************************************************************************/ +#ifndef NAVDATA_H +#define NAVDATA_H +#include +#include + +namespace OpenGTA { + + /** Helper class for area names. + * + * A simple box; beware uint8 overflow! + * + * @see NavData + */ + struct Rect2D { + public: + /** Zero-constructor. + * Everything is 0 + */ + Rect2D(); + /** Constructor with full params. + * @param x + * @param y + * @param w + * @param h + */ + Rect2D(PHYSFS_uint8, PHYSFS_uint8, PHYSFS_uint8, PHYSFS_uint8); + /** Test: point-in-box. + * @param x + * @param y + * @note If the point is inside 'lastSubLocation' is updated before returning. + */ + bool isInside(PHYSFS_uint8, PHYSFS_uint8); + /** Calculate north/south/east/west/central of point (which has to be inside). + * @param x + * @param y + * @return uint8 bitfield + */ + PHYSFS_uint8 subLocation(PHYSFS_uint8, PHYSFS_uint8); + PHYSFS_uint16 getSize(); + PHYSFS_uint8 x, y; + PHYSFS_uint8 w, h; + /** Last sub-area location. + * 0 = central + * 1 = north + * 2 = south + * 4 = east + * 8 = west + * ... valid combinations of the last four + */ + PHYSFS_uint8 lastSubLocation; + }; + + /** Container of all named sectors. + * @see Sector + */ + class NavData { + public: + /** A named sector of the map. + */ + struct Sector : public Rect2D { + /** Constructor from valid PHYSFS handle. + */ + Sector(PHYSFS_file*); + Sector(); + /** Sample number. + * 1) see $LANGUAGE.FXT file for actual name + * 2) probably sound? + */ + PHYSFS_uint8 sam; // sample number + char name[30]; // FIXME: should not be used + /** Returns the name prefixed with sub-area location. + */ + const char* getFullName(); + private: + bool isADummy; + }; + NavData(PHYSFS_uint32 size, PHYSFS_file *fd); + ~NavData(); + Sector* getSectorAt(PHYSFS_uint8, PHYSFS_uint8); + private: + void clear(); + typedef std::multimap SectorMapType; + SectorMapType areas; + }; +} + +#endif diff --git a/opengta.h b/opengta.h new file mode 100644 index 0000000..91ea175 --- /dev/null +++ b/opengta.h @@ -0,0 +1,523 @@ +/************************************************************************ +* Copyright (c) 2005-2006 tok@openlinux.org.uk * +* * +* This file contains code derived from information copyrighted by * +* DMA Design. It may not be used in a commercial product. * +* * +* See license.txt for details. * +* * +* This notice may not be removed or altered. * +************************************************************************/ +#ifndef OPENGTA_MAIN_H +#define OPENGTA_MAIN_H + +#include +#include +#include +#include +#include +#include "set.h" + +namespace OpenGTA { + + /** The common class for all graphics wrappers. + * Contains a number of common variables; does essentially nothing. + */ + class GraphicsBase { + public: + GraphicsBase(); + virtual ~GraphicsBase(); + + typedef struct ObjectInfo { + PHYSFS_uint32 width, height, depth; + PHYSFS_uint16 sprNum, weight, aux; + PHYSFS_sint8 status; + PHYSFS_uint8 numInto; + //PHYSFS_uint16 into[255]; // FIXME: MAX_INTO ??? + } ObjectInfo; + typedef struct LoadedAnim { + LoadedAnim(size_t size) : frame(size) {} + PHYSFS_uint8 block; + PHYSFS_uint8 which; + PHYSFS_uint8 speed; + PHYSFS_uint8 frameCount; + std::vector frame; + } LoadedAnim; + + typedef struct DoorInfo { + PHYSFS_sint16 rpx, rpy; + PHYSFS_sint16 object; + PHYSFS_sint16 delta; + } DoorInfo; + + typedef struct HlsInfo { + PHYSFS_sint16 h, l, s; + } HlsInfo; + + typedef struct CarInfo { + PHYSFS_sint16 width, height, depth; + PHYSFS_sint16 sprNum; + PHYSFS_sint16 weightDescriptor; + PHYSFS_sint16 maxSpeed, minSpeed; + PHYSFS_sint16 acceleration, braking; + PHYSFS_sint16 grip, handling; + // ... remaps + HlsInfo remap24[12]; + PHYSFS_uint8 remap8[12]; + PHYSFS_uint8 vtype; + PHYSFS_uint8 model; + PHYSFS_uint8 turning; + PHYSFS_uint8 damagable; + PHYSFS_uint16 value[4]; + PHYSFS_sint8 cx,cy; + PHYSFS_uint32 moment; + float rbpMass; + float g1_Thrust; + float tyreAdhesionX, tyreAdhesionY; + float handBrakeFriction; + float footBrakeFriction; + float frontBrakeBias; + PHYSFS_sint16 turnRatio; + PHYSFS_sint16 driveWheelOffset; + PHYSFS_sint16 steeringWheelOffset; + float backEndSlideValue; + float handBrakeSlideValue; + PHYSFS_uint8 convertible; + PHYSFS_uint8 engine; + PHYSFS_uint8 radio; + PHYSFS_uint8 horn; + PHYSFS_uint8 soundFunction; + PHYSFS_uint8 fastChangeFlag; + PHYSFS_sint16 numDoors; + DoorInfo door[4]; // FIXME: MAX_DOORS + } CarInfo; + /* + * float->fixed: + * fixed = int(floatnum * 65536) + * + * fixed->float + * float = float(fixedNum)/65536 + * + * int->fixed + * fixed = intNum << 16 + * + * fixed->int + * int = fixedNum >> 16 + */ + + typedef struct DeltaInfo { + PHYSFS_uint16 size; + unsigned char* ptr; + } DeltaInfo; + + typedef struct SpriteInfo { + PHYSFS_uint8 w; + PHYSFS_uint8 h; + PHYSFS_uint8 deltaCount; + PHYSFS_uint16 size; + PHYSFS_uint16 clut; + PHYSFS_uint8 xoffset; + PHYSFS_uint8 yoffset; + PHYSFS_uint16 page; + //unsigned char* ptr; + DeltaInfo delta[33]; //FIXME: GTA_SPRITE_MAX_DELTAS + } SpriteInfo; + + typedef struct SpriteNumbers { + PHYSFS_uint16 GTA_SPRITE_ARROW; + PHYSFS_uint16 GTA_SPRITE_DIGITS; + PHYSFS_uint16 GTA_SPRITE_BOAT; + PHYSFS_uint16 GTA_SPRITE_BOX; + PHYSFS_uint16 GTA_SPRITE_BUS; + PHYSFS_uint16 GTA_SPRITE_CAR; + PHYSFS_uint16 GTA_SPRITE_OBJECT; + PHYSFS_uint16 GTA_SPRITE_PED; + PHYSFS_uint16 GTA_SPRITE_SPEEDO; + PHYSFS_uint16 GTA_SPRITE_TANK; + PHYSFS_uint16 GTA_SPRITE_TRAFFIC_LIGHTS; + PHYSFS_uint16 GTA_SPRITE_TRAIN; + PHYSFS_uint16 GTA_SPRITE_TRDOORS; + PHYSFS_uint16 GTA_SPRITE_BIKE; + PHYSFS_uint16 GTA_SPRITE_TRAM; + PHYSFS_uint16 GTA_SPRITE_WBUS; + PHYSFS_uint16 GTA_SPRITE_WCAR; + PHYSFS_uint16 GTA_SPRITE_EX; + PHYSFS_uint16 GTA_SPRITE_TUMCAR; + PHYSFS_uint16 GTA_SPRITE_TUMTRUCK; + PHYSFS_uint16 GTA_SPRITE_FERRY; + + enum SpriteTypes { + ARROW = 0, + DIGIT, + BOAT, + BOX, + BUS, + CAR, + OBJECT, + PED, + SPEEDO, + TANK, + TRAFFIC_LIGHT, + TRAIN, + TRDOOR, + BIKE, + TRAM, + WBUS, + WCAR, + EX, + TUMCAR, + TUMTRUCK, + FERRY + }; + + PHYSFS_uint16 reIndex(const PHYSFS_uint16 & id, const enum SpriteTypes & st) const; + PHYSFS_uint16 countByType(const SpriteTypes & t) const; + } SpriteNumbers; + + bool isAnimatedBlock(uint8_t area_code, uint8_t id); + + void prepareSideTexture(unsigned int idx, unsigned char* dst); + void prepareLidTexture(unsigned int idx, unsigned char* dst); + void prepareAuxTexture(unsigned int idx, unsigned char* dst); + + SpriteNumbers spriteNumbers; + + CarInfo* findCarByModel(PHYSFS_uint8); + unsigned char* getTmpBuffer(bool rgba); + SpriteInfo* getSprite(size_t id) { return spriteInfos[id]; } + + virtual unsigned char* getSide(unsigned int idx, unsigned int palIdx, bool rgba) = 0; + virtual unsigned char* getLid(unsigned int idx, unsigned int palIdx, bool rgba) = 0; + virtual unsigned char* getAux(unsigned int idx, unsigned int palIdx, bool rgba) = 0; + + virtual unsigned char* getSpriteBitmap(size_t id, int remap, Uint32 delta) = 0; + + std::vector animations; + std::vector spriteInfos; + std::vector objectInfos; + std::vector carInfos; + + bool getDeltaHandling(); + void setDeltaHandling(bool delta_as_set); + + bool isBlockingSide(uint8_t id); + void setupBlocking(const std::string & file); + + protected: + void loadTileTextures(); + void loadAnim(); + + void loadObjectInfo_shared(PHYSFS_uint64 offset); + void loadSpriteNumbers_shared(PHYSFS_uint64 offset); + void loadCarInfo_shared(PHYSFS_uint64 offset); + void loadSpriteInfo_shared(PHYSFS_uint64 offset); + + void handleDeltas(const SpriteInfo & spriteinfo, unsigned char* buffer, + Uint32 delta); + void applyDelta(const SpriteInfo & spriteInfo, unsigned char* buffer, + Uint32 offset, const DeltaInfo & deltaInfo, bool mirror = false); + + PHYSFS_file* fd; + unsigned char* rawTiles; + unsigned char* rawSprites; + + PHYSFS_uint32 sideSize; + PHYSFS_uint32 lidSize; + PHYSFS_uint32 auxSize; + PHYSFS_uint32 animSize; + PHYSFS_uint32 objectInfoSize; + PHYSFS_uint32 carInfoSize; + PHYSFS_uint32 spriteInfoSize; + PHYSFS_uint32 spriteGraphicsSize; + PHYSFS_uint32 spriteNumberSize; + + PHYSFS_uint32 auxBlockTrailSize; + + /* + int loadSide(); + int loadLid(); + int loadAux(); + int loadAnim(); + int loadObject(); + int loadCar(); + int loadSpriteInfo(); + int loadSpriteGraphics(); + int loadSpriteNumbers();*/ + + PHYSFS_uint8 _topHeaderSize; + + unsigned char tileTmp[4096]; + unsigned char tileTmpRGB[4096*3]; + unsigned char tileTmpRGBA[4096*4]; + + bool delta_is_a_set; + + Util::Set sideTexBlockMove; + }; + + // just a forward declaration + class CityView; + + /** Loader for STYLE*.GRY files. + * + * Implements loading the 8-bit graphic files. + */ + class Graphics8Bit : public GraphicsBase { + /** allow renderer direct access to members */ + friend class CityView; + public: + /** Constructor for graphics loader. + * @param style a valid filename (maybe uppercase depending on your files) + */ + Graphics8Bit(const std::string& style); + /** Destructor cleans all rawdata caches. */ + ~Graphics8Bit(); + + /** Helper to apply palettes to various raw bitmaps. + * @see Graphics8Bit + * @see Font + */ + class RGBPalette { + private: + unsigned char data[256*3]; + public: + /** Empty constructor. + * You HAVE to call loadFromFile() function when using this constructor!. + */ + RGBPalette(); + /** Formerly private member, now exposed for Font class; take care. + * @param fd PHYSFS_file* handle. + */ + int loadFromFile(PHYSFS_file* fd); + /** Constructor from PHYFS_file. + * @param fd PHYSFS_file* handle + */ + RGBPalette(PHYSFS_file* fd); + /** Constructor from filename. + * @param filename a palette file name + */ + RGBPalette(const std::string& palette); + /** Transforms an input buffer using the palette stored in this instance. + * @param len length of the src buffer (in byte) + * @param src pointer to src buffer + * @param dst pointer to dst buffer (must exist and be large enough) + * @param rgba use 'true' to create a RGBA image, or 'false' (default) for RGB + */ + void apply(unsigned int len, const unsigned char* src, unsigned char* dst, bool rgba = false); + }; + + unsigned char* getSide(unsigned int idx, unsigned int palIdx, bool rgba); + unsigned char* getLid(unsigned int idx, unsigned int palIdx, bool rgba); + unsigned char* getAux(unsigned int idx, unsigned int palIdx, bool rgba); + + unsigned char* getSpriteBitmap(size_t id, int remap, Uint32 delta); + + void dump(); + + private: + PHYSFS_uint32 paletteSize; + PHYSFS_uint32 remapSize; + PHYSFS_uint32 remapIndexSize; + protected: + void loadHeader(); + void loadPalette(); + void loadRemapTables(); + void loadRemapIndex(); + void loadObjectInfo(); + void loadCarInfo(); + void loadSpriteInfo(); + void loadSpriteGraphics(); + void loadSpriteNumbers(); + void applyRemap(unsigned int len, unsigned int which, unsigned char* buffer); + RGBPalette* masterRGB; + PHYSFS_uint8 remapTables[256][256]; + PHYSFS_uint8 remapIndex[256][4]; + + }; + + class Graphics24Bit : public GraphicsBase { + public: + Graphics24Bit(const std::string & style); + ~Graphics24Bit(); + + unsigned char* getSide(unsigned int idx, unsigned int palIdx, bool rgba); + unsigned char* getLid(unsigned int idx, unsigned int palIdx, bool rgba); + unsigned char* getAux(unsigned int idx, unsigned int palIdx, bool rgba); + + unsigned char* getSpriteBitmap(size_t id, int remap, Uint32 delta); + + void dumpClut(const char* fname); + + protected: + void loadHeader(); + void loadClut(); + void loadPalIndex(); + void loadObjectInfo(); + void loadCarInfo(); + void loadSpriteInfo(); + void loadSpriteGraphics(); + void loadSpriteNumbers(); + + void applyClut(unsigned char* src, unsigned char* dst, + const size_t & len, const PHYSFS_uint16 & clutIdx, bool rgba); + + private: + PHYSFS_uint32 clutSize; + PHYSFS_uint32 pagedClutSize; + PHYSFS_uint32 tileclutSize; + PHYSFS_uint32 spriteclutSize; + PHYSFS_uint32 newcarclutSize; + PHYSFS_uint32 fontclutSize; + PHYSFS_uint32 paletteIndexSize; + + unsigned char* rawClut; + PHYSFS_uint16* palIndex; + }; + + class NavData; // see navdata.h + + #define GTA_MAP_MAXDIMENSION 256 + /** the wrapper for the CMP (compressed map) files */ + class Map { + friend class MapViewGL; + public: + Map(const std::string& filename); + ~Map(); + + typedef struct BlockInfo { + PHYSFS_uint16 typeMap; + PHYSFS_uint8 typeMapExt; + PHYSFS_uint8 left, right, top, bottom, lid; + + inline bool upOk() { return (typeMap & 1); } + inline bool downOk() { return (typeMap & 2); } + inline bool leftOk() { return (typeMap & 4); } + inline bool rightOk() { return (typeMap & 8); } + inline uint8_t blockType() { return ((typeMap & 16 ? 1 : 0) + (typeMap & 32 ? 2 : 0) + (typeMap & 64 ? 4 : 0)); } + inline bool isFlat() { return (typeMap & 128); } + inline uint8_t slopeType() { return ((typeMap & 256 ? 1 : 0) + (typeMap & 512 ? 2 : 0) + + (typeMap & 1024 ? 4 : 0) + (typeMap & 2048 ? 8 : 0) + (typeMap & 4096 ? 16 : 0) + (typeMap & 8192 ? 32 : 0));} + inline uint8_t rotation() { return ((typeMap & 16384 ? 1 : 0) + (typeMap & 32768 ? 2 : 0)); } + /* m1win seems to indicate: + * 000 - Nothing + * 001 - traffic lights + * 010 - invalid + * 011 - invalid + * 100 - railway end turn + * 101 - railway start turn + * 110 - railway station + * 111 - railway station train + */ + inline bool trafficLights() { return (typeMapExt & 1); } + inline bool railEndTurn() { return (typeMapExt & 4); } + inline bool railStartTurn() { return ((typeMapExt & 4) && (typeMapExt &1)); } + inline bool railStation() { return ((typeMapExt & 4) && (typeMapExt & 2));} + inline bool railStationTrain() { return ((typeMapExt & 4) && (typeMapExt & 2) && (typeMapExt & 1)); } + inline uint8_t remapIndex() { return ((typeMapExt & 8 ? 1 : 0) + (typeMapExt & 16 ? 2 : 0));} + inline bool flipTopBottom() { return (typeMapExt & 32); } + inline bool flipLeftRight() { return (typeMapExt & 64); } + inline bool railway() { return (typeMapExt & 128); } + + } BlockInfo; + typedef struct { + PHYSFS_uint16 x, y, z; + PHYSFS_uint8 type; + PHYSFS_uint8 remap; + PHYSFS_uint16 rotation; // see: cds.doc + PHYSFS_uint16 pitch; + PHYSFS_uint16 roll; + } ObjectPosition; + typedef struct Location { + Location(); + Location(const Location & other); + PHYSFS_uint8 x, y, z; + } Location; + typedef std::multimap LocationMap; + //... + PHYSFS_uint16 getNumBlocksAt(PHYSFS_uint8 x, PHYSFS_uint8 y); + PHYSFS_uint16 getNumBlocksAtNew(PHYSFS_uint8 x, PHYSFS_uint8 y); + BlockInfo* getBlockAt(PHYSFS_uint8 x, PHYSFS_uint8 y, PHYSFS_uint8 z); + BlockInfo* getBlockAtNew(PHYSFS_uint8 x, PHYSFS_uint8 y, PHYSFS_uint8 z); + BlockInfo* getBlockByInternalId(PHYSFS_uint16 id); + PHYSFS_uint16 getInternalIdAt(PHYSFS_uint8 x, PHYSFS_uint8 y, PHYSFS_uint8 z); + void dump(); + NavData *nav; + ObjectPosition *objects; + PHYSFS_uint16 numObjects; + protected: + + PHYSFS_uint32 base[GTA_MAP_MAXDIMENSION][GTA_MAP_MAXDIMENSION]; + PHYSFS_uint16 *column; + BlockInfo *block; + LocationMap locations; + + private: + PHYSFS_file* fd; + + PHYSFS_uint8 styleNumber; + PHYSFS_uint32 routeSize; + PHYSFS_uint32 objectPosSize; + PHYSFS_uint32 columnSize; + PHYSFS_uint32 blockSize; + PHYSFS_uint32 navDataSize; + + int loadHeader(); + int loadBase(); + int loadColumn(); + int loadBlock(); + void loadObjects(); + void loadRoutes(); + void loadLocations(); + void loadNavData(); + static const PHYSFS_uint8 _topHeaderSize = 28; + static const PHYSFS_uint64 _baseSize = 262144; + }; + + class MessageDB { + public: + MessageDB(); + MessageDB(const std::string &file); + ~MessageDB(); + void load(const std::string &file); + const std::string& getText(const char* id); + const std::string& getText(const std::string &id); + const std::string& getText(const uint32_t id); + private: + std::map messages; + std::string _error; + }; + + class Font { + public: + class Character { + public: + Character(PHYSFS_file*, uint8_t); + ~Character(); + uint8_t width; + uint8_t *rawData; + }; + Font(const std::string &file); + ~Font(); + uint8_t getNumChars() { return numChars; } + uint8_t getCharHeight() { return charHeight; } + Character *getCharById(size_t num) ; + size_t getIdByChar(const char c) ; + uint8_t getMoveWidth(const char c); + + void addMapping(char c, size_t num); + + void dumpAs(const char* filename, size_t id) ; + unsigned char* getCharacterBitmap(size_t num, unsigned int *width, + unsigned int *height); + private: + void loadMapping(const std::string &name); + void readHeader(PHYSFS_file*); + uint8_t charHeight; + uint8_t numChars; + std::vector chars; + std::map mapping; + Graphics8Bit::RGBPalette palette; + unsigned char *workBuffer; + }; +} +#endif diff --git a/pedestrian.cpp b/pedestrian.cpp new file mode 100644 index 0000000..42a5799 --- /dev/null +++ b/pedestrian.cpp @@ -0,0 +1,515 @@ +/************************************************************************ +* Copyright (c) 2005-2006 tok@openlinux.org.uk * +* * +* This software is provided as-is, without any express or implied * +* warranty. In no event will the authors be held liable for any * +* damages arising from the use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute * +* it freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must * +* not claim that you wrote the original software. If you use this * +* software in a product, an acknowledgment in the product documentation * +* would be appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must * +* not be misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source * +* distribution. * +************************************************************************/ +#include "pedestrian.h" +#include "spritemanager.h" +#include "opengta.h" +#include "dataholder.h" +#include "log.h" + +float slope_height_offset(unsigned char slope_type, float dx, float dz); +namespace OpenGTA { + Projectile::Projectile(uint8_t id, float r, Vector3D p, Vector3D d, Uint32 now) : + typeId(id), rot(r), pos(p), delta(d), endsAtTick(now + 5000) {} + + SpriteObject::Animation::Animation() : + Util::Animation(7, 7), + firstFrameOffset(0), moveSpeed(0.0f) {} + + SpriteObject::Animation::Animation(const Animation & other) : + Util::Animation(other.numFrames, 1000 / other.delay), + firstFrameOffset(other.firstFrameOffset), + //numFrames(other.numFrames), + moveSpeed(other.moveSpeed) {} + + SpriteObject::Animation::Animation(Uint16 foff, Uint8 num) : + Util::Animation(num, 7), + firstFrameOffset(foff), moveSpeed(0.0f) {} + + SpriteObject::Animation::Animation(Uint16 foff, Uint8 num, float speed) : + Util::Animation(num, 7), + firstFrameOffset(foff), moveSpeed(speed) {} + + SpriteObject::SpriteObject(const Vector3D & p) : + pos(p), //animRef(&SpriteManagerHolder::Instance().getAnimationById(0)) { + anim(SpriteManagerHolder::Instance().getAnimationById(0)) { + sprNum = 0; + remap = -1; + //curFrame = 0; + lastFrameUpdateAt = 0; + lastUpdateAt = 0; + rot = 0.0f; + animActive = false; + sprType = GraphicsBase::SpriteNumbers::ARROW; + } + + Uint8 SpriteObject::calcCurrentFrame(Uint32 ticks) { + Uint32 delta = ticks - lastFrameUpdateAt; + //assert(animRef); + if (delta > 100) { + //curFrame += 1; + //INFO << "new frame: " << int(curFrame) << " total: " << sprNum + curFrame + anim.firstFrameOffset << std::endl; + lastFrameUpdateAt = ticks; + } + /* + if (curFrame > anim.numFrames) + curFrame = 0; + */ + return 0;//curFrame; + } + + SpriteObject::SpriteObject(const SpriteObject & other) : + pos(other.pos), anim(other.anim) { + copyValues(other); + } + + void SpriteObject::copyValues(const SpriteObject & other) { + sprNum = other.sprNum; + //curFrame = other.curFrame; + remap = other.remap; + lastFrameUpdateAt = other.lastFrameUpdateAt; + lastUpdateAt = other.lastUpdateAt; + rot = other.rot; + sprType = other.sprType; + animActive = other.animActive; + } + + float SpriteObject::heightOverTerrain(const Vector3D & v) { + float x, y, z; + x = floor(v.x); + y = floor(v.y); + z = floor(v.z); + PHYSFS_uint8 x_b, z_b; + x_b = (PHYSFS_uint8)x; + z_b = (PHYSFS_uint8)z; + if (y < 0.0f) { + ERROR << "Below level! at coords: " << v.x << ", " << v.y << ", " << v.z << std::endl; + return 1.0f; + } + OpenGTA::Map & map = OpenGTA::MapHolder::Instance().get(); + while (y >= map.getNumBlocksAtNew(x_b, z_b) && y > 0.0f) + y -= 1.0f; + while (y < map.getNumBlocksAtNew(x_b, z_b) && y > 0.0f) { + OpenGTA::Map::BlockInfo * block = map.getBlockAtNew(x_b, z_b, (PHYSFS_uint8)y); + assert(block); + if (block->blockType() > 0) { + float bz = slope_height_offset(block->slopeType(), v.x - x, v.z - z); + if (block->slopeType() == 0 && block->blockType() != 5) + bz -= 1.0f; + return v.y - (y + bz); + } + y -= 1.0f; + } + y = floor(v.y) + 1.0f; + while (y < map.getNumBlocksAtNew(x_b, z_b) && y > 0.0f) { + OpenGTA::Map::BlockInfo * block = map.getBlockAtNew(x_b, z_b, (PHYSFS_uint8)y); + assert(block); + if (block->blockType() > 0) { + float bz = slope_height_offset(block->slopeType(), v.x - x, v.z - z); + if (block->slopeType() == 0 && block->blockType() != 5) + bz -= 1.0f; + return v.y - (y + bz); + } + y += 1.0f; + } + INFO << "should this be reached?" << std::endl; + return 1.0f; + } + + Pedestrian::Pedestrian(const Vector3D & e, + const Vector3D & p) : SpriteObject(p), + OBox(RollMatrix3D(0), e * 0.5f) { + m_M.Translate(p); + pedId = 0; + m_control = 0; + animId = 0; + inGroundContact = 0; + sprType = GraphicsBase::SpriteNumbers::PED; + activeWeapon = 0; + speedForces = Vector3D(0, 0, 0); + weaponReloadedAt = 0; + } + + Pedestrian::Pedestrian(const Vector3D & e, + const Vector3D & p, const Uint32 & asId) : SpriteObject(p), + OBox(RollMatrix3D(0), e * 0.5f) { + m_M.Translate(p); + pedId = asId; + m_control = 0; + animId = 0; + inGroundContact = 0; + sprType = GraphicsBase::SpriteNumbers::PED; + activeWeapon = 0; + speedForces = Vector3D(0, 0, 0); + weaponReloadedAt = 0; + } + + Pedestrian::Pedestrian(const Pedestrian & other) : SpriteObject(other), + OBox(other.m_M, other.m_Extent) { + //animRef(SpriteManagerHolder::Instance().getAnimationById(other.animId)) { + copyValues(other); + } + + void Pedestrian::switchToAnim(const Uint32 & newId) { + anim = Animation(SpriteManagerHolder::Instance().getAnimationById(newId)); + anim.set(Util::Animation::PLAY_FORWARD, Util::Animation::LOOP); + animId = newId; + //curFrame = 0; + } + + void SpriteObject::setAnimation(Animation & otherAnim) { + anim = Animation(otherAnim); + //curFrame = 0; + // FIXME: animId? + } + + void Pedestrian::copyValues(const Pedestrian & other) { + + m_control = other.m_control; + animId = other.animId; + pedId = other.pedId; + inGroundContact = other.inGroundContact; + sprType = other.sprType; + speedForces = other.speedForces; + activeWeapon = other.activeWeapon; + inventory = other.inventory; + weaponReloadedAt = other.weaponReloadedAt; + } + + void Pedestrian::giveItem(uint8_t id, uint32_t amount) { + InventoryType::iterator i = inventory.find(id); + if (i == inventory.end()) + inventory[id] = amount; + else + i->second += amount; + } + + void Pedestrian::tryMove(Vector3D nPos) { + float x, y, z; + x = floor(nPos.x); + y = floor(nPos.y); + z = floor(nPos.z); + OpenGTA::Map & map = OpenGTA::MapHolder::Instance().get(); + OpenGTA::GraphicsBase & graphics = OpenGTA::StyleHolder::Instance().get(); + //INFO << heightOverTerrain(nPos) << std::endl; + float hot = heightOverTerrain(nPos); + if (hot > 0.3f) + inGroundContact = 0; + else if (hot < 0.0) { + INFO << "gone below: " << hot << " at " << nPos.x << ", " << nPos.y << ", " << nPos.z << std::endl; + nPos.y -= (hot - 0.3f); + INFO << nPos.y << std::endl; + inGroundContact = 1; + } + else { + inGroundContact = 1; + } + if (y < map.getNumBlocksAtNew(PHYSFS_uint8(x), PHYSFS_uint8(z)) && y > 0.0f) { + //INFO << x << ", " << y << ", " << z << ": " << int(map.getNumBlocksAtNew(PHYSFS_uint8(x), PHYSFS_uint8(z))) << std::endl; + OpenGTA::Map::BlockInfo * block = map.getBlockAtNew(PHYSFS_uint8(x), PHYSFS_uint8(z), PHYSFS_uint8(y)); + assert(block);/* + if (block->blockType() > 0) { + float bz = slope_height_offset(block->slopeType(), nPos.x - x, nPos.z - z); + if (block->slopeType() == 0) + bz -= 1.0f; + if (inGroundContact) { + nPos.y = y + bz + 0.3f; + //pos = nPos; + } + else if (nPos.y - y - bz < 0.3f) { + INFO << "ped near ground (type: " << int(block->blockType()) << " float-pos: " + << nPos.x << ", " << nPos.y << ", " << nPos.z << std::endl; + inGroundContact = 1; + nPos.y = y + bz + 0.3f; + INFO << "height rewritten to: " << nPos.y << std::endl; + //pos = nPos; + + } + } + else { + inGroundContact = 0; + INFO << "lost footing: " << x << ", " << y << ", " << z << std::endl; + }*/ + if (block->left && graphics.isBlockingSide(block->left)) { // && block->isFlat() == false) { + if (block->isFlat()) { + //INFO << "xblock left: " << x - pos.x << std::endl; + if (x - pos.x < 0 && x - pos.x > -0.2f) { + nPos.x = (nPos.x < pos.x) ? pos.x : nPos.x; + } + else if (x - pos.x > 0 && x - pos.x < 0.2f) + nPos.x = pos.x; + } + else { + INFO << "xblock left: " << x - pos.x << " tex: " << int(block->left) << std::endl; + if (x - pos.x > 0 && x - pos.x < 0.2f) + nPos.x = pos.x; + else if (x - pos.x < 0 && x - pos.x > -0.2f) + nPos.x = (nPos.x < pos.x) ? pos.x : nPos.x; + } + } + if (block->right && block->isFlat() == false) { + INFO << "xblock right: " << pos.x - x - 1 << " tex: " << int(block->right) << std::endl; + if (pos.x - x - 1 > 0 && pos.x - x - 1 < 0.2f) { + nPos.x = pos.x; + } + else if (pos.x - x - 1 < 0 && pos.x - x - 1 > -0.2f) + nPos.x = (nPos.x > pos.x) ? pos.x : nPos.x; + } + if (block->top && graphics.isBlockingSide(block->top)) { // && block->isFlat() == false) { + if (block->isFlat()) { + INFO << "zblock top: " << z - pos.z << " tex: " << int(block->top) << std::endl; + if (z - pos.z > 0 && z - pos.z < 0.2f) + nPos.z = pos.z; + else if (z - pos.z < 0 && z - pos.z > -0.2f) + nPos.z = (nPos.z < pos.z) ? pos.z : nPos.z; + } + else { + INFO << "zblock top: " << z - pos.z << " tex: " << int(block->top)<< std::endl; + if (z - pos.z > 0 && z - pos.z < 0.2f) + nPos.z = pos.z; + else if (z - pos.z < 0 && z - pos.z > -0.2f) + nPos.z = (nPos.z < pos.z) ? pos.z : nPos.z; + } + } + if (block->bottom && block->isFlat() == false) { + INFO << "zblock bottom: " << pos.z - z - 1<< " tex: " << int(block->bottom)<< std::endl; + if (pos.z - z - 1 > 0 && pos.z - z - 1 < 0.2f) { + nPos.z = pos.z; + } + else if (pos.z - z - 1 < 0 && pos.z - z - 1 > -0.2f) + nPos.z = (nPos.z > pos.z) ? pos.z : nPos.z; + } + if (x >= 1 && y < map.getNumBlocksAtNew(PHYSFS_uint8(x-1), PHYSFS_uint8(z))) { + block = map.getBlockAtNew(PHYSFS_uint8(x-1), PHYSFS_uint8(z), PHYSFS_uint8(y)); + if (block->right && block->isFlat() == false) { + INFO << "xblock right: " << pos.x - x << " tex: " << int(block->right)<< std::endl; + if (pos.x - x < 0.2f) { + nPos.x = (nPos.x < pos.x ? pos.x : nPos.x); + } + } + } + if (x < 255 && y < map.getNumBlocksAtNew(PHYSFS_uint8(x+1), PHYSFS_uint8(z))) { + block = map.getBlockAtNew(PHYSFS_uint8(x+1), PHYSFS_uint8(z), PHYSFS_uint8(y)); + if (block->left && graphics.isBlockingSide(block->left)) { // && block->isFlat() == false) { + INFO << "xblock left: " << x + 1 - pos.x << " tex: " << int(block->left)<< std::endl; + if (block->isFlat()) { + if (x + 1 - pos.x > 0 && x + 1 - pos.x < 0.2f) + nPos.x = (nPos.x < pos.x ? nPos.x : pos.x); + } + else { + if (x + 1 - pos.x < 0.2f) + nPos.x = (nPos.x < pos.x ? nPos.x : pos.x); + } + } + } + if (z >= 1 && y < map.getNumBlocksAtNew(PHYSFS_uint8(x), PHYSFS_uint8(z-1))) { + block = map.getBlockAtNew(PHYSFS_uint8(x), PHYSFS_uint8(z-1), PHYSFS_uint8(y)); + if (block->bottom && block->isFlat() == false) { + INFO << "zblock bottom: " << pos.z - z<< " tex: " << int(block->bottom)<< std::endl; + if (pos.z - z < 0.2f) { + nPos.z = (nPos.z < pos.z ? pos.z : nPos.z); + } + } + } + if (z < 255 && y < map.getNumBlocksAtNew(PHYSFS_uint8(x), PHYSFS_uint8(z+1))) { + block = map.getBlockAtNew(PHYSFS_uint8(x), PHYSFS_uint8(z+1), PHYSFS_uint8(y)); + if (block->top && graphics.isBlockingSide(block->top)) { // && block->isFlat() == false) { + INFO << "zblock top: " << z + 1 - pos.z<< " tex: " << int(block->top) << std::endl; + if (block->isFlat()) { + if (z + 1 - pos.z > 0 && z + 1 - pos.z < 0.2f) + nPos.z = (nPos.z < pos.z ? nPos.z : pos.z); + } + else { + if (z + 1 - pos.z < 0.2f) + nPos.z = (nPos.z < pos.z ? nPos.z : pos.z); + } + } + } + //if (inGroundContact) + // pos = nPos; + } + if (inGroundContact) + pos = nPos; + //else + // inGroundContact = 0; + + } + + void Pedestrian::equip(uint8_t id) { + if (id == 0) { + activeWeapon = 0; + } + else { + InventoryType::iterator i = inventory.find(id); + if (i != inventory.end()) { + activeWeapon = i->first; + } + else + ERROR << "Ped does not have item type " << int(id) << std::endl; + } + } + + void Pedestrian::fireWeapon(Uint32 ticks) { + if (activeWeapon == 0) + return; // FIXME: punching! + InventoryType::iterator i = inventory.find(activeWeapon); + if (i->second == 0) + return; // no ammo + if (ticks < weaponReloadedAt) + return; + + weaponReloadedAt = ticks + 2000; + OpenGTA::SpriteManagerHolder::Instance().createProjectile(i->first, rot, pos, Vector3D(0.2f, 0, 0.2f), ticks); + + } + + void Pedestrian::update(Uint32 ticks) { + // update the animation + if (m_control) { + switch(m_control->move) { + case 1: + if (!(animId == 2u + activeWeapon*3)) + switchToAnim(2 + activeWeapon*3); + break; + case 2: + if (!(animId == 3u + activeWeapon*3)) + switchToAnim(3 + activeWeapon*3); + break; + case 0: + if (!(animId == 1u + activeWeapon*3)) + switchToAnim(1 + activeWeapon*3); + break; + case -1: + if (!(animId == 2u + activeWeapon*3)) { + switchToAnim(2 + activeWeapon*3); + anim.set(Util::Animation::PLAY_BACKWARD, Util::Animation::LOOP); + } + } + } + anim.update(ticks); + // update position/rotation + Uint32 delta = ticks - lastUpdateAt; + Vector3D moveDelta(0, 0, 0); + if (m_control) { + switch(m_control->turn) { + case -1: + rot -= 0.2f * delta; + break; + case 1: + rot += 0.2f * delta; + break; + case 0: + break; + } + if (rot >= 360.0f) + rot -= 360.0f; + if (rot < 0.0f) + rot += 360.0f; + switch(m_control->move) { + case -1: + moveDelta.x -= sin(rot * M_PI / 180.0f) * anim.moveSpeed * delta; + moveDelta.z -= cos(rot * M_PI / 180.0f) * anim.moveSpeed * delta; + break; + case 1: + moveDelta.x += sin(rot * M_PI / 180.0f) * anim.moveSpeed * delta; + moveDelta.z += cos(rot * M_PI / 180.0f) * anim.moveSpeed * delta; + break; + case 2: + moveDelta.x += sin(rot * M_PI / 180.0f) * anim.moveSpeed * delta; + moveDelta.z += cos(rot * M_PI / 180.0f) * anim.moveSpeed * delta; + break; + case 0: + break; + } + } + tryMove(pos + moveDelta); + if (!inGroundContact) { + speedForces.y += 0.1f; + pos.y -= speedForces.y; + if (speedForces.y < 0.2f) + INFO << "bridge step? height: " << pos.y << " speed: " << speedForces.y << std::endl; + else + INFO << "FALLING" << pos.y << " speed " << speedForces.y << std::endl; + } + else { + if (speedForces.y > 0.1) + INFO << "impacting with speed: " << speedForces.y << std::endl; + speedForces.y = 0.0f; + } + lastUpdateAt = ticks; + + } + +#define INT2FLOAT_WRLD(c) (float(c >> 6) + float(c % 64) / 64.0f) +#define INT2F_DIV64(v) (float(v) / 64.0f) +#define INT2F_DIV128(v) (float(v) / 128.0f) + + Car::Car(const OpenGTA::Map::ObjectPosition & op) : + SpriteObject(Vector3D(INT2FLOAT_WRLD(op.x), 6.05f-INT2FLOAT_WRLD(op.z), INT2FLOAT_WRLD(op.y))), + OBox(RollMatrix3D(0), Vector3D()), + c_info(*StyleHolder::Instance().get().findCarByModel(op.type)) { + type = op.type; + sprType = GraphicsBase::SpriteNumbers::CAR; + sprNum = c_info.sprNum; + m_Extent = Vector3D(INT2F_DIV128(c_info.width), + INT2F_DIV128(c_info.depth), + INT2F_DIV128(c_info.height)); + m_M.Translate(pos); + rot = op.rotation * 360 / 1024; + } + + Car::Car(const Car & other) : SpriteObject(other), + OBox(other.m_M, other.m_Extent), + c_info(*StyleHolder::Instance().get().findCarByModel(other.type)) { + copyValues(other); + } + + void Car::copyValues(const Car & other) { + sprType = other.sprType; + delta = other.delta; + carId = other.carId; + type = other.type; + } + + GameObject::GameObject(const OpenGTA::Map::ObjectPosition & op) : + SpriteObject(Vector3D(INT2FLOAT_WRLD(op.x), 6.05f-INT2FLOAT_WRLD(op.z), INT2FLOAT_WRLD(op.y))), + OBox(RollMatrix3D(0), Vector3D()) { + sprType = GraphicsBase::SpriteNumbers::OBJECT; + GraphicsBase & style = StyleHolder::Instance().get(); + sprNum = style.objectInfos[op.type]->sprNum; + m_Extent = Vector3D(INT2F_DIV128(style.objectInfos[op.type]->width), + INT2F_DIV128(style.objectInfos[op.type]->depth), + INT2F_DIV128(style.objectInfos[op.type]->height)); + m_M.Translate(pos); + rot = op.rotation * 360 / 1024; + + } + + GameObject::GameObject(const GameObject & other) : SpriteObject(other), + OBox(other.m_M, other.m_Extent) { + copyValues(other); + } + + void GameObject::copyValues(const GameObject & other) { + sprType = other.sprType; + } +} diff --git a/pedestrian.h b/pedestrian.h new file mode 100644 index 0000000..2b2d69b --- /dev/null +++ b/pedestrian.h @@ -0,0 +1,134 @@ +/************************************************************************ +* Copyright (c) 2005-2006 tok@openlinux.org.uk * +* * +* This software is provided as-is, without any express or implied * +* warranty. In no event will the authors be held liable for any * +* damages arising from the use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute * +* it freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must * +* not claim that you wrote the original software. If you use this * +* software in a product, an acknowledgment in the product documentation * +* would be appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must * +* not be misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source * +* distribution. * +************************************************************************/ +#ifndef OGTA_PEDESTRIAN_H +#define OGTA_PEDESTRIAN_H + +#include +#include "math3d.h" +#include "obox.h" +#include "animation.h" +#include "opengta.h" + +namespace OpenGTA { + struct Projectile { + Projectile(uint8_t, float, Vector3D, Vector3D, Uint32); + uint8_t typeId; + float rot; + Vector3D pos; + Vector3D delta; + Uint32 endsAtTick; + }; + class SpriteObject { + public: + SpriteObject(const Vector3D & p); + SpriteObject(const SpriteObject & other); + struct Animation : public Util::Animation { + Animation(); + Animation(const Animation & other); + Animation(Uint16 foff, Uint8 num); + Animation(Uint16 foff, Uint8 num, float speed); + Uint16 firstFrameOffset; + //Uint8 numFrames; + float moveSpeed; + }; + Vector3D pos; + //Uint8 curFrame; + Uint16 sprNum; + Sint16 remap; + //Animation * animRef; + Animation anim; + bool animActive; + void update(Uint32 ticks); + Uint32 lastFrameUpdateAt; + Uint32 lastUpdateAt; + float rot; + GraphicsBase::SpriteNumbers::SpriteTypes sprType; + void setAnimation(Animation & otherAnim); + protected: + Uint8 calcCurrentFrame(Uint32 ticks); + float heightOverTerrain(const Vector3D & v); + private: + void copyValues(const SpriteObject & other); + SpriteObject & operator = (const SpriteObject & other) { return *this; } + }; + + class Pedestrian : public SpriteObject, public OBox { + public: + struct Controller { + // turn, move, + // run/walk/swim/crawl + // jump, shoot/punch, + Sint8 turn; + Sint8 move; + }; + Pedestrian(const Vector3D & e, const Vector3D & pos); + Pedestrian(const Vector3D & e, const Vector3D & pos, const Uint32 & asId); + Pedestrian(const Pedestrian & other); + Uint32 pedId; + Controller* m_control; + Uint32 animId; + Vector3D speedForces; + void switchToAnim(const Uint32 & newId); + + void update(Uint32 ticks); + void equip(uint8_t eq_id); + void giveItem(uint8_t id, uint32_t amount); + void fireWeapon(Uint32 ticks); + private: + typedef std::map InventoryType; + InventoryType inventory; + uint8_t activeWeapon; + bool inGroundContact; + void tryMove(Vector3D nPos); + void copyValues(const Pedestrian & other); + Uint32 weaponReloadedAt; + //Uint8 calcCurrentFrame(Uint32 ticks); + Pedestrian & operator = (const Pedestrian & other) { return *this; } + }; + + class Car : public SpriteObject, public OBox { + public: + Car(const OpenGTA::Map::ObjectPosition & op); + Car(const Car & other); + Uint32 delta; + Uint32 carId; + Uint8 type; + private: + GraphicsBase::CarInfo & c_info; + void copyValues(const Car & other); + Car & operator = (const Car & other) { return *this; } + + }; + + class GameObject : public SpriteObject, public OBox { + public: + GameObject(const OpenGTA::Map::ObjectPosition & op); + GameObject(const GameObject & other); + Uint32 objId; + private: + void copyValues(const GameObject & other); + GameObject & operator = (const GameObject & o) { return *this; } + }; +} + +#endif diff --git a/prepare_build.sh b/prepare_build.sh new file mode 100755 index 0000000..fc61a60 --- /dev/null +++ b/prepare_build.sh @@ -0,0 +1,277 @@ +#!/bin/bash + +function program_exists() { + $1 $2 1>/dev/null 2>&1 + if [ $? -eq 127 ]; then + return 0 + fi + upname=$(echo $1 | tr '[a-z]' '[A-Z]' | tr '-' '_') + eval "$upname=$1" + return 1 +} + +function print_make_file_list() { +FOO=GL_SRC +FOOO=GL_OBJ +( grep -l "^namespace OpenGL" *.cpp ; echo "gl_frustum.cpp";echo "math/obox.cpp coldet/math3d.cpp" ) | sort | xargs echo "$FOO =" +echo "$FOOO = \${$FOO:.cpp=.o}" +FOO=OGTA_SRC +FOOO=OGTA_OBJ +( grep -l "^namespace OpenGTA" *.cpp ; echo "slope_height_func.cpp" ) | sort | xargs echo "$FOO =" +echo "$FOOO = \${$FOO:.cpp=.o}" + +UTIL_SRC=$(ls util/*.cpp | grep -v color.cpp | grep -v sound | xargs echo) +SOUND_SRC=$(ls util/*.cpp | grep sound.* | xargs echo) +LUA_SRC=$(ls lua_addon/*.cpp | xargs echo) + +cat < src_list.make +#print_target_list >> src_list.make + +function pkg_config_haslib () { + $PKG_CONFIG $1 1>/dev/null 2>&1 + if [ $? -eq 0 ]; then + return 1 + fi + return 0 +} + +function check_sdl () { + program_exists sdl-config + if [ $? -eq 1 ]; then + SDL_INC=$($SDL_CONFIG --cflags) + SDL_LIB=$($SDL_CONFIG --libs) + else + program_exists pkg-config + if [ $? -eq 1 ]; then + pkg_config_try_multiple SDL SDL sdl + fi + fi +} + +function pkg_config_try_multiple () { + local _prefixName=$1 + shift + while [ $# -gt 0 ]; do + pkg_config_haslib $1 + if [ $? -eq 1 ]; then + eval "${_prefixName}_INC=\$($PKG_CONFIG --cflags $1)" + eval "${_prefixName}_LIB=\$($PKG_CONFIG --libs $1)" + return + fi + shift + done +} + +function check_lua () { + program_exists pkg-config + if [ $? -eq 1 ]; then + pkg_config_try_multiple LUA lua5.1 lua51 lua5 lua + else + program_exists lua-config + if [ $? -eq 1 ]; then + LUA_INC=$($LUA_CONFIG --include) + LUA_LIB=$($LUA_CONFIG --libs) + fi + fi +} + +function check_physfs () { + program_exists pkg-config + if [ $? -eq 1 ]; then + pkg_config_try_multiple PHYSFS physfs + fi +} + +function check_compiler () { + g++ 1>/dev/null 2>&1 + if [ $? -eq 1 ]; then + CXX=g++ + else + CXX= + fi + gcc 1>/dev/null 2>&1 + if [ $? -eq 1 ]; then + CC=gcc + else + CC= + fi +} + +# defaults + +DEBUG=-ggdb +WARN=-Wall +OPT=-O2 +if [ "$1" == "LINUX" ]; then + DEFS="-DLINUX -DDO_SCALEX" +else + DEFS="-DWIN32 -DDO_SCALEX" +fi +PHYSFS_LIB=-lphysfs +SDL_LIB=-lSDL +LUA_LIB=-llua51 + +function print_detected() { + cat <= 5.1 (but not 5.0); not detected +LUA_INC = $LUA_INC +LUA_LIB = $LUA_LIB + +LINK_LAZY = -Xlinker --unresolved-symbols -Xlinker ignore-all + +EOF +} + +function print_w32settings() { + cat <= 5.1 (but not 5.0); not detected +LUA_INC = $LUA_INC +LUA_LIB = $LUA_LIB + +# good idea here? +LINK_LAZY = -Xlinker --unresolved-symbols -Xlinker ignore-all + +EOF +} + +function print_all() { +check_sdl +check_lua +check_physfs +check_compiler +print_detected +print_make_file_list +print_target_list +cat < src_list.make +else + echo "*** WIN32 ***" + print_w32settings > src_list.make + print_make_file_list >> src_list.make + print_target_list >> src_list.make +fi diff --git a/read_cmp.cpp b/read_cmp.cpp new file mode 100644 index 0000000..33fb05a --- /dev/null +++ b/read_cmp.cpp @@ -0,0 +1,284 @@ +/************************************************************************ +* Copyright (c) 2005-2006 tok@openlinux.org.uk * +* * +* This file contains code derived from information copyrighted by * +* DMA Design. It may not be used in a commercial product. * +* * +* See license.txt for details. * +* * +* This notice may not be removed or altered. * +************************************************************************/ +#include +#include +#include +#include "opengta.h" +#include "navdata.h" +#include "log.h" +#include "m_exceptions.h" + +/* see http://members.aol.com/form1/fixed.htm for fixed point floats: + * int_var = (long) fixed_var >> 8; // for 8 bits after point + */ +namespace OpenGTA { + + Map::Map(const std::string& filename) { + nav = 0; + fd = PHYSFS_openRead(filename.c_str()); + if (fd == NULL) { + std::string f2(filename); + transform(f2.begin(), f2.end(), f2.begin(), tolower); + fd = PHYSFS_openRead(f2.c_str()); + } + if (!fd) { + //throw std::string("FileNotFound: ") + filename; + std::ostringstream o; + o << filename << " with error: " << SDL_GetError(); + throw E_FILENOTFOUND(o.str()); + } + loadHeader(); + loadBase(); + loadColumn(); + loadBlock(); + loadObjects(); + loadRoutes(); + loadLocations(); + loadNavData(); + //dump(); + } + Map::~Map() { + if (column) delete [] column; + if (block) delete [] block; + if (objects) delete [] objects; + if (nav) delete nav; + LocationMap::iterator i = locations.begin(); + while (i != locations.end()) { + delete i->second; + ++i; + } + locations.clear(); + if (fd) + PHYSFS_close(fd); + } + int Map::loadHeader() { + PHYSFS_uint32 vc; + PHYSFS_readULE32(fd, &vc); + //INFO << "Map version code: " << vc << std::endl; + PHYSFS_uint8 sn; + PHYSFS_read(fd, static_cast(&styleNumber), 1, 1); + //INFO << "Style number: " << int(styleNumber) << std::endl; + PHYSFS_read(fd, static_cast(&sn), 1, 1); + //INFO << "Sample number: " << int(sn) << std::endl; + PHYSFS_uint16 reserved; + PHYSFS_readULE16(fd, &reserved); + PHYSFS_readULE32(fd, &routeSize); + PHYSFS_readULE32(fd, &objectPosSize); + PHYSFS_readULE32(fd, &columnSize); + PHYSFS_readULE32(fd, &blockSize); + PHYSFS_readULE32(fd, &navDataSize); + /* + INFO << "Route size: " << routeSize << std::endl; + INFO << "Object size: " << objectPosSize << std::endl; + INFO << "Column size: " << columnSize << std::endl; + INFO << "Block size: " << blockSize << " (" << + blockSize / sizeof(BlockInfo) << " blocks " << blockSize % sizeof(BlockInfo) + << " overcount)" << std::endl; + INFO << "Navdata size: " << navDataSize << std::endl; + */ + + column = new PHYSFS_uint16[columnSize/2]; + block = new BlockInfo[blockSize / sizeof(BlockInfo) ]; + + objects = new ObjectPosition[objectPosSize / sizeof(ObjectPosition)]; + + + return 0; + } + int Map::loadBase() { + PHYSFS_seek(fd, static_cast(_topHeaderSize)); + for (int y = 0; y < GTA_MAP_MAXDIMENSION; y++) { + for(int x = 0; x < GTA_MAP_MAXDIMENSION; x++) { + PHYSFS_readULE32(fd, &base[x][y]); + //std::cout << x << "," << y << " : " << base[x][y] << std::endl; + } + } + return 0; + } + int Map::loadColumn() { + if (!PHYSFS_seek(fd, _baseSize + _topHeaderSize)) { + //throw std::string("IO Error while seeking in mapfile"); + throw E_IOERROR(PHYSFS_getLastError()); + } + //PHYSFS_uint16 v; + for (unsigned int i = 0; i < columnSize/2; i++) { + PHYSFS_readULE16(fd, &column[i]); + //std::cout << i << ": " << v << std::endl; + } + return 0; + } + int Map::loadBlock() { + PHYSFS_seek(fd, _baseSize + columnSize + _topHeaderSize); + int i, max; + max = blockSize / sizeof(BlockInfo); + //uint8_t tmp; + for (i = 0; i < max; i++) { + PHYSFS_readULE16(fd, &block[i].typeMap); + PHYSFS_read(fd, static_cast(&block[i].typeMapExt), 1, 1); + PHYSFS_read(fd, static_cast(&block[i].left), 1, 1); + PHYSFS_read(fd, static_cast(&block[i].right), 1, 1); + PHYSFS_read(fd, static_cast(&block[i].top), 1, 1); + PHYSFS_read(fd, static_cast(&block[i].bottom), 1, 1); + PHYSFS_read(fd, static_cast(&block[i].lid), 1, 1); + //block[i].animMode = 0; + } + return 0; + } + void Map::loadObjects() { + PHYSFS_seek(fd, _baseSize + columnSize + _topHeaderSize + blockSize); + int c = objectPosSize / sizeof(ObjectPosition); + numObjects = c; + assert(objectPosSize % sizeof(ObjectPosition) == 0); + for (int i=0; i < c; i++) { + PHYSFS_readULE16(fd, &objects[i].x); + PHYSFS_readULE16(fd, &objects[i].y); + PHYSFS_readULE16(fd, &objects[i].z); + PHYSFS_read(fd, static_cast(&objects[i].type), 1, 1); + PHYSFS_read(fd, static_cast(&objects[i].remap), 1, 1); + PHYSFS_readULE16(fd, &objects[i].rotation); + PHYSFS_readULE16(fd, &objects[i].pitch); + PHYSFS_readULE16(fd, &objects[i].roll); + + // shift every coord? or just if any > 255 + /* + objects[i].x = objects[i].x >> 6; + objects[i].y = objects[i].y >> 6; + objects[i].z = objects[i].z >> 6;*/ + /* + std::cout << objects[i].x << "," << objects[i].y << "," << objects[i].z << " " << int(objects[i].type) + << " remap " << int(objects[i].remap) + << " rot " << objects[i].rotation << " " << objects[i].pitch << " " << objects[i].roll << std::endl; + */ + } + } + void Map::loadRoutes() { + //FIXME: missing + PHYSFS_uint32 _si = _baseSize + columnSize + _topHeaderSize + + objectPosSize + blockSize; + PHYSFS_seek(fd, _si); + PHYSFS_uint32 _counted = 0; + while (_counted < routeSize) { + PHYSFS_uint8 num_vertices = 0; + PHYSFS_uint8 route_type = 0; + PHYSFS_read(fd, static_cast(&num_vertices), 1, 1); + PHYSFS_read(fd, static_cast(&route_type), 1, 1); + //INFO << "route-t " << int(route_type) << " with " << int(num_vertices) << " vertices" << std::endl; + PHYSFS_uint8 x, y, z; + for (int i=0; i < num_vertices; i++) { + PHYSFS_read(fd, static_cast(&x), 1, 1); + PHYSFS_read(fd, static_cast(&y), 1, 1); + PHYSFS_read(fd, static_cast(&z), 1, 1); + //INFO << int(x) << "," << int(y) << "," << int(z) << std::endl; + _counted += 3; + } + + _counted += 2; + } + } + Map::Location::Location() : x(0), y(0), z(0) {} + Map::Location::Location(const Map::Location & other) : x(other.x), y(other.y), z(other.z) {} + void Map::loadLocations() { + //FIXME: missing + PHYSFS_uint32 _si = _baseSize + columnSize + _topHeaderSize + + objectPosSize + routeSize + blockSize; + PHYSFS_seek(fd, _si); + // police + // hospital + // unused + // unused + // fire + // unused + Location loc; + PHYSFS_uint8 loc_type = 0; + for (int i = 0; i < 36; ++i) { + PHYSFS_read(fd, static_cast(&loc.x), 1, 1); + PHYSFS_read(fd, static_cast(&loc.y), 1, 1); + PHYSFS_read(fd, static_cast(&loc.z), 1, 1); + if ((loc.x == 0) && (loc.y == 0) && (loc.z == 0)) + continue; + if (i < 6) + loc_type = 0; + else if ((i >= 6) && (i < 12)) + loc_type = 1; + else if ((i >= 24) && (i < 30)) + loc_type = 2; + else + continue; + //std::cout << int(loc_type) <<": " << int(loc.x) << ", " << int(loc.y) << ", " << int(loc.z) << std::endl; + locations.insert(std::pair(loc_type, new Location(loc))); + } + + } + void Map::loadNavData() { + PHYSFS_uint32 _si = _baseSize + columnSize + _topHeaderSize + + objectPosSize + routeSize + 3 * 6 * 6 + blockSize; + PHYSFS_seek(fd, _si); + nav = new NavData(navDataSize, fd); + assert(nav); + } + PHYSFS_uint16 Map::getNumBlocksAt(PHYSFS_uint8 x, PHYSFS_uint8 y) { + return column[base[x][y] / 2]; + } + PHYSFS_uint16 Map::getNumBlocksAtNew(PHYSFS_uint8 x, PHYSFS_uint8 y) { + return 6 - column[base[x][y] / 2]; + } + Map::BlockInfo* Map::getBlockAt(PHYSFS_uint8 x, PHYSFS_uint8 y, PHYSFS_uint8 z) { + PHYSFS_uint16 v = column[base[x][y] / 2 + z]; + return &block[v]; + } + Map::BlockInfo* Map::getBlockAtNew(PHYSFS_uint8 x, PHYSFS_uint8 y, PHYSFS_uint8 z) { + PHYSFS_uint16 idx0 = 6 - column[base[x][y] / 2]; + if (idx0 > z) + idx0 -= z; + else + assert(idx0 > z); + idx0 = column[base[x][y] / 2 + idx0]; + return &block[idx0]; + } + PHYSFS_uint16 Map::getInternalIdAt(PHYSFS_uint8 x, PHYSFS_uint8 y, PHYSFS_uint8 z) { + return column[base[x][y] / 2 + z]; + } + Map::BlockInfo* Map::getBlockByInternalId(PHYSFS_uint16 id) { + return &block[id]; + } + void Map::dump() { + for (int y = 0; y < GTA_MAP_MAXDIMENSION; y++) { + for(int x = 0; x < GTA_MAP_MAXDIMENSION; x++) { + std::cout << x << "," << y << ":" << column[base[x][y] / 2] << "||"; + PHYSFS_uint16 ts = column[base[x][y] / 2]; + std::cout << "("; + for(int t=1; t <= (6 - ts); t++) { + BlockInfo *info = &block[column[base[x][y] / 2 + t]]; + std::cout << int(info->slopeType()) << ", "; + + } + std::cout << ")" << std::endl; + } + } + } +} + +#if 0 +#include +void do_exit() { + PHYSFS_deinit(); +} +int main(int argc, char* argv[]) { + PHYSFS_init(argv[0]); + atexit(do_exit); + std::cout << "Physfs-Base: " << PHYSFS_getBaseDir() << std::endl; + PHYSFS_addToSearchPath(PHYSFS_getBaseDir(), 1); + std::cout << "Has: " << argv[1] << " : " << PHYSFS_exists(argv[1]) << std::endl; + OpenGTA::Map a(argv[1]); + a.dump(); + return 0; +} +#endif diff --git a/read_fnt.cpp b/read_fnt.cpp new file mode 100644 index 0000000..124ce72 --- /dev/null +++ b/read_fnt.cpp @@ -0,0 +1,428 @@ +/************************************************************************ +* Copyright (c) 2005-2006 tok@openlinux.org.uk * +* * +* This file contains code derived from information copyrighted by * +* DMA Design. It may not be used in a commercial product. * +* * +* See license.txt for details. * +* * +* This notice may not be removed or altered. * +************************************************************************/ +#include +#include +#include "cistring.h" +#include "opengta.h" +#include "m_exceptions.h" +#include "log.h" + +namespace OpenGTA { + Font::Font(const std::string &file) { + PHYSFS_file *fd = PHYSFS_openRead(file.c_str()); + if (fd == NULL) { + std::string f2(file); + transform(f2.begin(), f2.end(), f2.begin(), tolower); + fd = PHYSFS_openRead(f2.c_str()); + } + if (!fd) + throw E_FILENOTFOUND(file); + //throw std::string("FileNotFound: ") + file; + readHeader(fd); + int ww = 0; + int lw = 0; + for (uint8_t i = 0; i < numChars; i++) { + Character * ch = new Character(fd, charHeight); + ww += ch->width; + if (ch->width > lw) + lw = ch->width; + chars.push_back(ch); + } + INFO << "total width " << ww << " largest width " << lw << std::endl; + palette.loadFromFile(fd); + PHYSFS_close(fd); + size_t ih = charHeight; + while (ww > 1024) { + ih *= 2; + ww /= 2; + } + workBuffer = new unsigned char[lw*charHeight*4]; + loadMapping(file); + } + Font::~Font() { + std::vector::iterator i = chars.begin(); + while(i != chars.end()) { + delete *i; + i++; + } + chars.clear(); + delete [] workBuffer; + } + void Font::readHeader(PHYSFS_file *fd) { + PHYSFS_read(fd, static_cast(&numChars), 1, 1); + PHYSFS_read(fd, static_cast(&charHeight), 1, 1); + INFO << "Font contains " << int(numChars) << + " characters of height " << int(charHeight) << std::endl; + } + void Font::addMapping(char c, size_t num) { + mapping[c] = num; + } + Font::Character* Font::getCharById(size_t num) { + return chars[num]; + } + size_t Font::getIdByChar(const char c) { + std::map::iterator i = mapping.find(c); + if (i == mapping.end()) + return 0; + else + return i->second; + } + uint8_t Font::getMoveWidth(const char c) { + std::map::iterator i = mapping.find(c); + if (i == mapping.end()) { + return chars[0]->width; + } + return chars[i->second]->width; + } + + unsigned char* Font::getCharacterBitmap(size_t num, unsigned int *width, unsigned int *height) { + unsigned int len = chars[num]->width; + len *= charHeight; + palette.apply(len, chars[num]->rawData, workBuffer, true); + if (width != NULL) + *width = chars[num]->width; + if (height != NULL) + *height = charHeight; + return workBuffer; + /* + unsigned int glwidth = 1; + unsigned int glheight = 1; + + while(glwidth < chars[num]->width) + glwidth <<= 1; + + while(glheight < charHeight) + glheight <<= 1; + unsigned char *res = new unsigned char[glwidth*glheight*4]; + */ + } + void Font::dumpAs(const char* filename, size_t id) { + unsigned int len = chars[id]->width; + len *= charHeight; + palette.apply(len, chars[id]->rawData, workBuffer, true); +#if SDL_BYTEORDER == SDL_BIG_ENDIAN +#define rmask 0xff000000 +#define gmask 0x00ff0000 +#define bmask 0x0000ff00 +#define amask 0x000000ff +#else +#define rmask 0x000000ff +#define gmask 0x0000ff00 +#define bmask 0x00ff0000 +#define amask 0xff000000 +#endif + SDL_Surface* s = SDL_CreateRGBSurface(SDL_SWSURFACE|SDL_SRCALPHA, + chars[id]->width, charHeight, 32, rmask, gmask, bmask, amask); + SDL_LockSurface(s); + unsigned char* dst = static_cast(s->pixels); + unsigned char * rp = workBuffer; + for (unsigned int i=0; i(&width), 1, 1); + int c = int(width); + c *= int(height); + //std::cout <<"width " << int(width) << " going to read " << c << " bytes" << std::endl; + rawData = new uint8_t[c]; + PHYSFS_read(fd, static_cast(rawData), 1, c); + } + Font::Character::~Character() { + delete [] rawData; + } + + void Font::loadMapping(const std::string & name) { + Util::ci_string name2(name.c_str()); +#define chr(n) ((char)(n)) + if (name2.find("big1.fon") != std::string::npos) { + INFO << "found mapping: big1.fon - " << name << std::endl; + addMapping('!', 0); + addMapping('-', 12); + for (int j = 65; j < 91; j++) { + addMapping(chr(j), j - 33); + } + for (int j = 192; j < 195; j++) { + addMapping(chr(j), j - 97); + } + addMapping(196, 98); + addMapping(198, 99); + addMapping(199, 100); + for (int j=200; j < 208; j++) + addMapping(j, j - 99); + for (int j=210; j < 213; j++) + addMapping(j, j - 101); + addMapping(214, 112); + for (int j=217; j < 221; j++) + addMapping(j, j - 104); + addMapping(223, 117); + } + else if ((name2.find("pager1.fon") != std::string::npos) || + (name2.find("pager2.fon") != std::string::npos)) { + addMapping('!', 0); + addMapping('"', 1); + addMapping('$', 3); + addMapping('\'', 6); + addMapping('(', 7); + addMapping(')', 8); + addMapping(',', 11); + addMapping('.', 13); + for (int j = 48; j < 58; j++) { + addMapping(chr(j), j - 33); + } + addMapping(':', 25); + addMapping(';', 26); + addMapping('<', 27); + addMapping('>', 29); + addMapping('?', 30); + addMapping('_', 62); + for (int j = 65; j < 91; j++) { + addMapping(chr(j), j - 33); + } + for (int j = 192; j < 195; j++) { + addMapping(chr(j), j - 97); + } + addMapping(196, 98); + addMapping(198, 99); + addMapping(199, 100); + for (int j=200; j < 208; j++) + addMapping(j, j - 99); + for (int j=210; j < 213; j++) + addMapping(j, j - 101); + addMapping(214, 112); + for (int j=217; j < 221; j++) + addMapping(j, j - 104); + addMapping(223, 117); + } + else if (name2.find("street1.fon") != std::string::npos) { + INFO << "found mapping: street1.fon - " << name << std::endl; + for (int j = 65; j < 91; j++) { + addMapping(chr(j), j - 33); + } + for (int j = 48; j < 58; j++) { + addMapping(chr(j), j - 33); + } + for (int j = 97; j < 123; j++) { + addMapping(chr(j), j - 33); + } + WARN << "incomplete mapping" << std::endl; + } + else if ((name2.find("m_mmiss.fon") != std::string::npos)) { + addMapping('!', 0); + addMapping('"', 1); + addMapping('#', 2); + addMapping('$', 3); + addMapping('\'', 6); + addMapping('(', 7); + addMapping(')', 8); + addMapping('+', 10); + addMapping(',', 11); + addMapping('.', 13); + addMapping('/', 14); + addMapping(':', 25); + addMapping(';', 26); + addMapping('<', 27); + addMapping('=', 28); + addMapping('>', 29); + addMapping('?', 30); + addMapping('\\', 59); + addMapping('[', 58); + addMapping(']', 60); + addMapping('|', 91); + addMapping('~', 93); + for (int j = 65; j < 91; j++) { + addMapping(chr(j), j - 33); + } + for (int j = 97; j < 123; j++) { + addMapping(chr(j), j - 33); + } + for (int j = 48; j < 58; j++) { + addMapping(chr(j), j - 33); + } + for (int j = 192; j < 195; j++) { + addMapping(chr(j), j - 97); + } + // incomplete + } + else if ((name2.find("f_mtext.fon") != std::string::npos)) { + addMapping('!', 0); + addMapping('"', 1); + addMapping('#', 2); + addMapping('$', 3); + addMapping('%', 4); + addMapping('\'', 6); + addMapping('(', 7); + addMapping(')', 8); + addMapping(169, 9); // copyright + addMapping(',', 11); + addMapping('-', 12); + addMapping('.', 13); + addMapping('/', 14); + addMapping(':', 25); + addMapping(';', 26); + addMapping('<', 27); + addMapping('=', 28); + addMapping('>', 29); + addMapping('?', 30); + for (int j = 48; j < 58; j++) { + addMapping(chr(j), j - 33); + } + addMapping('\\', 59); + for (int j = 65; j < 91; j++) { + addMapping(chr(j), j - 33); + } + for (int j = 97; j < 123; j++) { + addMapping(chr(j), j - 33); + } + // incomplete + + } + else if ((name2.find("f_mhead.fon") != std::string::npos)) { + addMapping('!', 0); + addMapping('"', 1); + addMapping('#', 2); + addMapping('$', 3); + addMapping('\'', 6); + addMapping('(', 7); + addMapping(')', 8); + addMapping(',', 11); + addMapping('.', 13); + addMapping('/', 14); + addMapping(':', 25); + addMapping(';', 26); + addMapping('<', 27); + addMapping('>', 29); + addMapping('?', 30); + addMapping('\\', 59); + for (int j = 65; j < 91; j++) { + addMapping(chr(j), j - 33); + } + for (int j = 97; j < 123; j++) { + addMapping(chr(j), j - 33); + } + for (int j = 48; j < 58; j++) { + addMapping(chr(j), j - 33); + } + for (int j = 192; j < 195; j++) { + addMapping(chr(j), j - 97); + } + addMapping(196, 98); + addMapping(198, 99); + addMapping(199, 100); + for (int j=200; j < 208; j++) + addMapping(j, j - 99); + for (int j=210; j < 213; j++) + addMapping(j, j - 101); + addMapping(214, 112); + for (int j=217; j < 221; j++) + addMapping(j, j - 104); + addMapping(223, 117); + for (int j=224; j < 227; j++) + addMapping(j, j - 106); + addMapping(228, 121); + for (int j=230; j < 240; j++) + addMapping(j, j - 108); + for (int j=242; j < 245; j++) + addMapping(j, j - 110); + addMapping(246, 135); + for (int j=249; j < 253; j++) + addMapping(j, j - 113); + + } + else if ((name2.find("sub1.fon") != std::string::npos) || + (name2.find("sub2.fon") != std::string::npos)) { + addMapping('!', 0); + addMapping('"', 1); + addMapping('$', 3); + addMapping('\'', 6); // ´ + addMapping('(', 7); + addMapping(')', 8); + addMapping(',', 11); + addMapping('-', 12); // not in street1/2 + addMapping('.', 13); + addMapping('/', 14); + addMapping(':', 25); + addMapping(';', 26); + addMapping('<', 27); + addMapping('>', 29); + addMapping('?', 30); + + for (int j = 65; j < 91; j++) { + addMapping(chr(j), j - 33); + } + for (int j = 97; j < 123; j++) { + addMapping(chr(j), j - 33); + } + for (int j = 48; j < 58; j++) { + addMapping(chr(j), j - 33); + } + for (int j = 192; j < 195; j++) { + addMapping(chr(j), j - 97); + } + addMapping(196, 98); + addMapping(198, 99); + addMapping(199, 100); + for (int j=200; j < 208; j++) + addMapping(j, j - 99); + for (int j=210; j < 213; j++) + addMapping(j, j - 101); + addMapping(214, 112); + for (int j=217; j < 221; j++) + addMapping(j, j - 104); + addMapping(223, 117); + for (int j=224; j < 227; j++) + addMapping(j, j - 106); + addMapping(228, 121); + for (int j=230; j < 240; j++) + addMapping(j, j - 108); + for (int j=242; j < 245; j++) + addMapping(j, j - 110); + addMapping(246, 135); + for (int j=249; j < 253; j++) + addMapping(j, j - 113); + } + else if ((name2.find("score1.fon") != std::string::npos)|| + (name2.find("score2.fon") != std::string::npos) || + (name2.find("score8.fon") != std::string::npos)) { + for (int j = 48; j < 58; j++) { + addMapping(chr(j), j - 48); + } + } + else { + ERROR << "mapping for font " << name << " is not known" << std::endl; + } + } +} + +#ifdef FONT_DUMP_TOOL +#include +void do_exit() { + PHYSFS_deinit(); +} +int main(int argc, char* argv[]) { + PHYSFS_init(argv[0]); + atexit(do_exit); + std::cout << "Physfs-Base: " << PHYSFS_getBaseDir() << std::endl; + PHYSFS_addToSearchPath(PHYSFS_getBaseDir(), 1); + PHYSFS_addToSearchPath("gtadata.zip", 1); + std::cout << "Has: " << argv[1] << " : " << PHYSFS_exists(argv[1]) << std::endl; + OpenGTA::Font a(argv[1]); + a.dumpAs("out.bmp", atoi(argv[2])); + return 0; +} +#endif diff --git a/read_fxt.cpp b/read_fxt.cpp new file mode 100644 index 0000000..3013c57 --- /dev/null +++ b/read_fxt.cpp @@ -0,0 +1,135 @@ +/************************************************************************ +* Copyright (c) 2005-2006 tok@openlinux.org.uk * +* * +* This file contains code derived from information copyrighted by * +* DMA Design. It may not be used in a commercial product. * +* * +* See license.txt for details. * +* * +* This notice may not be removed or altered. * +************************************************************************/ +#include +#include +#include "opengta.h" + +namespace OpenGTA { + MessageDB::MessageDB() { + load("ENGLISH.FXT"); + _error = "ERROR"; + } + MessageDB::MessageDB(const std::string &file) { + load(file); + } + MessageDB::~MessageDB() { + messages.clear(); + } + void MessageDB::load(const std::string &file) { + PHYSFS_file* f = PHYSFS_openRead(file.c_str()); + if (f == NULL) { + std::string f2(file); + transform(f2.begin(), f2.end(), f2.begin(), tolower); + f = PHYSFS_openRead(f2.c_str()); + } + if (f == NULL) { + std::cerr << "Error: could not open " << file << " for reading" << std::endl; + return; + } + + messages.clear(); + + unsigned char v; + int c = -1; + unsigned char addVal = 0; + char buff[200]; + int i = 0; + std::string tmp; + while (!PHYSFS_eof(f)) { + PHYSFS_read(f, static_cast(&v), 1, 1); + + /* thanks to: Michael Mendelsohn + * http://gta.mendelsohn.de/ + */ + + v--; // decode: decrease by one + c++; // helper: count bytes read + if (c <= 7) // polyalphabetic code for the first 8 bytes + v -= (0x63 << c); + /* another twist: skip and add 64 (minus decoding) to next */ + if (v == 195) { + addVal = 64; + } + else { + v += addVal; + addVal = 0; + if (v == '[') { + i = 0; + } + else if (v == ']') { + buff[i] = 0x00; + tmp = std::string(buff); + i = 0; + } + else if (v == 0x00) { + buff[i] = 0x00; + if (tmp.length() > 0) + messages[tmp] = std::string(buff); + //std::cout << tmp << " : " << buff << std::endl; + /*else + std::cout << "Skipping: " << tmp << ": " << buff << std::endl;*/ + } + else { + buff[i] = v; + i++; + if (i>199) + i=0; + } + + } + } + PHYSFS_close(f); + } + + const std::string& MessageDB::getText(const char* id) { + std::map::iterator i = messages.find(std::string(id)); + if (i == messages.end()) { + std::cerr << "Error: string lookup failed for key: " << id << std::endl; + return _error; + } + return i->second; + } + + const std::string& MessageDB::getText(const std::string &id) { + std::map::iterator i = messages.find(id); + if (i == messages.end()) { + std::cerr << "Error: string lookup failed for key: " << id << std::endl; + return _error; + } + return i->second; + } + + const std::string& MessageDB::getText(const uint32_t id) { + char tmp[10]; + snprintf(reinterpret_cast(&tmp), 10, "%i", id); + std::map::iterator i = messages.find(std::string(tmp)); + if (i == messages.end()) { + std::cerr << "Error: string lookup failed for key: " << id << std::endl; + return _error; + } + return i->second; + } + +} + +#if FXT_TEST + +int main(int argc, char* argv[]) { + PHYSFS_init(argv[0]); + PHYSFS_addToSearchPath("gtadata.zip", 1); + OpenGTA::MessageDB* strings = new OpenGTA::MessageDB(); + std::cout << strings->getText(1001) << std::endl; + + delete strings; + PHYSFS_deinit(); +} + +#endif diff --git a/read_g24.cpp b/read_g24.cpp new file mode 100644 index 0000000..20c41af --- /dev/null +++ b/read_g24.cpp @@ -0,0 +1,473 @@ +/************************************************************************ +* Copyright (c) 2005-2006 tok@openlinux.org.uk * +* * +* This file contains code derived from information copyrighted by * +* DMA Design. It may not be used in a commercial product. * +* * +* See license.txt for details. * +* * +* This notice may not be removed or altered. * +************************************************************************/ +#include +#include +#include "opengta.h" +#include "buffercache.h" +#include "log.h" + +using namespace Util; +namespace OpenGTA { + +#define GTA_GRAPHICS_GRX 290 +#define GTA_GRAPHICS_GRY 325 +#define GTA_GRAPHICS_G24 336 + + Graphics24Bit::Graphics24Bit(const std::string& style) : GraphicsBase() { + fd = PHYSFS_openRead(style.c_str()); + assert(fd!=NULL); + _topHeaderSize = 64; + rawClut = NULL; + palIndex = NULL; + loadHeader(); + setupBlocking(style); + } + + Graphics24Bit::~Graphics24Bit() { + if (rawClut) + delete [] rawClut; + if (palIndex) + delete [] palIndex; + PHYSFS_close(fd); + } + + void Graphics24Bit::loadHeader() { + PHYSFS_uint32 vc; + PHYSFS_readULE32(fd, &vc); + if(vc != GTA_GRAPHICS_G24) { + ERROR << "graphics file specifies version " << vc << + " instead of " << GTA_GRAPHICS_G24 << std::endl; + return; + } + PHYSFS_readULE32(fd, &sideSize); + PHYSFS_readULE32(fd, &lidSize); + PHYSFS_readULE32(fd, &auxSize); + PHYSFS_readULE32(fd, &animSize); + PHYSFS_readULE32(fd, &clutSize); + PHYSFS_readULE32(fd, &tileclutSize); + PHYSFS_readULE32(fd, &spriteclutSize); + PHYSFS_readULE32(fd, &newcarclutSize); + PHYSFS_readULE32(fd, &fontclutSize); + PHYSFS_readULE32(fd, &paletteIndexSize); + PHYSFS_readULE32(fd, &objectInfoSize); + PHYSFS_readULE32(fd, &carInfoSize); + PHYSFS_readULE32(fd, &spriteInfoSize); + PHYSFS_readULE32(fd, &spriteGraphicsSize); + PHYSFS_readULE32(fd, &spriteNumberSize); + +/* + INFO << "Version: " << vc << std::endl << " Block textures: S " << sideSize / 4096 << " L " << + lidSize / 4096 << " A " << auxSize / 4096 << std::endl; + */ + if (sideSize % 4096 != 0) { + ERROR << "Side-Block texture size is not a multiple of 4096" << std::endl; + return; + } + if (lidSize % 4096 != 0) { + ERROR << "Lid-Block texture size is not a multiple of 4096" << std::endl; + return; + } + if (auxSize % 4096 != 0) { + ERROR << "Aux-Block texture size is not a multiple of 4096" << std::endl; + return; + } + + PHYSFS_uint32 tmp = sideSize / 4096 + lidSize / 4096 + auxSize / 4096; + tmp = tmp % 4; + if (tmp) { + auxBlockTrailSize = (4 - tmp) * 4096; + INFO << "adjusting aux-block by " << auxBlockTrailSize << std::endl; + } + INFO << "Anim size: " << animSize << std::endl; + INFO << "Obj-info size: " << objectInfoSize << " car-size: " << carInfoSize << + " sprite-info size: " << spriteInfoSize << " graphic size: " << spriteGraphicsSize << + " numbers s: " << spriteNumberSize << std::endl; + if (spriteNumberSize != 42) { + ERROR << "spriteNumberSize is " << spriteNumberSize << " (should be 42)" << std::endl; + return; + } + + INFO << " clut: " << clutSize << " tileclut: " << tileclutSize << " spriteclut: "<< spriteclutSize << + " newcar: " << newcarclutSize << " fontclut: " << fontclutSize << std::endl << + + "Obj-info size: " << objectInfoSize << " car-size: " << carInfoSize << + " pal-index size: " << paletteIndexSize << + std::endl; + + loadTileTextures(); + loadAnim(); + loadClut(); + loadPalIndex(); + loadObjectInfo(); + loadCarInfo(); + loadSpriteInfo(); + loadSpriteGraphics(); + loadSpriteNumbers(); + } + + void Graphics24Bit::loadClut() { + PHYSFS_uint64 st = static_cast(_topHeaderSize) + + sideSize + lidSize + auxSize + auxBlockTrailSize + animSize; + PHYSFS_seek(fd, st); + pagedClutSize = clutSize; + if (clutSize % 65536 != 0) + pagedClutSize += (65536 - (clutSize % 65536)); + rawClut = new unsigned char[pagedClutSize]; + assert(rawClut); + PHYSFS_read(fd, rawClut, 1, pagedClutSize); + //write(2, rawClut, pagedClutSize); + } + + void Graphics24Bit::loadPalIndex() { + PHYSFS_uint64 st = static_cast(_topHeaderSize) + + sideSize + lidSize + auxSize + auxBlockTrailSize + animSize + + pagedClutSize; + PHYSFS_seek(fd, st); + PHYSFS_uint16 pal_index_count = paletteIndexSize / 2; + assert(paletteIndexSize % 2 == 0); + palIndex = new PHYSFS_uint16[pal_index_count]; + for (PHYSFS_uint16 i = 0; i < pal_index_count; i++) { + PHYSFS_readULE16(fd, &palIndex[i]); + } + } + + void Graphics24Bit::loadCarInfo() { + PHYSFS_uint64 st = static_cast(_topHeaderSize) + + sideSize + lidSize + auxSize + auxBlockTrailSize + animSize + + pagedClutSize + paletteIndexSize + objectInfoSize; + loadCarInfo_shared(st); + } + + void Graphics24Bit::loadSpriteInfo() { + PHYSFS_uint64 st = static_cast(_topHeaderSize) + + sideSize + lidSize + auxSize + auxBlockTrailSize + animSize + + pagedClutSize + paletteIndexSize + objectInfoSize + + carInfoSize; + PHYSFS_seek(fd, st); + + PHYSFS_uint8 v; + PHYSFS_uint32 w; + PHYSFS_uint32 _bytes_read = 0; + while (_bytes_read < spriteInfoSize) { + SpriteInfo *si = new SpriteInfo(); + PHYSFS_read(fd, static_cast(&si->w), 1, 1); + PHYSFS_read(fd, static_cast(&si->h), 1, 1); + PHYSFS_read(fd, static_cast(&si->deltaCount), 1, 1); + PHYSFS_read(fd, static_cast(&v), 1, 1); + PHYSFS_readULE16(fd, &si->size); + _bytes_read += 6; + PHYSFS_readULE16(fd, &si->clut); + PHYSFS_read(fd, static_cast(&si->xoffset), 1, 1); + PHYSFS_read(fd, static_cast(&si->yoffset), 1, 1); + PHYSFS_readULE16(fd, &si->page); + _bytes_read += 6; + /* + std::cout << "sprite: " << int(si->w) << "x" << int(si->h) << " deltas: " << int(si->deltaCount) + << " clut: " << si->clut << " x: " << int(si->xoffset) << " y: " << int(si->yoffset) << + " page: " << si->page << std::endl; + */ + // sanity check + if (v) + WARN << "Compression flag active in sprite!" << std::endl; + if (int(si->w) * int(si->h) != int(si->size)) { + ERROR << "Sprite info size mismatch: " << int(si->w) << "x" << int(si->h) << + " != " << si->size << std::endl; + return; + } + if (si->deltaCount > 32) { + ERROR << "Delta count of sprite is " << si->deltaCount << std::endl; + return; + } + for (PHYSFS_uint8 j = 0; j < si->deltaCount; j++) { + si->delta[j].size = 0; + si->delta[j].ptr = 0; + if (si->deltaCount && (j < si->deltaCount)) { + PHYSFS_readULE16(fd, &si->delta[j].size); + PHYSFS_readULE32(fd, &w); + _bytes_read += 6; + si->delta[j].ptr = reinterpret_cast(w); + } + } + spriteInfos.push_back(si); + } + st = static_cast(_topHeaderSize) + + sideSize + lidSize + auxSize + auxBlockTrailSize + animSize + + pagedClutSize + paletteIndexSize + objectInfoSize + + carInfoSize + spriteInfoSize; + assert(PHYSFS_tell(fd) == PHYSFS_sint64(st)); + + } + + void Graphics24Bit::loadSpriteNumbers() { + PHYSFS_uint64 st = static_cast(_topHeaderSize) + + sideSize + lidSize + auxSize + auxBlockTrailSize + animSize + + pagedClutSize + paletteIndexSize + objectInfoSize + + carInfoSize + spriteInfoSize + spriteGraphicsSize; + loadSpriteNumbers_shared(st); + } + + void Graphics24Bit::loadSpriteGraphics() { + PHYSFS_uint64 st = static_cast(_topHeaderSize) + + sideSize + lidSize + auxSize + auxBlockTrailSize + animSize + + pagedClutSize + paletteIndexSize + objectInfoSize + + carInfoSize + spriteInfoSize; + PHYSFS_seek(fd, st); + + rawSprites = new unsigned char[spriteGraphicsSize]; + assert(rawSprites != NULL); + PHYSFS_read(fd, static_cast(rawSprites), spriteGraphicsSize, 1); + + std::vector::const_iterator i = spriteInfos.begin(); + std::vector::const_iterator end = spriteInfos.end(); + PHYSFS_uint32 _pagewise = 256 * 256; + while (i != end) { + SpriteInfo *info = *i; + for (uint8_t k = 0; k < info->deltaCount; ++k) { + PHYSFS_uint32 offset = reinterpret_cast(info->delta[k].ptr); + PHYSFS_uint32 page = offset / 65536; + PHYSFS_uint32 y = (offset % 65536) / 256; + PHYSFS_uint32 x = (offset % 65536) % 256; + info->delta[k].ptr = rawSprites + page * _pagewise + 256 * y + x; + } + i++; + } + } + + void Graphics24Bit::loadObjectInfo() { + PHYSFS_uint64 st = static_cast(_topHeaderSize) + + sideSize + lidSize + auxSize + auxBlockTrailSize + animSize + + pagedClutSize + paletteIndexSize; + loadObjectInfo_shared(st); + } + + void Graphics24Bit::applyClut(unsigned char* src, unsigned char* dst, + const size_t & len, const PHYSFS_uint16 & clutIdx, bool rgba) { + PHYSFS_uint32 off = 65536 * (clutIdx / 64) + 4 * (clutIdx % 64); + for (size_t i= 0; i < len; i++) { + PHYSFS_uint32 coff = PHYSFS_uint32(*src) * 256 + off; + *dst = rawClut[coff+2]; + ++dst; + *dst = rawClut[coff+1]; + ++dst; + *dst = rawClut[coff+0]; + ++dst; + if (rgba) { + if (*src == 0) + *dst = 0; + else + *dst = 0xff; + ++dst; + } + ++src; + } + } + + unsigned char *Graphics24Bit::getLid(unsigned int idx, unsigned int _not_used, bool rgba = false) { + prepareLidTexture(idx - 1, reinterpret_cast(tileTmp)); + unsigned char* src = tileTmp; + unsigned char* dst = (rgba) ? tileTmpRGBA : tileTmpRGB; + PHYSFS_uint16 clutIdx = palIndex[4 * (idx + sideSize / 4096)]; + applyClut(src, dst, 4096, clutIdx, rgba); + + return (rgba) ? tileTmpRGBA : tileTmpRGB; + } + + unsigned char *Graphics24Bit::getSide(unsigned int idx, unsigned int _not_used, bool rgba = false) { + prepareSideTexture(idx-1, reinterpret_cast(tileTmp)); + unsigned char* src = tileTmp; + unsigned char* dst = (rgba) ? tileTmpRGBA : tileTmpRGB; + PHYSFS_uint16 clutIdx = palIndex[idx*4]; + applyClut(src, dst, 4096, clutIdx, rgba); + + return (rgba) ? tileTmpRGBA : tileTmpRGB; + } + + unsigned char *Graphics24Bit::getAux(unsigned int idx, unsigned int _not_used, bool rgba = false) { + prepareAuxTexture(idx - 1, reinterpret_cast(tileTmp)); + + unsigned char* src = tileTmp; + unsigned char* dst = (rgba) ? tileTmpRGBA : tileTmpRGB; + PHYSFS_uint16 clutIdx = palIndex[4 * (idx + sideSize / 4096 + lidSize / 4096)]; + applyClut(src, dst, 4096, clutIdx, rgba); + + return (rgba) ? tileTmpRGBA : tileTmpRGB; + } + + void Graphics24Bit::dumpClut(const char* fname) { + assert(pagedClutSize % 1024 == 0); + //PHYSFS_uint32 num_clut = pagedClutSize / 1024; + PHYSFS_uint32 num_pal = paletteIndexSize / 2; +#if SDL_BYTEORDER == SDL_BIG_ENDIAN +#define rmask 0xff000000 +#define gmask 0x00ff0000 +#define bmask 0x0000ff00 +#define amask 0x000000ff +#else +#define rmask 0x000000ff +#define gmask 0x0000ff00 +#define bmask 0x00ff0000 +#define amask 0xff000000 +#endif + SDL_Surface* s = SDL_CreateRGBSurface(SDL_SWSURFACE|SDL_SRCALPHA, + num_pal, 256, 32, rmask, gmask, bmask, amask); + SDL_LockSurface(s); + unsigned char* dst = static_cast(s->pixels); + + for (PHYSFS_uint32 color = 0; color < 256; color++) { + + for (PHYSFS_uint32 pal_id = 0; pal_id < num_pal; pal_id++) { + PHYSFS_uint32 clut_id = palIndex[pal_id]; + PHYSFS_uint32 off = 65536 * (clut_id / 64) + 4 * (clut_id % 64); + + *dst = rawClut[off+color*256]; + ++dst; + *dst = rawClut[off+color*256+1]; + ++dst; + *dst = rawClut[off+color*256+2]; + ++dst; + *dst = 0xff; + ++dst; + } + + } + SDL_UnlockSurface(s); + SDL_SaveBMP(s, fname); + SDL_FreeSurface(s); + } + + unsigned char* Graphics24Bit::getSpriteBitmap(size_t id, int remap = -1, Uint32 delta = 0) { + const SpriteInfo *info = spriteInfos[id]; + assert(info != NULL); + const PHYSFS_uint32 y = info->yoffset; + const PHYSFS_uint32 x = info->xoffset; + const PHYSFS_uint32 page_size = 256 * 256; + + unsigned char * page_start = rawSprites + info->page * page_size; + + BufferCache & bcache = BufferCacheHolder::Instance(); + unsigned char * dest = bcache.requestBuffer(page_size); + bcache.lockBuffer(dest); + memcpy(dest, page_start, page_size); + if (delta > 0) { + handleDeltas(*info, dest, delta); + /* + assert(delta < info->deltaCount); + DeltaInfo & di = info->delta[delta]; + applyDelta(*info, dest+256*y+x, di); + */ + } + + unsigned char* bigbuf = bcache.requestBuffer(page_size * 4); + unsigned char* result = dest; + unsigned int skip_cluts = 0; + if (remap > -1) + skip_cluts = spriteclutSize / 1024 + remap + 1; + + PHYSFS_uint16 clutIdx = palIndex[info->clut + tileclutSize / 1024] + skip_cluts; + // PHYSFS_uint16 clutIdx = palIndex[info->clut + (spriteclutSize + tileclutSize) / 1024] + (remap > -1 ? remap+2 : 0); + applyClut(dest, bigbuf, page_size, clutIdx, true); + assert(page_size > PHYSFS_uint32(info->w * info->h * 4)); + for (uint16_t i = 0; i < info->h; i++) { + memcpy(result, bigbuf+(256*y+x)*4, info->w * 4); + result += info->w * 4; + bigbuf += 256 * 4; + } + + return dest; + } +} + +#ifdef G24_DUMPER + +SDL_Surface* image = NULL; + +void on_exit() { + if (image) + SDL_FreeSurface(image); + PHYSFS_deinit(); + SDL_Quit(); +} + +SDL_Surface* get_image(unsigned char* rp, unsigned int w, unsigned int h) { + assert(rp); +#if SDL_BYTEORDER == SDL_BIG_ENDIAN +#define rmask 0xff000000 +#define gmask 0x00ff0000 +#define bmask 0x0000ff00 +#define amask 0x000000ff +#else +#define rmask 0x000000ff +#define gmask 0x0000ff00 +#define bmask 0x00ff0000 +#define amask 0xff000000 +#endif + SDL_Surface* s = SDL_CreateRGBSurface(SDL_SWSURFACE|SDL_SRCALPHA, + w, h, 32, rmask, gmask, bmask, amask); + SDL_LockSurface(s); + unsigned char* dst = static_cast(s->pixels); + for (unsigned int i=0; i 2) + idx = atoi(argv[2]); + //image = get_image(graphics.getAux(idx, 0, true), 64,64); + OpenGTA::GraphicsBase::SpriteInfo * sinfo = graphics.getSprite(idx); + image = get_image(graphics.getSpriteBitmap(idx, -1, 0), sinfo->w, sinfo->h); + if (argc == 4) + SDL_SaveBMP(image, argv[3]); + else + display_image(image); + + return 0; +} + +#endif diff --git a/read_gry.cpp b/read_gry.cpp new file mode 100644 index 0000000..b706b35 --- /dev/null +++ b/read_gry.cpp @@ -0,0 +1,1009 @@ +/************************************************************************ +* Copyright (c) 2005-2006 tok@openlinux.org.uk * +* * +* This file contains code derived from information copyrighted by * +* DMA Design. It may not be used in a commercial product. * +* * +* See license.txt for details. * +* * +* This notice may not be removed or altered. * +************************************************************************/ +#include +#include +#include +#include "opengta.h" +#include "buffercache.h" +#include "log.h" +#include "m_exceptions.h" +#include "set.h" + +using namespace Util; + +namespace OpenGTA { + +#define GTA_GRAPHICS_GRX 290 +#define GTA_GRAPHICS_GRY 325 +#define GTA_GRAPHICS_G24 336 + + PHYSFS_uint16 GraphicsBase::SpriteNumbers::countByType(const SpriteTypes & t) const { + switch (t) { + case ARROW: + return GTA_SPRITE_ARROW; + case DIGIT: + return GTA_SPRITE_DIGITS; + case BOAT: + return GTA_SPRITE_BOAT; + case BOX: + return GTA_SPRITE_BOX; + case BUS: + return GTA_SPRITE_BUS; + case CAR: + return GTA_SPRITE_CAR; + case OBJECT: + return GTA_SPRITE_OBJECT; + case PED: + return GTA_SPRITE_PED; + case SPEEDO: + return GTA_SPRITE_SPEEDO; + case TANK: + return GTA_SPRITE_TANK; + case TRAFFIC_LIGHT: + return GTA_SPRITE_TRAFFIC_LIGHTS; + case TRAIN: + return GTA_SPRITE_TRAIN; + case TRDOOR: + return GTA_SPRITE_TRDOORS; + case BIKE: + return GTA_SPRITE_BIKE; + case TRAM: + return GTA_SPRITE_TRAM; + case WBUS: + return GTA_SPRITE_WBUS; + case WCAR: + return GTA_SPRITE_WCAR; + case EX: + return GTA_SPRITE_EX; + case TUMCAR: + return GTA_SPRITE_TUMCAR; + case TUMTRUCK: + return GTA_SPRITE_TUMTRUCK; + case FERRY: + return GTA_SPRITE_FERRY; + default: + break; + } + INFO << "UPS: " << t << std::endl; + assert(0); + } + PHYSFS_uint16 GraphicsBase::SpriteNumbers::reIndex(const PHYSFS_uint16 & id, const SpriteTypes & t) const { + switch (t) { + case ARROW: + return id; + case DIGIT: + return GTA_SPRITE_ARROW + id; + case BOAT: + return GTA_SPRITE_ARROW + GTA_SPRITE_DIGITS + id; + case BOX: + return GTA_SPRITE_ARROW + GTA_SPRITE_DIGITS + GTA_SPRITE_BOAT + id; + case BUS: + return GTA_SPRITE_ARROW + GTA_SPRITE_DIGITS + GTA_SPRITE_BOAT + + GTA_SPRITE_BOX + id; + case CAR: + return GTA_SPRITE_ARROW + GTA_SPRITE_DIGITS + GTA_SPRITE_BOAT + + GTA_SPRITE_BOX + GTA_SPRITE_BUS + id; + case OBJECT: + return GTA_SPRITE_ARROW + GTA_SPRITE_DIGITS + GTA_SPRITE_BOAT + + GTA_SPRITE_BOX + GTA_SPRITE_BUS + GTA_SPRITE_CAR + id; + case PED: + return GTA_SPRITE_ARROW + GTA_SPRITE_DIGITS + GTA_SPRITE_BOAT + + GTA_SPRITE_BOX + GTA_SPRITE_BUS + GTA_SPRITE_CAR + GTA_SPRITE_OBJECT + + id; + case SPEEDO: + return GTA_SPRITE_ARROW + GTA_SPRITE_DIGITS + GTA_SPRITE_BOAT + + GTA_SPRITE_BOX + GTA_SPRITE_BUS + GTA_SPRITE_CAR + GTA_SPRITE_OBJECT + + GTA_SPRITE_PED + id; + case TANK: + return GTA_SPRITE_ARROW + GTA_SPRITE_DIGITS + GTA_SPRITE_BOAT + + GTA_SPRITE_BOX + GTA_SPRITE_BUS + GTA_SPRITE_CAR + GTA_SPRITE_OBJECT + + GTA_SPRITE_PED + GTA_SPRITE_SPEEDO + id; + case TRAFFIC_LIGHT: + return GTA_SPRITE_ARROW + GTA_SPRITE_DIGITS + GTA_SPRITE_BOAT + + GTA_SPRITE_BOX + GTA_SPRITE_BUS + GTA_SPRITE_CAR + GTA_SPRITE_OBJECT + + GTA_SPRITE_PED + GTA_SPRITE_SPEEDO + GTA_SPRITE_TANK + id; + case TRAIN: + return GTA_SPRITE_ARROW + GTA_SPRITE_DIGITS + GTA_SPRITE_BOAT + + GTA_SPRITE_BOX + GTA_SPRITE_BUS + GTA_SPRITE_CAR + GTA_SPRITE_OBJECT + + GTA_SPRITE_PED + GTA_SPRITE_SPEEDO + GTA_SPRITE_TANK + + + GTA_SPRITE_TRAFFIC_LIGHTS + id; + case TRDOOR: + return GTA_SPRITE_ARROW + GTA_SPRITE_DIGITS + GTA_SPRITE_BOAT + + GTA_SPRITE_BOX + GTA_SPRITE_BUS + GTA_SPRITE_CAR + GTA_SPRITE_OBJECT + + GTA_SPRITE_PED + GTA_SPRITE_SPEEDO + GTA_SPRITE_TANK + + + GTA_SPRITE_TRAFFIC_LIGHTS + GTA_SPRITE_TRAIN + id; + case BIKE: + return GTA_SPRITE_ARROW + GTA_SPRITE_DIGITS + GTA_SPRITE_BOAT + + GTA_SPRITE_BOX + GTA_SPRITE_BUS + GTA_SPRITE_CAR + GTA_SPRITE_OBJECT + + GTA_SPRITE_PED + GTA_SPRITE_SPEEDO + GTA_SPRITE_TANK + + + GTA_SPRITE_TRAFFIC_LIGHTS + GTA_SPRITE_TRAIN + GTA_SPRITE_TRDOORS + id; + case TRAM: + return GTA_SPRITE_ARROW + GTA_SPRITE_DIGITS + GTA_SPRITE_BOAT + + GTA_SPRITE_BOX + GTA_SPRITE_BUS + GTA_SPRITE_CAR + GTA_SPRITE_OBJECT + + GTA_SPRITE_PED + GTA_SPRITE_SPEEDO + GTA_SPRITE_TANK + + + GTA_SPRITE_TRAFFIC_LIGHTS + GTA_SPRITE_TRAIN + GTA_SPRITE_TRDOORS + + GTA_SPRITE_BIKE + id; + case WBUS: + return GTA_SPRITE_ARROW + GTA_SPRITE_DIGITS + GTA_SPRITE_BOAT + + GTA_SPRITE_BOX + GTA_SPRITE_BUS + GTA_SPRITE_CAR + GTA_SPRITE_OBJECT + + GTA_SPRITE_PED + GTA_SPRITE_SPEEDO + GTA_SPRITE_TANK + + + GTA_SPRITE_TRAFFIC_LIGHTS + GTA_SPRITE_TRAIN + GTA_SPRITE_TRDOORS + + GTA_SPRITE_BIKE + GTA_SPRITE_TRAM + id; + case WCAR: + return GTA_SPRITE_ARROW + GTA_SPRITE_DIGITS + GTA_SPRITE_BOAT + + GTA_SPRITE_BOX + GTA_SPRITE_BUS + GTA_SPRITE_CAR + GTA_SPRITE_OBJECT + + GTA_SPRITE_PED + GTA_SPRITE_SPEEDO + GTA_SPRITE_TANK + + + GTA_SPRITE_TRAFFIC_LIGHTS + GTA_SPRITE_TRAIN + GTA_SPRITE_TRDOORS + + GTA_SPRITE_BIKE + GTA_SPRITE_TRAM + GTA_SPRITE_WBUS + id; + case EX: + return GTA_SPRITE_ARROW + GTA_SPRITE_DIGITS + GTA_SPRITE_BOAT + + GTA_SPRITE_BOX + GTA_SPRITE_BUS + GTA_SPRITE_CAR + GTA_SPRITE_OBJECT + + GTA_SPRITE_PED + GTA_SPRITE_SPEEDO + GTA_SPRITE_TANK + + + GTA_SPRITE_TRAFFIC_LIGHTS + GTA_SPRITE_TRAIN + GTA_SPRITE_TRDOORS + + GTA_SPRITE_BIKE + GTA_SPRITE_TRAM + GTA_SPRITE_WBUS + GTA_SPRITE_WCAR + id; + case TUMCAR: + return GTA_SPRITE_ARROW + GTA_SPRITE_DIGITS + GTA_SPRITE_BOAT + + GTA_SPRITE_BOX + GTA_SPRITE_BUS + GTA_SPRITE_CAR + GTA_SPRITE_OBJECT + + GTA_SPRITE_PED + GTA_SPRITE_SPEEDO + GTA_SPRITE_TANK + + + GTA_SPRITE_TRAFFIC_LIGHTS + GTA_SPRITE_TRAIN + GTA_SPRITE_TRDOORS + + GTA_SPRITE_BIKE + GTA_SPRITE_TRAM + GTA_SPRITE_WBUS + GTA_SPRITE_WCAR + + GTA_SPRITE_TUMCAR + id; + case TUMTRUCK: + return GTA_SPRITE_ARROW + GTA_SPRITE_DIGITS + GTA_SPRITE_BOAT + + GTA_SPRITE_BOX + GTA_SPRITE_BUS + GTA_SPRITE_CAR + GTA_SPRITE_OBJECT + + GTA_SPRITE_PED + GTA_SPRITE_SPEEDO + GTA_SPRITE_TANK + + + GTA_SPRITE_TRAFFIC_LIGHTS + GTA_SPRITE_TRAIN + GTA_SPRITE_TRDOORS + + GTA_SPRITE_BIKE + GTA_SPRITE_TRAM + GTA_SPRITE_WBUS + GTA_SPRITE_WCAR + + GTA_SPRITE_TUMCAR + GTA_SPRITE_TUMCAR + id; + case FERRY: + return GTA_SPRITE_ARROW + GTA_SPRITE_DIGITS + GTA_SPRITE_BOAT + + GTA_SPRITE_BOX + GTA_SPRITE_BUS + GTA_SPRITE_CAR + GTA_SPRITE_OBJECT + + GTA_SPRITE_PED + GTA_SPRITE_SPEEDO + GTA_SPRITE_TANK + + + GTA_SPRITE_TRAFFIC_LIGHTS + GTA_SPRITE_TRAIN + GTA_SPRITE_TRDOORS + + GTA_SPRITE_BIKE + GTA_SPRITE_TRAM + GTA_SPRITE_WBUS + GTA_SPRITE_WCAR + + GTA_SPRITE_TUMCAR + GTA_SPRITE_TUMCAR + GTA_SPRITE_TUMTRUCK + id; + } + assert(0); // should never be reached + return 0; + } + + GraphicsBase::GraphicsBase() : sideTexBlockMove(256) { + rawTiles = NULL; + rawSprites = NULL; + delta_is_a_set = false; + for (int i=0; i < 256; ++i) + sideTexBlockMove.set_item(i, true); + } + + bool GraphicsBase::isBlockingSide(uint8_t id) { + return sideTexBlockMove.get_item(id); + } + + void GraphicsBase::setupBlocking(const std::string & filename) { + // style001 + sideTexBlockMove.set_item(10, false); + sideTexBlockMove.set_item(20, false); + sideTexBlockMove.set_item(97, false); + sideTexBlockMove.set_item(109, false); + sideTexBlockMove.set_item(110, false); + sideTexBlockMove.set_item(115, false); + sideTexBlockMove.set_item(116, false); + sideTexBlockMove.set_item(155, false); + sideTexBlockMove.set_item(156, false); + sideTexBlockMove.set_item(157, false); + sideTexBlockMove.set_item(158, false); + + } + + bool GraphicsBase::getDeltaHandling() { + return delta_is_a_set; + } + + void GraphicsBase::setDeltaHandling(bool delta_as_set) { + delta_is_a_set = delta_as_set; + } + + GraphicsBase::~GraphicsBase() { + if (fd) + PHYSFS_close(fd); + std::vector::iterator i = animations.begin(); + while (i != animations.end()) { + delete *i; + i++; + } + animations.clear(); + std::vector::iterator i2 = spriteInfos.begin(); + while (i2 != spriteInfos.end()) { + delete *i2; + i2++; + } + spriteInfos.clear(); + std::vector::iterator i3 = objectInfos.begin(); + while (i3 != objectInfos.end()) { + delete *i3; + i3++; + } + objectInfos.clear(); + std::vector::iterator i4 = carInfos.begin(); + while (i4 != carInfos.end()) { + delete *i4; + i4++; + } + carInfos.clear(); + if (rawTiles) + delete [] rawTiles; + if (rawSprites) + delete [] rawSprites; + } + + bool GraphicsBase::isAnimatedBlock(uint8_t area_code, uint8_t id) { + std::vector::iterator i = animations.begin(); + while (i != animations.end()) { + if ((*i)->which == area_code && (*i)->block == id) + return true; + ++i; + } + return false; + } + + GraphicsBase::CarInfo* GraphicsBase::findCarByModel(PHYSFS_uint8 model) { + std::vector::iterator i = carInfos.begin(); + while (i != carInfos.end()) { + if ((*i)->model == model) + return *i; + ++i; + } + //throw std::string("Failed to find car by model"); + std::ostringstream o; + o << "Searching for car model " << int(model) << " failed"; + throw E_UNKNOWNKEY(o.str()); + return NULL; + } + + Graphics8Bit::Graphics8Bit(const std::string& style) : GraphicsBase() { + fd = PHYSFS_openRead(style.c_str()); + if (fd == NULL) { + std::string style2(style); + transform(style2.begin(), style2.end(), style2.begin(), tolower); + fd = PHYSFS_openRead(style2.c_str()); + } + if (fd == NULL) { + //throw std::string("FileNotFound: ") + style; + std::ostringstream o; + o << style << " with error: " << SDL_GetError(); + throw E_FILENOTFOUND(o.str()); + } + _topHeaderSize = 52; + rawTiles = NULL; + rawSprites = NULL; + masterRGB = NULL; + auxBlockTrailSize = 0; + loadHeader(); + setupBlocking(style); + } + + Graphics8Bit::~Graphics8Bit() { + //if (rawSprites) + // delete [] rawSprites; + if (masterRGB) + delete masterRGB; + } + + void Graphics8Bit::dump() { + + uint32_t gs = sideSize + lidSize + auxSize; + + INFO << "* graphics info *" << std::endl << + gs << " bytes in " << gs / 65536 << " pages " << gs / 4096 + << " images" << std::endl << + spriteInfos.size() << " sprites (" << spriteInfoSize <<") total: " << + spriteGraphicsSize << " bytes" << std::endl << + "sprite numbers: " << std::endl << + spriteNumbers.GTA_SPRITE_ARROW << " arrows" << std::endl << + spriteNumbers.GTA_SPRITE_DIGITS << " digits" << std::endl << + spriteNumbers.GTA_SPRITE_BOAT << " boats" << std::endl << + spriteNumbers.GTA_SPRITE_BOX << " boxes" << std::endl << + spriteNumbers.GTA_SPRITE_BUS << " buses" << std::endl << + spriteNumbers.GTA_SPRITE_CAR << " cars" << std::endl << + spriteNumbers.GTA_SPRITE_OBJECT << " objects" << std::endl << + spriteNumbers.GTA_SPRITE_PED << " peds" << std::endl << + spriteNumbers.GTA_SPRITE_SPEEDO << " speedos" << std::endl << + spriteNumbers.GTA_SPRITE_TANK << " tanks" << std::endl << + spriteNumbers.GTA_SPRITE_TRAFFIC_LIGHTS << " traffic lights" << std::endl << + spriteNumbers.GTA_SPRITE_TRAIN << " trains" << std::endl << + spriteNumbers.GTA_SPRITE_TRDOORS << " train doors" << std::endl << + spriteNumbers.GTA_SPRITE_BIKE << " bikes" << std::endl << + spriteNumbers.GTA_SPRITE_TRAM << " trams" << std::endl << + spriteNumbers.GTA_SPRITE_WBUS << " wbuses" << std::endl << + spriteNumbers.GTA_SPRITE_WCAR << " wcars" << std::endl << + spriteNumbers.GTA_SPRITE_EX << " exes"<< std::endl << + spriteNumbers.GTA_SPRITE_TUMCAR << " tumcars" << std::endl << + spriteNumbers.GTA_SPRITE_TUMTRUCK << " tumtrucks" << std::endl << + spriteNumbers.GTA_SPRITE_FERRY << " ferries"<< std::endl << + "#object-info: " << objectInfos.size() << " #car-info: " << carInfos.size() << std::endl; + } + + + + void Graphics8Bit::loadHeader() { + PHYSFS_uint32 vc; + PHYSFS_readULE32(fd, &vc); + if(vc != GTA_GRAPHICS_GRY) { + ERROR << "graphics file specifies version " << vc << + " instead of " << GTA_GRAPHICS_GRY << std::endl; + throw E_INVALIDFORMAT("8-bit loader failed"); + return; + } + PHYSFS_readULE32(fd, &sideSize); + PHYSFS_readULE32(fd, &lidSize); + PHYSFS_readULE32(fd, &auxSize); + PHYSFS_readULE32(fd, &animSize); + PHYSFS_readULE32(fd, &paletteSize); + PHYSFS_readULE32(fd, &remapSize); + PHYSFS_readULE32(fd, &remapIndexSize); + PHYSFS_readULE32(fd, &objectInfoSize); + PHYSFS_readULE32(fd, &carInfoSize); + PHYSFS_readULE32(fd, &spriteInfoSize); + PHYSFS_readULE32(fd, &spriteGraphicsSize); + PHYSFS_readULE32(fd, &spriteNumberSize); + + INFO << "Block textures: S " << sideSize / 4096 << " L " << + lidSize / 4096 << " A " << auxSize / 4096 << std::endl; + if (sideSize % 4096 != 0) { + ERROR << "Side-Block texture size is not a multiple of 4096" << std::endl; + return; + } + if (lidSize % 4096 != 0) { + ERROR << "Lid-Block texture size is not a multiple of 4096" << std::endl; + return; + } + if (auxSize % 4096 != 0) { + ERROR << "Aux-Block texture size is not a multiple of 4096" << std::endl; + return; + } + + PHYSFS_uint32 tmp = sideSize / 4096 + lidSize / 4096 + auxSize / 4096; + tmp = tmp % 4; + if (tmp) { + auxBlockTrailSize = (4 - tmp) * 4096; + INFO << "adjusting aux-block by " << auxBlockTrailSize << std::endl; + } + INFO << "Anim size: " << animSize << " palette size: " << paletteSize << + " remap size: " << remapSize << " remap-index size: " << remapIndexSize << std::endl; + INFO << "Obj-info size: " << objectInfoSize << " car-size: " << carInfoSize << + " sprite-info size: " << spriteInfoSize << " graphic size: " << spriteGraphicsSize << + " numbers s: " << spriteNumberSize << std::endl; + if (spriteNumberSize != 42) { + ERROR << "spriteNumberSize is " << spriteNumberSize << " (should be 42)" << std::endl; + return; + } + loadTileTextures(); + loadAnim(); + loadPalette(); + loadRemapTables(); + loadRemapIndex(); + loadObjectInfo(); + loadCarInfo(); + loadSpriteInfo(); + loadSpriteGraphics(); + loadSpriteNumbers(); + dump(); + } + + void GraphicsBase::loadAnim() { + PHYSFS_uint64 st = static_cast(_topHeaderSize) + + sideSize + lidSize + auxSize + auxBlockTrailSize; + PHYSFS_seek(fd, st); + PHYSFS_uint8 numAnim; + PHYSFS_read(fd, static_cast(&numAnim), 1, 1); + for (int i=0; i(&block), 1, 1); + PHYSFS_read(fd, static_cast(&which), 1, 1); + PHYSFS_read(fd, static_cast(&speed), 1, 1); + PHYSFS_read(fd, static_cast(&frameCount), 1, 1); + LoadedAnim *animation = new LoadedAnim(frameCount); + animation->block = block; + animation->which = which; + animation->speed = speed; + animation->frameCount = frameCount; + + if (animation->frameCount > 180) { + ERROR << "found animation with " << int(animation->frameCount) + << " frames ???" << std::endl; + } + for (int j=0; jframeCount; j++) { + uint8_t val; + PHYSFS_read(fd, static_cast(&val), 1, 1); + animation->frame.push_back(val); + //PHYSFS_read(fd, static_cast(&animations[i].frame[j]), 1, 1); + } + animations.push_back(animation); + } + } + + void Graphics8Bit::loadPalette() { + PHYSFS_uint64 st = static_cast(_topHeaderSize) + + sideSize + lidSize + auxSize + auxBlockTrailSize + animSize; + PHYSFS_seek(fd, st); + if (masterRGB) + delete masterRGB; + masterRGB = new RGBPalette(fd); + } + + void Graphics8Bit::loadRemapTables() { + PHYSFS_uint64 st = static_cast(_topHeaderSize) + + sideSize + lidSize + auxSize + auxBlockTrailSize + animSize + paletteSize; + PHYSFS_seek(fd, st); + PHYSFS_read(fd, static_cast(remapTables), 256, 256); + /* + for (int i=0; i < 256; i++) { + for (int j = 0; j < 256; j++) { + std::cout << int(remapTables[i][j]) << " "; + } + std::cout << std::endl; + }*/ + } + + void Graphics8Bit::loadRemapIndex() { + PHYSFS_uint64 st = static_cast(_topHeaderSize) + + sideSize + lidSize + auxSize + auxBlockTrailSize + animSize + paletteSize + + remapSize; + PHYSFS_seek(fd, st); + PHYSFS_read(fd, static_cast(&remapIndex), 4, 256); + /* + std::cout << "LID remap tables" << std::endl; + for (int i=0; i<256; ++i) { + std::cout << i << ": " << int(remapIndex[i][0]) << ", " << int(remapIndex[i][1]) << + ", " << int(remapIndex[i][2]) << ", " << int(remapIndex[i][3]) << std::endl; + }*/ + } + + void GraphicsBase::loadObjectInfo_shared(PHYSFS_uint64 offset) { + PHYSFS_seek(fd, offset); + assert(objectInfoSize % 20 == 0); + int c = objectInfoSize / 20; + + for (int i=0; i< c; i++) { + ObjectInfo *obj = new ObjectInfo(); + PHYSFS_readULE32(fd, &obj->width); + PHYSFS_readULE32(fd, &obj->height); + PHYSFS_readULE32(fd, &obj->depth); + PHYSFS_readULE16(fd, &obj->sprNum); + PHYSFS_readULE16(fd, &obj->weight); + PHYSFS_readULE16(fd, &obj->aux); + PHYSFS_read(fd, static_cast(&obj->status), 1, 1); + PHYSFS_read(fd, static_cast(&obj->numInto), 1, 1); + + objectInfos.push_back(obj); + } + + } + + void Graphics8Bit::loadObjectInfo() { + PHYSFS_uint64 st = static_cast(_topHeaderSize) + + sideSize + lidSize + auxSize + auxBlockTrailSize + animSize + paletteSize + + remapSize + remapIndexSize; + loadObjectInfo_shared(st); + } + + void GraphicsBase::loadCarInfo_shared(PHYSFS_uint64 offset) { + PHYSFS_seek(fd, offset); + + PHYSFS_uint32 bytes_read = 0; + while (bytes_read < carInfoSize) { + CarInfo * car = new CarInfo(); + + PHYSFS_readSLE16(fd, &car->width); + PHYSFS_readSLE16(fd, &car->height); + PHYSFS_readSLE16(fd, &car->depth); + PHYSFS_readSLE16(fd, &car->sprNum); + PHYSFS_readSLE16(fd, &car->weightDescriptor); + PHYSFS_readSLE16(fd, &car->maxSpeed); + PHYSFS_readSLE16(fd, &car->minSpeed); + PHYSFS_readSLE16(fd, &car->acceleration); + PHYSFS_readSLE16(fd, &car->braking); + PHYSFS_readSLE16(fd, &car->grip); + PHYSFS_readSLE16(fd, &car->handling); + bytes_read += 2 * 11; + + for (int i=0; i < 12; i++) { + PHYSFS_readSLE16(fd, &car->remap24[i].h); + PHYSFS_readSLE16(fd, &car->remap24[i].l); + PHYSFS_readSLE16(fd, &car->remap24[i].s); + } + bytes_read += 12 * 3 * 2; + for (int i=0; i < 12; i++) { + PHYSFS_read(fd, static_cast(&car->remap8[i]), 1, 1); + } + bytes_read += 12; + + PHYSFS_read(fd, static_cast(&car->vtype), 1, 1); + PHYSFS_read(fd, static_cast(&car->model), 1, 1); + PHYSFS_read(fd, static_cast(&car->turning), 1, 1); + PHYSFS_read(fd, static_cast(&car->damagable), 1, 1); + bytes_read += 4; + + for (int i=0; i < 4; i++) + PHYSFS_readULE16(fd, &car->value[i]); + bytes_read += 4 * 2; + + PHYSFS_read(fd, static_cast(&car->cx), 1, 1); + PHYSFS_read(fd, static_cast(&car->cy), 1, 1); + PHYSFS_readULE32(fd, &car->moment); + bytes_read += 2 + 4; + + PHYSFS_uint32 fixed_tmp; + PHYSFS_readULE32(fd, &fixed_tmp); + car->rbpMass = float(fixed_tmp)/65536; + PHYSFS_readULE32(fd, &fixed_tmp); + car->g1_Thrust = float(fixed_tmp)/65536; + PHYSFS_readULE32(fd, &fixed_tmp); + car->tyreAdhesionX = float(fixed_tmp)/65536; + PHYSFS_readULE32(fd, &fixed_tmp); + car->tyreAdhesionY = float(fixed_tmp)/65536; + PHYSFS_readULE32(fd, &fixed_tmp); + car->handBrakeFriction = float(fixed_tmp)/65536; + PHYSFS_readULE32(fd, &fixed_tmp); + car->footBrakeFriction = float(fixed_tmp)/65536; + PHYSFS_readULE32(fd, &fixed_tmp); + car->frontBrakeBias = float(fixed_tmp)/65536; + bytes_read += 7 * 4; + + PHYSFS_readSLE16(fd, &car->turnRatio); + PHYSFS_readSLE16(fd, &car->driveWheelOffset); + PHYSFS_readSLE16(fd, &car->steeringWheelOffset); + bytes_read += 3 * 2; + + PHYSFS_readULE32(fd, &fixed_tmp); + car->backEndSlideValue = float(fixed_tmp)/65536; + PHYSFS_readULE32(fd, &fixed_tmp); + car->handBrakeSlideValue = float(fixed_tmp)/65536; + bytes_read += 2 * 4; + + PHYSFS_read(fd, static_cast(&car->convertible), 1, 1); + PHYSFS_read(fd, static_cast(&car->engine), 1, 1); + PHYSFS_read(fd, static_cast(&car->radio), 1, 1); + PHYSFS_read(fd, static_cast(&car->horn), 1, 1); + PHYSFS_read(fd, static_cast(&car->soundFunction), 1, 1); + PHYSFS_read(fd, static_cast(&car->fastChangeFlag), 1, 1); + bytes_read += 6; + + PHYSFS_readSLE16(fd, &car->numDoors); + bytes_read += 2; + + for (int i=0; i < car->numDoors; i++) { + PHYSFS_readSLE16(fd, &car->door[i].rpx); + PHYSFS_readSLE16(fd, &car->door[i].rpy); + PHYSFS_readSLE16(fd, &car->door[i].object); + PHYSFS_readSLE16(fd, &car->door[i].delta); + bytes_read += 4 * 2; + } + carInfos.push_back(car); + + } + assert(bytes_read == carInfoSize); + + } + + void Graphics8Bit::loadCarInfo() { + PHYSFS_uint64 st = static_cast(_topHeaderSize) + + sideSize + lidSize + auxSize + auxBlockTrailSize + animSize + paletteSize + + remapSize + remapIndexSize + objectInfoSize; + loadCarInfo_shared(st); + } + + void Graphics8Bit::loadSpriteInfo() { + PHYSFS_uint64 st = static_cast(_topHeaderSize) + + sideSize + lidSize + auxSize + auxBlockTrailSize + animSize + paletteSize + + remapSize + remapIndexSize + objectInfoSize + carInfoSize; + PHYSFS_seek(fd, st); + + PHYSFS_uint8 v; + PHYSFS_uint32 w; + PHYSFS_uint32 _bytes_read = 0; + while (_bytes_read < spriteInfoSize) { + SpriteInfo *si = new SpriteInfo(); + PHYSFS_read(fd, static_cast(&si->w), 1, 1); + PHYSFS_read(fd, static_cast(&si->h), 1, 1); + PHYSFS_read(fd, static_cast(&si->deltaCount), 1, 1); + PHYSFS_read(fd, static_cast(&v), 1, 1); + PHYSFS_readULE16(fd, &si->size); + PHYSFS_readULE32(fd, &w); + _bytes_read += 10; + //si->ptr = reinterpret_cast(w); + si->page = w / 65536; + si->xoffset = (w % 65536) % 256; + si->yoffset = (w % 65536) / 256; + si->clut = 0; + + /* + std::cout << int(si->w) << "," << int(si->h) << " = " << si->size << " deltas: " << int(si->deltaCount) << + " at " << w << std::endl; + */ + + // sanity check + if (v) + WARN << "Compression flag active in sprite!" << std::endl; + if (int(si->w) * int(si->h) != int(si->size)) { + ERROR << "Sprite info size mismatch: " << int(si->w) << "x" << int(si->h) << + " != " << si->size << std::endl; + return; + } + if (si->deltaCount > 32) { + ERROR << "Delta count of sprite is " << si->deltaCount << std::endl; + return; + } + for (PHYSFS_uint8 j = 0; j < 33; ++j) { + si->delta[j].size = 0; + si->delta[j].ptr = 0; + if (si->deltaCount && (j < si->deltaCount)) { + //std::cout << "reading " << int(j) << std::endl; + PHYSFS_readULE16(fd, &si->delta[j].size); + PHYSFS_readULE32(fd, &w); + _bytes_read += 6; + si->delta[j].ptr = reinterpret_cast(w); + } + } + spriteInfos.push_back(si); + + } + st = static_cast(_topHeaderSize) + + sideSize + lidSize + auxSize + auxBlockTrailSize + animSize + paletteSize + + remapSize + remapIndexSize + objectInfoSize + carInfoSize + spriteInfoSize; + assert(PHYSFS_tell(fd) == PHYSFS_sint64(st)); + } + + void Graphics8Bit::loadSpriteGraphics() { + PHYSFS_uint64 st = static_cast(_topHeaderSize) + + sideSize + lidSize + auxSize + auxBlockTrailSize + animSize + paletteSize + + remapSize + remapIndexSize + objectInfoSize + carInfoSize + spriteInfoSize; + PHYSFS_seek(fd, st); + rawSprites = new unsigned char[spriteGraphicsSize]; + assert(rawSprites != NULL); + PHYSFS_read(fd, static_cast(rawSprites), spriteGraphicsSize, 1); + + if (spriteInfos.size() == 0) { + INFO << "No SpriteInfo post-loading work done - structure is empty" << std::endl; + return; + } + std::vector::const_iterator i = spriteInfos.begin(); + std::vector::const_iterator end = spriteInfos.end(); + PHYSFS_uint32 _pagewise = 256 * 256; + while (i != end) { + SpriteInfo *info = *i; + /* + PHYSFS_uint32 offset = reinterpret_cast(info->ptr); + PHYSFS_uint32 page = offset / 65536; + PHYSFS_uint32 y = (offset % 65536) / 256; + PHYSFS_uint32 x = (offset % 65536) % 256; + */ + //std::cout << int(info->w) << "x" << int(info->h) << " " << int(info->deltaCount) << " deltas" << std::endl; + //std::cout << offset << " page " << page << " x,y " << x <<","<ptr = rawSprites + page * _pagewise + 256 * y + x; + for (uint8_t k = 0; k < info->deltaCount; ++k) { + PHYSFS_uint32 offset = reinterpret_cast(info->delta[k].ptr); + PHYSFS_uint32 page = offset / 65536; + PHYSFS_uint32 y = (offset % 65536) / 256; + PHYSFS_uint32 x = (offset % 65536) % 256; + info->delta[k].ptr = rawSprites + page * _pagewise + 256 * y + x; + } + i++; + } + + return; + } + + void GraphicsBase::loadSpriteNumbers_shared(PHYSFS_uint64 offset) { + + PHYSFS_seek(fd, offset); + + PHYSFS_readULE16(fd, &spriteNumbers.GTA_SPRITE_ARROW); + PHYSFS_readULE16(fd, &spriteNumbers.GTA_SPRITE_DIGITS); + PHYSFS_readULE16(fd, &spriteNumbers.GTA_SPRITE_BOAT); + PHYSFS_readULE16(fd, &spriteNumbers.GTA_SPRITE_BOX); + PHYSFS_readULE16(fd, &spriteNumbers.GTA_SPRITE_BUS); + PHYSFS_readULE16(fd, &spriteNumbers.GTA_SPRITE_CAR); + PHYSFS_readULE16(fd, &spriteNumbers.GTA_SPRITE_OBJECT); + PHYSFS_readULE16(fd, &spriteNumbers.GTA_SPRITE_PED); + PHYSFS_readULE16(fd, &spriteNumbers.GTA_SPRITE_SPEEDO); + PHYSFS_readULE16(fd, &spriteNumbers.GTA_SPRITE_TANK); + PHYSFS_readULE16(fd, &spriteNumbers.GTA_SPRITE_TRAFFIC_LIGHTS); + PHYSFS_readULE16(fd, &spriteNumbers.GTA_SPRITE_TRAIN); + PHYSFS_readULE16(fd, &spriteNumbers.GTA_SPRITE_TRDOORS); + PHYSFS_readULE16(fd, &spriteNumbers.GTA_SPRITE_BIKE); + PHYSFS_readULE16(fd, &spriteNumbers.GTA_SPRITE_TRAM); + PHYSFS_readULE16(fd, &spriteNumbers.GTA_SPRITE_WBUS); + PHYSFS_readULE16(fd, &spriteNumbers.GTA_SPRITE_WCAR); + PHYSFS_readULE16(fd, &spriteNumbers.GTA_SPRITE_EX); + PHYSFS_readULE16(fd, &spriteNumbers.GTA_SPRITE_TUMCAR); + PHYSFS_readULE16(fd, &spriteNumbers.GTA_SPRITE_TUMTRUCK); + PHYSFS_readULE16(fd, &spriteNumbers.GTA_SPRITE_FERRY); + + } + + void Graphics8Bit::loadSpriteNumbers() { + PHYSFS_uint64 st = static_cast(_topHeaderSize) + + sideSize + lidSize + auxSize + auxBlockTrailSize + animSize + paletteSize + + remapSize + remapIndexSize + objectInfoSize + carInfoSize + + spriteInfoSize + spriteGraphicsSize; + loadSpriteNumbers_shared(st); + } + + void GraphicsBase::loadTileTextures() { + PHYSFS_seek(fd, static_cast(_topHeaderSize)); + + PHYSFS_uint64 ts = sideSize+lidSize+auxSize; + rawTiles = new unsigned char[ts]; + int r = PHYSFS_read(fd, static_cast(rawTiles), 1, ts); + if ( PHYSFS_uint64(r) == ts ) + return; + else if ( r == -1) { + INFO << "Could not read texture raw data" << std::endl; + return; + } + else + INFO << "This message should never be displayed! (" << std::endl; + } + + void GraphicsBase::handleDeltas(const SpriteInfo & info, unsigned char* buffer, Uint32 delta) { + const unsigned int b_offset = 256 * info.yoffset + info.xoffset; + if (delta_is_a_set) { + Util::Set delta_set(32, (unsigned char*)&delta); + for (int i = 0; i < 32; ++i) { + if (delta_set.get_item(i)) { + assert(i < info.deltaCount); + const DeltaInfo & di = info.delta[i]; + applyDelta(info, buffer, b_offset, di); + } + } + assert(0); + } + else { + // delta is only an index; one to big + assert(delta <= info.deltaCount); + const DeltaInfo & di = info.delta[delta - 1]; + applyDelta(info, buffer, b_offset, di); + } + } + + unsigned char* Graphics8Bit::getSpriteBitmap(size_t id, int remap = -1, Uint32 delta = 0) { + SpriteInfo *info = spriteInfos[id]; + assert(info != NULL); + //PHYSFS_uint32 offset = reinterpret_cast(info->ptr); + //const PHYSFS_uint32 page = offset / 65536; + const PHYSFS_uint32 y = info->yoffset; // (offset % 65536) / 256; + const PHYSFS_uint32 x = info->xoffset; // (offset % 65536) % 256; + const PHYSFS_uint32 page_size = 256 * 256; + + unsigned char * page_start = rawSprites + info->page * page_size;// + 256 * y + x; + assert(page_start != NULL); + + BufferCache & bcache = BufferCacheHolder::Instance(); + unsigned char * dest = bcache.requestBuffer(page_size); + bcache.lockBuffer(dest); + + unsigned char * result = dest; + assert(dest != NULL); + memcpy(dest, page_start, page_size); + if (delta > 0) { + handleDeltas(*info, result, delta); + /* + assert(delta < info->deltaCount); + DeltaInfo & di = info->delta[delta]; + applyDelta(*info, result+256*y+x, di); + */ + } + if (remap > -1) + applyRemap(page_size, remap, result); + unsigned char* bigbuf = bcache.requestBuffer(page_size * 4); + + masterRGB->apply(page_size, result, bigbuf, true); + assert(page_size > PHYSFS_uint32(info->w * info->h * 4)); + for (uint16_t i = 0; i < info->h; i++) { + memcpy(result, bigbuf+(256*y+x)*4, info->w * 4); + result += info->w * 4; + bigbuf += 256 * 4; + } + + return dest; + } + + void GraphicsBase::applyDelta(const SpriteInfo & spriteInfo, unsigned + char* buffer, Uint32 page_offset, const DeltaInfo & deltaInfo, bool mirror) { + unsigned char* b = buffer + page_offset; + unsigned char* delta = deltaInfo.ptr; + PHYSFS_sint32 length_to_go = deltaInfo.size; + + if (mirror) { + PHYSFS_uint32 doff = 0; + while (length_to_go > 0) { + PHYSFS_uint16* offset = (PHYSFS_uint16*) delta; + doff += *offset; + delta += 2; + unsigned char this_length = *delta; + ++delta; + PHYSFS_uint32 noff = page_offset + doff; + PHYSFS_uint32 _y = noff / 256 * 256; + PHYSFS_uint32 _x = doff % 256; + for (int i=0; i < this_length; i++) + *(buffer + _y + spriteInfo.xoffset + spriteInfo.w - _x - i - 1) = *(delta+i); + length_to_go -= (this_length + 3); + doff += this_length; + delta += this_length; + } + return; + } + + while (length_to_go > 0) { + PHYSFS_uint16* offset = (PHYSFS_uint16*) delta; + b += *offset; + delta += 2; + unsigned char this_length = *delta; + ++delta; + memcpy(b, delta, this_length); + b += this_length; + delta += this_length; + length_to_go -= (this_length + 3); + } + + } + + void Graphics8Bit::applyRemap(unsigned int len, unsigned int which, unsigned char* buffer) { + assert(buffer!=NULL); + unsigned char* t = buffer; + for (unsigned int i = 0; i < len; ++i) { + *t = remapTables[which][*t]; //FIXME: is this the right order? Is this correct at all? + t++; + } + } + + void GraphicsBase::prepareSideTexture(unsigned int idx, unsigned char* dst) { + assert(dst!=NULL); + ++idx; + assert(rawTiles); + unsigned char* rt = rawTiles + (idx / 4) * 4096 * 4 + (idx % 4) * 64; + for (int i=0; i<64; i++) { + memcpy(dst, rt, 64); + dst += 64; + rt += 64*4; + } + } + + unsigned char* Graphics8Bit::getSide(unsigned int idx, unsigned int palIdx, bool rgba = false) { + prepareSideTexture(idx-1, reinterpret_cast(tileTmp)); + unsigned char *res; + if (rgba) { + masterRGB->apply(4096, reinterpret_cast(tileTmp), + reinterpret_cast(tileTmpRGBA), true); + res = reinterpret_cast(tileTmpRGBA); + } + else { + masterRGB->apply(4096, reinterpret_cast(tileTmp), + reinterpret_cast(tileTmpRGB), false); + res = reinterpret_cast(tileTmpRGB); + } + return res; + } + + void GraphicsBase::prepareLidTexture(unsigned int idx, unsigned char* dst) { + assert(dst!=NULL); + unsigned char* rt = rawTiles; + assert(rawTiles); + idx += sideSize / 4096 + 1; // FIXME: assumes partition == block end + rt += (idx / 4) * 4096 * 4 + (idx % 4) * 64; + for (int i=0; i<64; i++) { + memcpy(dst, rt, 64); + dst += 64; + rt += 64*4; + } + } + + unsigned char *Graphics8Bit::getLid(unsigned int idx, unsigned int palIdx, bool rgba = false) { + prepareLidTexture(idx-1, reinterpret_cast(tileTmp)); + if (palIdx > 0) + applyRemap(4096, palIdx, reinterpret_cast(tileTmp)); + + unsigned char *res; + if (rgba) { + masterRGB->apply(4096, reinterpret_cast(tileTmp), + reinterpret_cast(tileTmpRGBA), true); + res = reinterpret_cast(tileTmpRGBA); + } + else { + masterRGB->apply(4096, reinterpret_cast(tileTmp), + reinterpret_cast(tileTmpRGB), false); + res = reinterpret_cast(tileTmpRGB); + } + return res; + } + + void GraphicsBase::prepareAuxTexture(unsigned int idx, unsigned char* dst) { + assert(dst!=NULL); + unsigned char* rt = rawTiles; + assert(rawTiles); + idx += (sideSize+lidSize)/4096 + 1; // FIXME: assumes partition == block end + rt += (idx / 4) * 4096 * 4 + (idx % 4) * 64; + for (int i=0; i<64; i++) { + memcpy(dst, rt, 64); + dst += 64; + rt += 64*4; + } + } + + unsigned char* Graphics8Bit::getAux(unsigned int idx, unsigned int palIdx, bool rgba = false) { + prepareAuxTexture(idx-1, reinterpret_cast(tileTmp)); + unsigned char *res; + if (rgba) { + + masterRGB->apply(4096, reinterpret_cast(tileTmp), + reinterpret_cast(tileTmpRGBA), true); + res = reinterpret_cast(tileTmpRGBA); + } + else { + masterRGB->apply(4096, reinterpret_cast(tileTmp), + reinterpret_cast(tileTmpRGB), false); + res = reinterpret_cast(tileTmpRGB); + } + return res; + } + + unsigned char* GraphicsBase::getTmpBuffer(bool rgba = false) { + if (rgba) + return reinterpret_cast(tileTmpRGBA); + return reinterpret_cast(tileTmpRGB); + } + + /* RGBPalette */ + Graphics8Bit::RGBPalette::RGBPalette() { + } + + Graphics8Bit::RGBPalette::RGBPalette(const std::string& palette) { + PHYSFS_file* fd = PHYSFS_openRead(palette.c_str()); + if (fd == NULL) { + std::string pal2(palette); + transform(pal2.begin(), pal2.end(), pal2.begin(), tolower); + fd = PHYSFS_openRead(pal2.c_str()); + } + if (!fd) { + std::ostringstream o; + o << palette << " with error: " << PHYSFS_getLastError(); + throw E_FILENOTFOUND(o.str()); + } + loadFromFile(fd); + } + + Graphics8Bit::RGBPalette::RGBPalette(PHYSFS_file* fd) { + loadFromFile(fd); + } + + int Graphics8Bit::RGBPalette::loadFromFile(PHYSFS_file* fd) { + PHYSFS_read(fd, static_cast(&data), 1, 256*3); + return 0; + } + + void Graphics8Bit::RGBPalette::apply(unsigned int len, const unsigned char* src, + unsigned char* dst, bool rgba ) { + for (unsigned int i = 0; i < len; i++) { + *dst = data[*src * 3 ]; ++dst; + *dst = data[*src * 3 + 1]; ++dst; + *dst = data[*src * 3 + 2]; ++dst; + if (rgba) { + if (*src == 0) + *dst = 0x00; + else + *dst = 0xff; + ++dst; + } + ++src; + } + } +} diff --git a/read_ini.cpp b/read_ini.cpp new file mode 100644 index 0000000..0d9f8b9 --- /dev/null +++ b/read_ini.cpp @@ -0,0 +1,123 @@ +/************************************************************************ + * Copyright (c) 2005-2006 tok@openlinux.org.uk * + * * + * This file contains code derived from information copyrighted by * + * DMA Design. It may not be used in a commercial product. * + * * + * See license.txt for details. * + * * + * This notice may not be removed or altered. * + ************************************************************************/ +#include +#include +#include +#include +#include + +namespace OpenGTA { + + class ScriptParser { + public: + ScriptParser(const std::string &file); + ~ScriptParser(); + void loadLevel(PHYSFS_uint32 level); + private: + typedef std::map LevelMapType; + LevelMapType levels; + PHYSFS_file* fd; + }; + + ScriptParser::ScriptParser(const std::string &file) { + fd = PHYSFS_openRead(file.c_str()); + if (!fd) { + std::cerr << "Error: could not open file " << file << " for reading!" << std::endl; + } + else { + std::cout << "* Loading script " << file << " ... "; + unsigned char v; + unsigned char m = 0; + char buffer[10]; + char* b = reinterpret_cast(&buffer); + while(!PHYSFS_eof(fd)) { + PHYSFS_read(fd, static_cast(&v), 1, 1); + if (m) { + if (v != ']') { + *b = v; + b++; + } + else { + m = 0; + *b = 0x00; + b = reinterpret_cast(&buffer); + levels[static_cast(atoi(buffer))] = PHYSFS_tell(fd) + 1; + } + } + if (v == '[') { + m = 1; + } + } + std::cout << int(levels.size()) << " sections indexed" << std::endl; + } + } + + ScriptParser::~ScriptParser() { + if (fd != NULL) + PHYSFS_close(fd); + levels.clear(); + } + + void ScriptParser::loadLevel(PHYSFS_uint32 level) { + LevelMapType::iterator i = levels.find(level); + if (i == levels.end()) { + std::cerr << "not a valid level: " << level << std::endl; + return; + } + PHYSFS_seek(fd, i->second); + char buffer[256]; + PHYSFS_uint16 read_bytes = 255; + PHYSFS_uint16 offset = 0; + while(!PHYSFS_eof(fd)) { + memset(buffer+offset, 0, read_bytes+1); + PHYSFS_read(fd, buffer + offset, 1, read_bytes); + char* line_start = buffer; + while (1) { + char* line_end = strchr(line_start, '\r'); + if (line_start && line_end) { + *line_end = 0; + if (strlen(line_start) > 0) { + std::cout <<"["<< line_start << "]" << strlen(line_start)< +#include "m_exceptions.h" +#include "fx_sdt.h" + +#ifdef SOUND_DUMPER +#include +#endif +namespace OpenGTA { + SoundsDB::Entry::Entry(PHYSFS_uint32 r1, PHYSFS_uint32 r2, PHYSFS_uint32 sr) { + rawStart = r1; + rawSize = r2; + sampleRate = sr; + } + + SoundsDB::SoundsDB() { + dataFile = 0; + } + + SoundsDB::SoundsDB(const std::string & sdt_file) { + dataFile = PHYSFS_openRead(sdt_file.c_str()); + if (!dataFile) { + std::string sdt2(sdt_file); + transform(sdt2.begin(), sdt2.end(), sdt2.begin(), tolower); + dataFile = PHYSFS_openRead(sdt2.c_str()); + } + if (!dataFile) + throw E_FILENOTFOUND(sdt_file); + //throw std::string("FileNotFound: ") + sdt_file; + PHYSFS_uint32 num_e = PHYSFS_fileLength(dataFile); + if (num_e % 12) { + //throw std::string("Ups: invalid SDT file?"); + std::stringstream o; + o << "SDT filesize " << num_e << " % 12 != 0"; + throw E_INVALIDFORMAT(o.str()); + } + num_e /= 12; + PHYSFS_uint32 r1, r2, sr; + for (PHYSFS_uint16 i = 0; i < num_e; i++) { + PHYSFS_readULE32(dataFile, &r1); + PHYSFS_readULE32(dataFile, &r2); + PHYSFS_readULE32(dataFile, &sr); +#ifdef SOUND_DUMPER + std::cout << i << " " << r1 << " " << r2 << " " << sr << std::endl; +#endif + knownEntries.insert( std::make_pair(i, Entry(r1, r2, sr))); + } + PHYSFS_close(dataFile); + + std::string raw_file(sdt_file); + raw_file.replace(raw_file.size() - 3, 3, "RAW"); + dataFile = PHYSFS_openRead(raw_file.c_str()); + if (!dataFile) { + std::string sdt2(raw_file); + transform(sdt2.begin(), sdt2.end(), sdt2.begin(), tolower); + dataFile = PHYSFS_openRead(sdt2.c_str()); + } + assert(dataFile); + + } + + SoundsDB::~SoundsDB() { + clear(); + if (dataFile) + PHYSFS_close(dataFile); + } + + void SoundsDB::clear() { + knownEntries.clear(); + if (dataFile) + PHYSFS_close(dataFile); + dataFile = 0; + } + + SoundsDB::Entry & SoundsDB::getEntry(KeyType key) { + MapType::iterator i = knownEntries.find(key); + if (i == knownEntries.end()) { + //throw std::string("Unknown sound-db entry"); + std::ostringstream o; + o << "Querying for sound id: " << key; + throw E_UNKNOWNKEY(o.str()); + } + return i->second; + } + + unsigned char* SoundsDB::getBuffered(KeyType key) { + Entry & e = getEntry(key); + unsigned int t_len = e.rawSize;// + 36 + 8; + unsigned char* buf = new unsigned char[t_len]; + assert(buf); + memset(buf, 0, t_len); + PHYSFS_seek(dataFile, e.rawStart); + PHYSFS_read(dataFile, buf, 1, e.rawSize); + return buf; + } + +} + +#ifdef SOUND_DUMPER +int main(int argc, char*argv[]) { + PHYSFS_init(argv[0]); + PHYSFS_addToSearchPath("gtadata.zip", 1); + try { + OpenGTA::SoundsDB sounds(argv[1]); + } + catch (std::string & e) { + std::cerr << e << std::endl; + } + PHYSFS_deinit(); +} +#endif diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..4a0150f --- /dev/null +++ b/readme.txt @@ -0,0 +1,134 @@ +~-~-~ OGTA is OpenGTA ~-~-~ + += Required software = +* OpenGL + [ http://www.opengl.org/ ] +* SDL ( & SDL_image ) + [ http://www.libsdl.org/ ] +* PhysicsFS + [ http://www.icculus.org/physfs/ ] +* Loki + [ http://sourceforge.net/projects/loki-lib/ ] + +* g++ ( GNU C++ ) + [ http://gcc.gnu.org/ ] +* GNU Make + [ http://www.gnu.org/software/make/ ] + += Compiling = + +Only tested on GNU Linux using gcc & make; I assume that GL, SDL and +PhysicsFS are installed, Loki is downloaded and compiled automatically. + +Just run 'make' or specifically 'make viewer'. + +The other programs are/were used for development or debugging. + += Installing the data-files = + +You can download the game from http://www.rockstargames.com/classics/ . + +These programs expect to find the data in the current directory, either +directly in the file-system or in a ZIP file (named 'gtadata.zip'). +You want the content of the original game directory GTADATA, but not +the directory itself. +See mods/using_mods.txt for a slightly longer description. + +Needed: +*.FXT, *.FON, *.CMP + +STYLE*.GRY (for 8-bit graphics) +STYLE*.G24 (for 24-bit graphics) + += Running = + +== gfxextract == + +Export/Display textures and sprites; +run ./gfxextract -h (or without any parameters) for usage info. + +== viewer == + +Brain-dead immediate-mode renderer of the city (now with objects); with +plenty of bugs... + +start as: +./viewer [flags] [0-2] + +The optional param loads the respective city; default is 0: +0 - NYC.CMP +1 - SANB.CMP +2 - MIAMI.CMP + +flags are: +* screen dimensions (have to specify neither or both) +-w width +-h height + +* log verbosity +-l level (0 default; everything, 1 warn + error, 2 only error) + +Using "-l 1" will make it easier to see actual error messages; +sometimes there is a lot of noise on 'info' level (0). + +* style-file +-c 0 (default; uses .GRY 8bit styles) +-c 1 (uses .G24 24bit styles) + +* other map & graphics files (have to specify neither or both) +-m map_filename -g style_filename + +This is for loading non-GTA1 data; like GTA London. + +keys: + +ESC, cursor-keys, + and - do what you might except them to do; +furthermore: + x : pseudo-3d view + z : top-down view + . : decrease visible range + , : increase visible range + t : display entire city (at a crawl) + f : toggle fullscreen/windowed + PRINT : save 'screenshot.bmp' in current directory + p : dump coords (in lua syntax) to stdout + i,j,k,l : move player-char + F2 : toggle drawing of sprite bounding-boxes + F3 : toggle marking of sprite tex-border-boxes + F4 : toggle free-move vs. follow-player + F5 : toggle drawing of road heading arrows + F6 : city map mode (ESC to exit, +, -, cursor keys) + 1 - 8 : choose a specific player-sprite animation + 9, 0 : set the player-sprite to a specific frame + +in 3d view: + F1 : demo-flight over the city + w : forward + s : backward + space : stop + r : toggle rotate cam (when not moving) + g : toggle ~gravity~ affect on 3d-cam + +You can move the view with the mouse; when you switch +to 3d and the screen is black: move the mouse down. + +== luaviewer: viewer + Lua (optional target) == + +Also needs Lua (only 5.1 tried) in path; run: 'make luaviewer' +then start it like with: + ./luaviewer -s scripts/demo1.lua [0-2] + +See the scripts for a little documentation. + += License & Credits = + +This is _not_ free software, as it must not be used for commercial +applications. [ http://www.opensource.org/docs/definition.php ] +Please read license.txt for the details. + +Author: tok@openlinux.org.uk + +Special thanks to: + +Jernej 'Delfi' L. (jernejcoder@gmail.com) for providing the vertex-data +and invaluable help concerning several of the file formats. diff --git a/scripts/demo1.lua b/scripts/demo1.lua new file mode 100644 index 0000000..54f3944 --- /dev/null +++ b/scripts/demo1.lua @@ -0,0 +1,26 @@ +pos = { + x = 214, + y = 15, -- this is Z (up) in GTA + z = 51 +} + +-- position the camera +city_view:setCamPosition(pos.x, pos.y, pos.z) +-- set pseudo-3d view +city_view:setTopDownView(true) +-- increase the visible range; default is 15 +--city_view:setVisibleRange(20) +--screen.setFullscreen(true) +--spritecache.setScale2x(false) + + +tick = 0 + +-- If a global function 'game_tick' exists, it will be called +-- every 100 ms, but it is pretty pointless at this time. +function game_tick() + if tick == 1 then + screen.makeScreenShot("test.bmp") + end + tick = tick + 1 +end diff --git a/scripts/demo2.lua b/scripts/demo2.lua new file mode 100644 index 0000000..7c6845f --- /dev/null +++ b/scripts/demo2.lua @@ -0,0 +1,59 @@ +screen.setFullscreen(true) +camera.setCenter(61.4261, 7.56265, 2.2242) +camera.setEye(61.4185, 8.72266, 0.810034) +camera.setUp(0, 1, 0) +city_view:setVisibleRange(30) +city_view:setTopDownView( false ) + +ticks = 0 +m_step = 1 + +function step() + if m_step == 1 then +camera.setCenter(61.8193, 5.56514, 14.7887) +camera.setEye(61.6693, 6.00516, 16.1949) + elseif m_step == 2 then +camera.setCenter(17.8087, 16.7441, 17.0151) +camera.setEye(16.7382, 17.3441, 16.0909) + elseif m_step == 3 then +camera.setCenter(22.4076, 14.1664, 20.9857) +camera.setEye(21.3371, 14.7664, 20.0615) + elseif m_step == 4 then +camera.setCenter(15.9171, 7.45477, 197.031) +camera.setEye(17.2552, 8.13476, 196.573) + elseif m_step == 5 then +camera.setCenter(4.66008, 5.85241, 198.842) +camera.setEye(3.24865, 6.53241, 198.754) + elseif m_step == 6 then +camera.setCenter(4.08809, 7.71514, 200.964) +camera.setEye(4.36918, 10.0751, 202.35) + elseif m_step == 7 then +camera.setCenter(91.2481, 6.10855, 230.936) +camera.setEye(91.2458, 6.86855, 229.522) + elseif m_step == 8 then +camera.setCenter(225.143, 6.53355, 173.986) +camera.setEye(226.363, 7.29354, 174.702) + elseif m_step == 9 then +camera.setCenter(249.057, 7.64056, 143.877) +camera.setEye(248.829, 8.40055, 142.481) + elseif m_step == 10 then +camera.setCenter(234.41, 5.14698, 115.074) +camera.setEye(235.747, 5.50698, 115.533) + elseif m_step == 11 then +camera.setCenter(149.634, 8.1291, 14.9788) +camera.setEye(151.044, 8.6491, 14.8622) + end + m_step = m_step + 1 +end + +--city_view:setCamPosition(143, 9, 23) +--city_view:setVisibleRange(15) +--city_view:setTopDownView( true ) + +function game_tick() + ticks = ticks + 1 + if ticks > 50 then + step() + ticks = 0 + end +end diff --git a/scripts/demo3.lua b/scripts/demo3.lua new file mode 100644 index 0000000..78fe843 --- /dev/null +++ b/scripts/demo3.lua @@ -0,0 +1,31 @@ +pos = { + x = 214, + y = 13, -- this is Z (up) in GTA + z = 51 +} + + +screen.setFullscreen(true) +city_view:setTopDownView(false) +-- position the camera +camera.setEye(pos.x, pos.y, pos.z) +camera.setCenter(pos.x, 0, pos.z) +camera.setUp(0, 0, -1) +-- set pseudo-3d view +-- increase the visible range; default is 15 +city_view:setVisibleRange(20) + +-- If a global function 'game_tick' exists, it will be called +-- every 100 ms, but it is pretty pointless at this time. + +tick_count = 0 +next_move_tick = 30 + +function game_tick() + if tick_count == next_move_tick then + x, y, z = camera.getEye() + camera.interpolateToPosition(x+math.random()*40-20, y, z + math.random()*40-20, 2000) + next_move_tick = next_move_tick + 50 + end + tick_count = tick_count + 1 +end diff --git a/slope1_data.h b/slope1_data.h new file mode 100644 index 0000000..712f920 --- /dev/null +++ b/slope1_data.h @@ -0,0 +1,1440 @@ +{ // slope: 0 + { // LID + { 0.00, 1.00, 1 }, + { 1.00, 1.00, 1 }, + { 1.00, 1.00, 0 }, + { 0.00, 1.00, 0 } + }, + { // NORTH + { 1.00, 1.00, 1 }, + { 0.00, 1.00, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 1.00, 0 }, + { 1.00, 1.00, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 1.00, 1 }, + { 0.00, 1.00, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 1.00, 0 }, + { 1.00, 1.00, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +}, +{ // slope: 3 + { // LID + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.50, 0 }, + { 0.00, 0.50, 0 } + }, + { // NORTH + { 1.00, 0.00, 1 }, + { 0.00, 0.00, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 0.50, 0 }, + { 1.00, 0.50, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 0.00, 1 }, + { 0.00, 0.50, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 0.50, 0 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +}, +{ // slope: 4 + { // LID + { 0.00, 0.50, 1 }, + { 1.00, 0.50, 1 }, + { 1.00, 1.00, 0 }, + { 0.00, 1.00, 0 } + }, + { // NORTH + { 1.00, 0.50, 1 }, + { 0.00, 0.50, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 1.00, 0 }, + { 1.00, 1.00, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 0.50, 1 }, + { 0.00, 1.00, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 1.00, 0 }, + { 1.00, 0.50, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +}, +{ // slope: 1 + { // LID + { 0.00, 0.50, 1 }, + { 1.00, 0.50, 1 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // NORTH + { 1.00, 0.50, 1 }, + { 0.00, 0.50, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 0.00, 0 }, + { 1.00, 0.00, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 0.50, 1 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 0.00, 0 }, + { 1.00, 0.50, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +}, +{ // slope: 2 + { // LID + { 0.00, 1.00, 1 }, + { 1.00, 1.00, 1 }, + { 1.00, 0.50, 0 }, + { 0.00, 0.50, 0 } + }, + { // NORTH + { 1.00, 1.00, 1 }, + { 0.00, 1.00, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 0.50, 0 }, + { 1.00, 0.50, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 1.00, 1 }, + { 0.00, 0.50, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 0.50, 0 }, + { 1.00, 1.00, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +}, +{ // slope: 5 + { // LID + { 0.00, 0.50, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.50, 0 } + }, + { // NORTH + { 1.00, 0.00, 1 }, + { 0.00, 0.50, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 0.50, 0 }, + { 1.00, 0.00, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 0.50, 1 }, + { 0.00, 0.50, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 0.00, 0 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +}, +{ // slope: 6 + { // LID + { 0.00, 1.00, 1 }, + { 1.00, 0.50, 1 }, + { 1.00, 0.50, 0 }, + { 0.00, 1.00, 0 } + }, + { // NORTH + { 1.00, 0.50, 1 }, + { 0.00, 1.00, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 1.00, 0 }, + { 1.00, 0.50, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 1.00, 1 }, + { 0.00, 1.00, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 0.50, 0 }, + { 1.00, 0.50, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +}, +{ // slope: 7 + { // LID + { 0.00, 0.00, 1 }, + { 1.00, 0.50, 1 }, + { 1.00, 0.50, 0 }, + { 0.00, 0.00, 0 } + }, + { // NORTH + { 1.00, 0.50, 1 }, + { 0.00, 0.00, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 0.00, 0 }, + { 1.00, 0.50, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 0.00, 1 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 0.50, 0 }, + { 1.00, 0.50, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +}, +{ // slope: 8 + { // LID + { 0.00, 0.50, 1 }, + { 1.00, 1.00, 1 }, + { 1.00, 1.00, 0 }, + { 0.00, 0.50, 0 } + }, + { // NORTH + { 1.00, 1.00, 1 }, + { 0.00, 0.50, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 0.50, 0 }, + { 1.00, 1.00, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 0.50, 1 }, + { 0.00, 0.50, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 1.00, 0 }, + { 1.00, 1.00, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +}, +{ // slope: 17 + { // LID + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.13, 0 }, + { 0.00, 0.13, 0 } + }, + { // NORTH + { 1.00, 0.00, 1 }, + { 0.00, 0.00, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 0.13, 0 }, + { 1.00, 0.13, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 0.00, 1 }, + { 0.00, 0.13, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 0.13, 0 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +}, +{ // slope: 18 + { // LID + { 0.00, 0.13, 1 }, + { 1.00, 0.13, 1 }, + { 1.00, 0.25, 0 }, + { 0.00, 0.25, 0 } + }, + { // NORTH + { 1.00, 0.13, 1 }, + { 0.00, 0.13, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 0.25, 0 }, + { 1.00, 0.25, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 0.13, 1 }, + { 0.00, 0.25, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 0.25, 0 }, + { 1.00, 0.13, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +}, +{ // slope: 19 + { // LID + { 0.00, 0.25, 1 }, + { 1.00, 0.25, 1 }, + { 1.00, 0.38, 0 }, + { 0.00, 0.38, 0 } + }, + { // NORTH + { 1.00, 0.25, 1 }, + { 0.00, 0.25, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 0.38, 0 }, + { 1.00, 0.38, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 0.25, 1 }, + { 0.00, 0.38, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 0.38, 0 }, + { 1.00, 0.25, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +}, +{ // slope: 20 + { // LID + { 0.00, 0.38, 1 }, + { 1.00, 0.38, 1 }, + { 1.00, 0.50, 0 }, + { 0.00, 0.50, 0 } + }, + { // NORTH + { 1.00, 0.38, 1 }, + { 0.00, 0.38, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 0.50, 0 }, + { 1.00, 0.50, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 0.38, 1 }, + { 0.00, 0.50, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 0.50, 0 }, + { 1.00, 0.38, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +}, +{ // slope: 21 + { // LID + { 0.00, 0.50, 1 }, + { 1.00, 0.50, 1 }, + { 1.00, 0.63, 0 }, + { 0.00, 0.63, 0 } + }, + { // NORTH + { 1.00, 0.50, 1 }, + { 0.00, 0.50, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 0.63, 0 }, + { 1.00, 0.63, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 0.50, 1 }, + { 0.00, 0.63, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 0.63, 0 }, + { 1.00, 0.50, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +}, +{ // slope: 22 + { // LID + { 0.00, 0.63, 1 }, + { 1.00, 0.63, 1 }, + { 1.00, 0.75, 0 }, + { 0.00, 0.75, 0 } + }, + { // NORTH + { 1.00, 0.63, 1 }, + { 0.00, 0.63, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 0.75, 0 }, + { 1.00, 0.75, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 0.63, 1 }, + { 0.00, 0.75, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 0.75, 0 }, + { 1.00, 0.63, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +}, +{ // slope: 23 + { // LID + { 0.00, 0.75, 1 }, + { 1.00, 0.75, 1 }, + { 1.00, 0.88, 0 }, + { 0.00, 0.88, 0 } + }, + { // NORTH + { 1.00, 0.75, 1 }, + { 0.00, 0.75, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 0.88, 0 }, + { 1.00, 0.88, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 0.75, 1 }, + { 0.00, 0.88, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 0.88, 0 }, + { 1.00, 0.75, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +}, +{ // slope: 24 + { // LID + { 0.00, 0.88, 1 }, + { 1.00, 0.88, 1 }, + { 1.00, 1.00, 0 }, + { 0.00, 1.00, 0 } + }, + { // NORTH + { 1.00, 0.88, 1 }, + { 0.00, 0.88, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 1.00, 0 }, + { 1.00, 1.00, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 0.88, 1 }, + { 0.00, 1.00, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 1.00, 0 }, + { 1.00, 0.88, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +}, +{ // slope: 9 + { // LID + { 0.00, 0.13, 1 }, + { 1.00, 0.13, 1 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // NORTH + { 1.00, 0.13, 1 }, + { 0.00, 0.13, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 0.00, 0 }, + { 1.00, 0.00, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 0.13, 1 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 0.00, 0 }, + { 1.00, 0.13, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +}, +{ // slope: 10 + { // LID + { 0.00, 0.25, 1 }, + { 1.00, 0.25, 1 }, + { 1.00, 0.13, 0 }, + { 0.00, 0.13, 0 } + }, + { // NORTH + { 1.00, 0.25, 1 }, + { 0.00, 0.25, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 0.13, 0 }, + { 1.00, 0.13, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 0.25, 1 }, + { 0.00, 0.13, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 0.13, 0 }, + { 1.00, 0.25, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +}, +{ // slope: 11 + { // LID + { 0.00, 0.38, 1 }, + { 1.00, 0.38, 1 }, + { 1.00, 0.25, 0 }, + { 0.00, 0.25, 0 } + }, + { // NORTH + { 1.00, 0.38, 1 }, + { 0.00, 0.38, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 0.25, 0 }, + { 1.00, 0.25, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 0.38, 1 }, + { 0.00, 0.25, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 0.25, 0 }, + { 1.00, 0.38, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +}, +{ // slope: 12 + { // LID + { 0.00, 0.50, 1 }, + { 1.00, 0.50, 1 }, + { 1.00, 0.38, 0 }, + { 0.00, 0.38, 0 } + }, + { // NORTH + { 1.00, 0.50, 1 }, + { 0.00, 0.50, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 0.38, 0 }, + { 1.00, 0.38, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 0.50, 1 }, + { 0.00, 0.38, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 0.38, 0 }, + { 1.00, 0.50, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +}, +{ // slope: 13 + { // LID + { 0.00, 0.63, 1 }, + { 1.00, 0.63, 1 }, + { 1.00, 0.50, 0 }, + { 0.00, 0.50, 0 } + }, + { // NORTH + { 1.00, 0.63, 1 }, + { 0.00, 0.63, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 0.50, 0 }, + { 1.00, 0.50, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 0.63, 1 }, + { 0.00, 0.50, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 0.50, 0 }, + { 1.00, 0.63, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +}, +{ // slope: 14 + { // LID + { 0.00, 0.75, 1 }, + { 1.00, 0.75, 1 }, + { 1.00, 0.63, 0 }, + { 0.00, 0.63, 0 } + }, + { // NORTH + { 1.00, 0.75, 1 }, + { 0.00, 0.75, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 0.63, 0 }, + { 1.00, 0.63, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 0.75, 1 }, + { 0.00, 0.63, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 0.63, 0 }, + { 1.00, 0.75, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +}, +{ // slope: 15 + { // LID + { 0.00, 0.88, 1 }, + { 1.00, 0.88, 1 }, + { 1.00, 0.75, 0 }, + { 0.00, 0.75, 0 } + }, + { // NORTH + { 1.00, 0.88, 1 }, + { 0.00, 0.88, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 0.75, 0 }, + { 1.00, 0.75, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 0.88, 1 }, + { 0.00, 0.75, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 0.75, 0 }, + { 1.00, 0.88, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +}, +{ // slope: 16 + { // LID + { 0.00, 1.00, 1 }, + { 1.00, 1.00, 1 }, + { 1.00, 0.88, 0 }, + { 0.00, 0.88, 0 } + }, + { // NORTH + { 1.00, 1.00, 1 }, + { 0.00, 1.00, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 0.88, 0 }, + { 1.00, 0.88, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 1.00, 1 }, + { 0.00, 0.88, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 0.88, 0 }, + { 1.00, 1.00, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +}, +{ // slope: 25 + { // LID + { 0.00, 0.13, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.13, 0 } + }, + { // NORTH + { 1.00, 0.00, 1 }, + { 0.00, 0.13, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 0.13, 0 }, + { 1.00, 0.00, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 0.13, 1 }, + { 0.00, 0.13, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 0.00, 0 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +}, +{ // slope: 26 + { // LID + { 0.00, 0.25, 1 }, + { 1.00, 0.13, 1 }, + { 1.00, 0.13, 0 }, + { 0.00, 0.25, 0 } + }, + { // NORTH + { 1.00, 0.13, 1 }, + { 0.00, 0.25, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 0.25, 0 }, + { 1.00, 0.13, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 0.25, 1 }, + { 0.00, 0.25, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 0.13, 0 }, + { 1.00, 0.13, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +}, +{ // slope: 27 + { // LID + { 0.00, 0.38, 1 }, + { 1.00, 0.25, 1 }, + { 1.00, 0.25, 0 }, + { 0.00, 0.38, 0 } + }, + { // NORTH + { 1.00, 0.25, 1 }, + { 0.00, 0.38, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 0.38, 0 }, + { 1.00, 0.25, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 0.38, 1 }, + { 0.00, 0.38, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 0.25, 0 }, + { 1.00, 0.25, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +}, +{ // slope: 28 + { // LID + { 0.00, 0.50, 1 }, + { 1.00, 0.38, 1 }, + { 1.00, 0.38, 0 }, + { 0.00, 0.50, 0 } + }, + { // NORTH + { 1.00, 0.38, 1 }, + { 0.00, 0.50, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 0.50, 0 }, + { 1.00, 0.38, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 0.50, 1 }, + { 0.00, 0.50, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 0.38, 0 }, + { 1.00, 0.38, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +}, +{ // slope: 29 + { // LID + { 0.00, 0.63, 1 }, + { 1.00, 0.50, 1 }, + { 1.00, 0.50, 0 }, + { 0.00, 0.63, 0 } + }, + { // NORTH + { 1.00, 0.50, 1 }, + { 0.00, 0.63, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 0.63, 0 }, + { 1.00, 0.50, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 0.63, 1 }, + { 0.00, 0.63, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 0.50, 0 }, + { 1.00, 0.50, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +}, +{ // slope: 30 + { // LID + { 0.00, 0.75, 1 }, + { 1.00, 0.63, 1 }, + { 1.00, 0.63, 0 }, + { 0.00, 0.75, 0 } + }, + { // NORTH + { 1.00, 0.63, 1 }, + { 0.00, 0.75, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 0.75, 0 }, + { 1.00, 0.63, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 0.75, 1 }, + { 0.00, 0.75, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 0.63, 0 }, + { 1.00, 0.63, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +}, +{ // slope: 31 + { // LID + { 0.00, 0.88, 1 }, + { 1.00, 0.75, 1 }, + { 1.00, 0.75, 0 }, + { 0.00, 0.88, 0 } + }, + { // NORTH + { 1.00, 0.75, 1 }, + { 0.00, 0.88, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 0.88, 0 }, + { 1.00, 0.75, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 0.88, 1 }, + { 0.00, 0.88, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 0.75, 0 }, + { 1.00, 0.75, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +}, +{ // slope: 32 + { // LID + { 0.00, 1.00, 1 }, + { 1.00, 0.88, 1 }, + { 1.00, 0.88, 0 }, + { 0.00, 1.00, 0 } + }, + { // NORTH + { 1.00, 0.88, 1 }, + { 0.00, 1.00, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 1.00, 0 }, + { 1.00, 0.88, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 1.00, 1 }, + { 0.00, 1.00, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 0.88, 0 }, + { 1.00, 0.88, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +}, +{ // slope: 33 + { // LID + { 0.00, 0.00, 1 }, + { 1.00, 0.13, 1 }, + { 1.00, 0.13, 0 }, + { 0.00, 0.00, 0 } + }, + { // NORTH + { 1.00, 0.13, 1 }, + { 0.00, 0.00, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 0.00, 0 }, + { 1.00, 0.13, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 0.00, 1 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 0.13, 0 }, + { 1.00, 0.13, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +}, +{ // slope: 34 + { // LID + { 0.00, 0.13, 1 }, + { 1.00, 0.25, 1 }, + { 1.00, 0.25, 0 }, + { 0.00, 0.13, 0 } + }, + { // NORTH + { 1.00, 0.25, 1 }, + { 0.00, 0.13, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 0.13, 0 }, + { 1.00, 0.25, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 0.13, 1 }, + { 0.00, 0.13, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 0.25, 0 }, + { 1.00, 0.25, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +}, +{ // slope: 35 + { // LID + { 0.00, 0.25, 1 }, + { 1.00, 0.38, 1 }, + { 1.00, 0.38, 0 }, + { 0.00, 0.25, 0 } + }, + { // NORTH + { 1.00, 0.38, 1 }, + { 0.00, 0.25, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 0.25, 0 }, + { 1.00, 0.38, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 0.25, 1 }, + { 0.00, 0.25, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 0.38, 0 }, + { 1.00, 0.38, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +}, +{ // slope: 36 + { // LID + { 0.00, 0.38, 1 }, + { 1.00, 0.50, 1 }, + { 1.00, 0.50, 0 }, + { 0.00, 0.38, 0 } + }, + { // NORTH + { 1.00, 0.50, 1 }, + { 0.00, 0.38, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 0.38, 0 }, + { 1.00, 0.50, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 0.38, 1 }, + { 0.00, 0.38, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 0.50, 0 }, + { 1.00, 0.50, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +}, +{ // slope: 37 + { // LID + { 0.00, 0.50, 1 }, + { 1.00, 0.63, 1 }, + { 1.00, 0.63, 0 }, + { 0.00, 0.50, 0 } + }, + { // NORTH + { 1.00, 0.63, 1 }, + { 0.00, 0.50, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 0.50, 0 }, + { 1.00, 0.63, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 0.50, 1 }, + { 0.00, 0.50, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 0.63, 0 }, + { 1.00, 0.63, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +}, +{ // slope: 38 + { // LID + { 0.00, 0.63, 1 }, + { 1.00, 0.75, 1 }, + { 1.00, 0.75, 0 }, + { 0.00, 0.63, 0 } + }, + { // NORTH + { 1.00, 0.75, 1 }, + { 0.00, 0.63, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 0.63, 0 }, + { 1.00, 0.75, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 0.63, 1 }, + { 0.00, 0.63, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 0.75, 0 }, + { 1.00, 0.75, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +}, +{ // slope: 39 + { // LID + { 0.00, 0.75, 1 }, + { 1.00, 0.88, 1 }, + { 1.00, 0.88, 0 }, + { 0.00, 0.75, 0 } + }, + { // NORTH + { 1.00, 0.88, 1 }, + { 0.00, 0.75, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 0.75, 0 }, + { 1.00, 0.88, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 0.75, 1 }, + { 0.00, 0.75, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 0.88, 0 }, + { 1.00, 0.88, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +}, +{ // slope: 40 + { // LID + { 0.00, 0.88, 1 }, + { 1.00, 1.00, 1 }, + { 1.00, 1.00, 0 }, + { 0.00, 0.88, 0 } + }, + { // NORTH + { 1.00, 1.00, 1 }, + { 0.00, 0.88, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 0.88, 0 }, + { 1.00, 1.00, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 0.88, 1 }, + { 0.00, 0.88, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 1.00, 0 }, + { 1.00, 1.00, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +}, +{ // slope: 42 + { // LID + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 1.00, 0 }, + { 0.00, 1.00, 0 } + }, + { // NORTH + { 1.00, 1.00, 1 }, + { 0.00, 1.00, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 1.00, 0 }, + { 1.00, 1.00, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 0.00, 1 }, + { 0.00, 1.00, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 1.00, 0 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +}, +{ // slope: 41 + { // LID + { 0.00, 1.00, 1 }, + { 1.00, 1.00, 1 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // NORTH + { 1.00, 1.00, 1 }, + { 0.00, 1.00, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 1.00, 0 }, + { 1.00, 1.00, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 1.00, 1 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 0.00, 0 }, + { 1.00, 1.00, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +}, +{ // slope: 43 + { // LID + { 0.00, 1.00, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 }, + { 0.00, 1.00, 0 } + }, + { // NORTH + { 1.00, 0.00, 1 }, + { 0.00, 1.00, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 1.00, 0 }, + { 1.00, 0.00, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 1.00, 1 }, + { 0.00, 1.00, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 1.00, 0 }, + { 1.00, 1.00, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +}, +{ // slope: 44 + { // LID + { 0.00, 0.00, 1 }, + { 1.00, 1.00, 1 }, + { 1.00, 1.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // NORTH + { 1.00, 1.00, 1 }, + { 0.00, 0.00, 1 }, + { 0.00, 0.00, 1 }, + { 1.00, 0.00, 1 } + }, + { // SOUTH + { 0.00, 0.00, 0 }, + { 1.00, 1.00, 0 }, + { 1.00, 0.00, 0 }, + { 0.00, 0.00, 0 } + }, + { // WEST + { 0.00, 1.00, 1 }, + { 0.00, 1.00, 0 }, + { 0.00, 0.00, 0 }, + { 0.00, 0.00, 1 } + }, + { // EAST + { 1.00, 1.00, 0 }, + { 1.00, 1.00, 1 }, + { 1.00, 0.00, 1 }, + { 1.00, 0.00, 0 } + } +} diff --git a/slope1_tcoords.h b/slope1_tcoords.h new file mode 100644 index 0000000..b4de454 --- /dev/null +++ b/slope1_tcoords.h @@ -0,0 +1,1170 @@ +{ // slope: 0 + { // north + { 1.00, 0.00 }, + { 0.00, 0.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 0.00 }, + { 0.00, 0.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 0.00 }, + { 0.00, 0.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 0.00 }, + { 0.00, 0.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +}, +{ // slope: 1 + { // north + { 1.00, 1.00 }, + { 0.00, 1.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 0.50 }, + { 0.00, 0.50 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 1.00 }, + { 0.00, 0.50 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 0.50 }, + { 0.00, 1.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +}, +{ // slope: 2 + { // north + { 1.00, 0.50 }, + { 0.00, 0.50 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 0.00 }, + { 0.00, 0.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 0.50 }, + { 0.00, 0.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 0.00 }, + { 0.00, 0.50 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +}, +{ // slope: 3 + { // north + { 1.00, 0.50 }, + { 0.00, 0.50 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 1.00 }, + { 0.00, 1.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 0.50 }, + { 0.00, 1.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 1.00 }, + { 0.00, 0.50 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +}, +{ // slope: 4 + { // north + { 1.00, 0.00 }, + { 0.00, 0.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 0.50 }, + { 0.00, 0.50 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 0.00 }, + { 0.00, 0.50 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 0.50 }, + { 0.00, 0.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +}, +{ // slope: 5 + { // north + { 1.00, 1.00 }, + { 0.00, 0.50 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 0.50 }, + { 0.00, 1.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 0.50 }, + { 0.00, 0.50 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 1.00 }, + { 0.00, 1.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +}, +{ // slope: 6 + { // north + { 1.00, 0.50 }, + { 0.00, 0.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 0.00 }, + { 0.00, 0.50 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 0.00 }, + { 0.00, 0.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 0.50 }, + { 0.00, 0.50 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +}, +{ // slope: 7 + { // north + { 1.00, 0.50 }, + { 0.00, 1.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 1.00 }, + { 0.00, 0.50 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 1.00 }, + { 0.00, 1.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 0.50 }, + { 0.00, 0.50 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +}, +{ // slope: 8 + { // north + { 1.00, 0.00 }, + { 0.00, 0.50 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 0.50 }, + { 0.00, 0.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 0.50 }, + { 0.00, 0.50 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 0.00 }, + { 0.00, 0.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +}, +{ // slope: 9 + { // north + { 1.00, 1.00 }, + { 0.00, 1.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 0.87 }, + { 0.00, 0.87 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 1.00 }, + { 0.00, 0.87 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 0.87 }, + { 0.00, 1.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +}, +{ // slope: 10 + { // north + { 1.00, 0.87 }, + { 0.00, 0.87 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 0.75 }, + { 0.00, 0.75 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 0.87 }, + { 0.00, 0.75 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 0.75 }, + { 0.00, 0.87 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +}, +{ // slope: 11 + { // north + { 1.00, 0.75 }, + { 0.00, 0.75 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 0.62 }, + { 0.00, 0.62 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 0.75 }, + { 0.00, 0.62 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 0.62 }, + { 0.00, 0.75 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +}, +{ // slope: 12 + { // north + { 1.00, 0.62 }, + { 0.00, 0.62 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 0.50 }, + { 0.00, 0.50 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 0.62 }, + { 0.00, 0.50 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 0.50 }, + { 0.00, 0.62 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +}, +{ // slope: 13 + { // north + { 1.00, 0.50 }, + { 0.00, 0.50 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 0.37 }, + { 0.00, 0.37 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 0.50 }, + { 0.00, 0.37 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 0.37 }, + { 0.00, 0.50 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +}, +{ // slope: 14 + { // north + { 1.00, 0.37 }, + { 0.00, 0.37 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 0.25 }, + { 0.00, 0.25 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 0.37 }, + { 0.00, 0.25 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 0.25 }, + { 0.00, 0.37 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +}, +{ // slope: 15 + { // north + { 1.00, 0.25 }, + { 0.00, 0.25 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 0.12 }, + { 0.00, 0.12 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 0.25 }, + { 0.00, 0.12 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 0.12 }, + { 0.00, 0.25 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +}, +{ // slope: 16 + { // north + { 1.00, 0.12 }, + { 0.00, 0.12 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 0.00 }, + { 0.00, 0.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 0.12 }, + { 0.00, 0.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 0.00 }, + { 0.00, 0.12 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +}, +{ // slope: 17 + { // north + { 1.00, 0.87 }, + { 0.00, 0.87 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 1.00 }, + { 0.00, 1.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 0.87 }, + { 0.00, 1.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 1.00 }, + { 0.00, 0.87 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +}, +{ // slope: 18 + { // north + { 1.00, 0.75 }, + { 0.00, 0.75 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 0.87 }, + { 0.00, 0.87 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 0.75 }, + { 0.00, 0.87 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 0.87 }, + { 0.00, 0.75 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +}, +{ // slope: 19 + { // north + { 1.00, 0.62 }, + { 0.00, 0.62 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 0.75 }, + { 0.00, 0.75 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 0.62 }, + { 0.00, 0.75 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 0.75 }, + { 0.00, 0.62 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +}, +{ // slope: 20 + { // north + { 1.00, 0.50 }, + { 0.00, 0.50 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 0.62 }, + { 0.00, 0.62 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 0.50 }, + { 0.00, 0.62 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 0.62 }, + { 0.00, 0.50 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +}, +{ // slope: 21 + { // north + { 1.00, 0.37 }, + { 0.00, 0.37 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 0.50 }, + { 0.00, 0.50 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 0.37 }, + { 0.00, 0.50 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 0.50 }, + { 0.00, 0.37 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +}, +{ // slope: 22 + { // north + { 1.00, 0.25 }, + { 0.00, 0.25 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 0.37 }, + { 0.00, 0.37 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 0.25 }, + { 0.00, 0.37 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 0.37 }, + { 0.00, 0.25 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +}, +{ // slope: 23 + { // north + { 1.00, 0.12 }, + { 0.00, 0.12 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 0.25 }, + { 0.00, 0.25 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 0.12 }, + { 0.00, 0.25 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 0.25 }, + { 0.00, 0.12 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +}, +{ // slope: 24 + { // north + { 1.00, 0.00 }, + { 0.00, 0.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 0.12 }, + { 0.00, 0.12 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 0.00 }, + { 0.00, 0.12 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 0.12 }, + { 0.00, 0.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +}, +{ // slope: 25 + { // north + { 1.00, 1.00 }, + { 0.00, 0.87 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 0.87 }, + { 0.00, 1.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 0.87 }, + { 0.00, 0.87 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 1.00 }, + { 0.00, 1.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +}, +{ // slope: 26 + { // north + { 1.00, 0.87 }, + { 0.00, 0.75 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 0.75 }, + { 0.00, 0.87 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 0.75 }, + { 0.00, 0.75 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 0.87 }, + { 0.00, 0.87 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +}, +{ // slope: 27 + { // north + { 1.00, 0.75 }, + { 0.00, 0.62 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 0.62 }, + { 0.00, 0.75 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 0.62 }, + { 0.00, 0.62 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 0.75 }, + { 0.00, 0.75 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +}, +{ // slope: 28 + { // north + { 1.00, 0.62 }, + { 0.00, 0.50 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 0.50 }, + { 0.00, 0.62 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 0.50 }, + { 0.00, 0.50 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 0.62 }, + { 0.00, 0.62 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +}, +{ // slope: 29 + { // north + { 1.00, 0.50 }, + { 0.00, 0.37 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 0.37 }, + { 0.00, 0.50 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 0.37 }, + { 0.00, 0.37 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 0.50 }, + { 0.00, 0.50 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +}, +{ // slope: 30 + { // north + { 1.00, 0.37 }, + { 0.00, 0.25 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 0.25 }, + { 0.00, 0.37 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 0.25 }, + { 0.00, 0.25 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 0.37 }, + { 0.00, 0.37 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +}, +{ // slope: 31 + { // north + { 1.00, 0.25 }, + { 0.00, 0.12 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 0.12 }, + { 0.00, 0.25 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 0.12 }, + { 0.00, 0.12 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 0.25 }, + { 0.00, 0.25 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +}, +{ // slope: 32 + { // north + { 1.00, 0.12 }, + { 0.00, 0.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 0.00 }, + { 0.00, 0.12 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 0.00 }, + { 0.00, 0.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 0.12 }, + { 0.00, 0.12 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +}, +{ // slope: 33 + { // north + { 1.00, 0.87 }, + { 0.00, 1.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 1.00 }, + { 0.00, 0.87 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 1.00 }, + { 0.00, 1.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 0.87 }, + { 0.00, 0.87 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +}, +{ // slope: 34 + { // north + { 1.00, 0.75 }, + { 0.00, 0.87 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 0.87 }, + { 0.00, 0.75 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 0.87 }, + { 0.00, 0.87 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 0.75 }, + { 0.00, 0.75 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +}, +{ // slope: 35 + { // north + { 1.00, 0.62 }, + { 0.00, 0.75 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 0.75 }, + { 0.00, 0.62 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 0.75 }, + { 0.00, 0.75 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 0.62 }, + { 0.00, 0.62 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +}, +{ // slope: 36 + { // north + { 1.00, 0.50 }, + { 0.00, 0.62 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 0.62 }, + { 0.00, 0.50 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 0.62 }, + { 0.00, 0.62 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 0.50 }, + { 0.00, 0.50 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +}, +{ // slope: 37 + { // north + { 1.00, 0.37 }, + { 0.00, 0.50 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 0.50 }, + { 0.00, 0.37 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 0.50 }, + { 0.00, 0.50 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 0.37 }, + { 0.00, 0.37 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +}, +{ // slope: 38 + { // north + { 1.00, 0.25 }, + { 0.00, 0.37 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 0.37 }, + { 0.00, 0.25 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 0.37 }, + { 0.00, 0.37 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 0.25 }, + { 0.00, 0.25 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +}, +{ // slope: 39 + { // north + { 1.00, 0.12 }, + { 0.00, 0.25 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 0.25 }, + { 0.00, 0.12 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 0.25 }, + { 0.00, 0.25 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 0.12 }, + { 0.00, 0.12 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +}, +{ // slope: 40 + { // north + { 1.00, 0.00 }, + { 0.00, 0.12 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 0.12 }, + { 0.00, 0.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 0.12 }, + { 0.00, 0.12 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 0.00 }, + { 0.00, 0.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +}, +{ // slope: 41 + { // north + { 1.00, 0.00 }, + { 0.00, 0.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 0.00 }, + { 0.00, 0.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 1.00 }, + { 0.00, 0.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 0.00 }, + { 0.00, 1.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +}, +{ // slope: 42 + { // north + { 1.00, 0.00 }, + { 0.00, 0.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 0.00 }, + { 0.00, 0.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 0.00 }, + { 0.00, 1.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 1.00 }, + { 0.00, 0.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +}, +{ // slope: 43 + { // north + { 1.00, 1.00 }, + { 0.00, 0.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 0.00 }, + { 0.00, 1.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 0.00 }, + { 0.00, 0.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 0.00 }, + { 0.00, 0.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +}, +{ // slope: 44 + { // north + { 1.00, 0.00 }, + { 0.00, 1.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // south + { 1.00, 1.00 }, + { 0.00, 0.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // west + { 1.00, 0.00 }, + { 0.00, 0.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + }, + { // east + { 1.00, 0.00 }, + { 0.00, 0.00 }, + { 0.00, 1.00 }, + { 1.00, 1.00 } + } +} diff --git a/slope_height_func.cpp b/slope_height_func.cpp new file mode 100644 index 0000000..42a33b3 --- /dev/null +++ b/slope_height_func.cpp @@ -0,0 +1,101 @@ +#include +float slope_height_offset(unsigned char slope_type, float dx, float dz) { + assert((dx >= 0.0f) && (dx <= 1.0f)); + assert((dz >= 0.0f) && (dz <= 1.0f)); + switch(slope_type) { + case 0: + return 1.0f; + case 1: + return 0.500000f - dz * 0.500000f; + case 2: + return 1.000000f - dz * 0.500000f; + case 3: + return 0.000000f + dz * 0.500000f; + case 4: + return 0.500000f + dz * 0.500000f; + case 5: + return 0.500000f - dx * 0.500000f; + case 6: + return 1.000000f - dx * 0.500000f; + case 7: + return 0.000000f + dx * 0.500000f; + case 8: + return 0.500000f + dx * 0.500000f; + case 9: + return 0.130000f - dz * 0.130000f; + case 10: + return 0.250000f - dz * 0.120000f; + case 11: + return 0.380000f - dz * 0.130000f; + case 12: + return 0.500000f - dz * 0.120000f; + case 13: + return 0.630000f - dz * 0.130000f; + case 14: + return 0.750000f - dz * 0.120000f; + case 15: + return 0.880000f - dz * 0.130000f; + case 16: + return 1.000000f - dz * 0.120000f; + case 17: + return 0.000000f + dz * 0.130000f; + case 18: + return 0.130000f + dz * 0.120000f; + case 19: + return 0.250000f + dz * 0.130000f; + case 20: + return 0.380000f + dz * 0.120000f; + case 21: + return 0.500000f + dz * 0.130000f; + case 22: + return 0.630000f + dz * 0.120000f; + case 23: + return 0.750000f + dz * 0.130000f; + case 24: + return 0.880000f + dz * 0.120000f; + case 25: + return 0.130000f - dx * 0.130000f; + case 26: + return 0.250000f - dx * 0.120000f; + case 27: + return 0.380000f - dx * 0.130000f; + case 28: + return 0.500000f - dx * 0.120000f; + case 29: + return 0.630000f - dx * 0.130000f; + case 30: + return 0.750000f - dx * 0.120000f; + case 31: + return 0.880000f - dx * 0.130000f; + case 32: + return 1.000000f - dx * 0.120000f; + case 33: + return 0.000000f + dx * 0.130000f; + case 34: + return 0.130000f + dx * 0.120000f; + case 35: + return 0.250000f + dx * 0.130000f; + case 36: + return 0.380000f + dx * 0.120000f; + case 37: + return 0.500000f + dx * 0.130000f; + case 38: + return 0.630000f + dx * 0.120000f; + case 39: + return 0.750000f + dx * 0.130000f; + case 40: + return 0.880000f + dx * 0.120000f; + case 41: + return 1.000000f - dz * 1.000000f; + case 42: + return 0.000000f + dz * 1.000000f; + case 43: + return 1.000000f - dx * 1.000000f; + case 44: + return 0.000000f + dx * 1.000000f; + default: + break; + } + assert(0); + return 0.0f; // should never be reached +} diff --git a/sprite_anim_player.cpp b/sprite_anim_player.cpp new file mode 100644 index 0000000..f26d539 --- /dev/null +++ b/sprite_anim_player.cpp @@ -0,0 +1,269 @@ +#include +#include +#include +#include +#include "gl_screen.h" +#include "opengta.h" +#include "log.h" +#include "spritemanager.h" +#include "m_exceptions.h" +#include "gl_camera.h" +#include "dataholder.h" +#include "gl_font.h" + +extern int global_EC; +extern int global_Done; +std::string style_file("STYLE001.GRY"); + +OpenGTA::Pedestrian ped(Vector3D(0.5f, 0.5f, 0.5f), Vector3D(4, 0.01f, 4), 0xffffffff); +OpenGTA::SpriteObject::Animation pedAnim(0, 0); + +OpenGL::DrawableFont m_font; + +int frame_offset = 0; +int first_offset = 0; +int second_offset = 0; +int now_frame = 0; +bool play_anim = false; +int bbox_toggle = 0; +int texsprite_toggle = 0; + +int spr_type = (int)ped.sprType; + +void on_exit() { + SDL_Quit(); + PHYSFS_deinit(); +} + +void run_init() { + PHYSFS_init("mapview"); + PHYSFS_addToSearchPath(PHYSFS_getBaseDir(), 1); + PHYSFS_addToSearchPath("gtadata.zip", 1); + OpenGL::ScreenHolder::Instance().activate(640, 480); + SDL_EnableKeyRepeat( 100, SDL_DEFAULT_REPEAT_INTERVAL ); + + OpenGTA::StyleHolder::Instance().load(style_file); + + m_font.loadFont("STREET1.FON"); + m_font.setScale(2); +} + +const char* spr_type_name(int t) { + switch(t) { + case 0: + return "arrow"; + case 1: + return "digit"; + case 2: + return "boat"; + case 3: + return "box"; + case 4: + return "bus"; + case 5: + return "car"; + case 6: + return "object"; + case 7: + return "ped"; + case 8: + return "speedo"; + case 9: + return "tank"; + case 10: + return "tr light"; + case 11: + return "train"; + case 12: + return "tr door"; + case 13: + return "bike"; + case 14: + return "tram"; + case 15: + return "wbus"; + case 16: + return "wcar"; + case 17: + return "ex"; + case 18: + return "tumcar"; + case 19: + return "tumtruck"; + case 20: + return "ferry"; + } + return "???"; +} + +void drawScene(Uint32 ticks) { + GL_CHECKERROR; + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + OpenGL::ScreenHolder::Instance().set3DProjection(); + OpenGL::CameraHolder::Instance().update(ticks); + + if (play_anim) { + pedAnim.firstFrameOffset = now_frame; + } + OpenGTA::SpriteManagerHolder::Instance().drawPed(ped); + + OpenGL::ScreenHolder::Instance().setFlatProjection(); + + glPushMatrix(); + glTranslatef(10, 10, 0); + std::ostringstream sprite_info_str; + sprite_info_str << spr_type_name(spr_type) << " offset " << frame_offset; + m_font.drawString(sprite_info_str.str()); + glPopMatrix(); + + SDL_GL_SwapBuffers(); + GL_CHECKERROR; +} + +void handleKeyPress( SDL_keysym *keysym ) { + OpenGL::Camera & cam = OpenGL::CameraHolder::Instance(); + OpenGTA::GraphicsBase & style = OpenGTA::StyleHolder::Instance().get(); + bool update_anim = false; + switch ( keysym->sym ) { + case SDLK_ESCAPE: + global_Done = 1; + break; + case '+': + cam.translateBy(Vector3D(0, -0.5f, 0)); + break; + case '-': + cam.translateBy(Vector3D(0, 0.5f, 0)); + break; + case ',': + frame_offset -= 1; + if (frame_offset < 0) + frame_offset = 0; + update_anim = true; + break; + case '.': + frame_offset += 1; + if (frame_offset >= style.spriteNumbers.countByType(ped.sprType)) + frame_offset -= 1; + update_anim = true; + break; + case 'n': + do { + spr_type -= 1; + if (spr_type < 0) + spr_type = 0; + } while (style.spriteNumbers.countByType( + (OpenGTA::GraphicsBase::SpriteNumbers::SpriteTypes) spr_type) == 0); + ped.sprType = (OpenGTA::GraphicsBase::SpriteNumbers::SpriteTypes)spr_type; + frame_offset = 0; + update_anim = 1; + break; + case 'm': + do { + spr_type += 1; + if (spr_type > 20) + spr_type = (int)ped.sprType; + } while (style.spriteNumbers.countByType( + (OpenGTA::GraphicsBase::SpriteNumbers::SpriteTypes) spr_type) == 0); + ped.sprType = (OpenGTA::GraphicsBase::SpriteNumbers::SpriteTypes)spr_type; + frame_offset = 0; + update_anim = 1; + break; + case SDLK_F2: + bbox_toggle = (bbox_toggle ? 0 : 1); + OpenGTA::SpriteManagerHolder::Instance().setDrawBBox(bbox_toggle); + break; + case SDLK_F3: + texsprite_toggle = (texsprite_toggle ? 0 : 1); + OpenGTA::SpriteManagerHolder::Instance().setDrawTexBorder(texsprite_toggle); + break; + case SDLK_F5: + first_offset = frame_offset; + break; + case SDLK_F6: + second_offset = frame_offset; + break; + default: + break; + } + if (update_anim) { + pedAnim.firstFrameOffset = frame_offset; + ped.setAnimation(pedAnim); + } +} + +void usage(const char* a0) { + std::cout << "USAGE: " << a0 << " [style-filename]" << std::endl; + std::cout << std::endl << "Default is: STYLE001.GRY" << std::endl << + "Keys:" << std::endl << + " + - : zoom in/out" << std::endl << + " , . : previous/next frame offset" << std::endl << + " n m : previous/next sprite-type" << std::endl << + " F2 : toggle BBox drawn" << std::endl << + " F3 : toggle tex-border drawn" << std::endl; +} + +void parse_args(int argc, char* argv[]) { + int index; + int c; + + opterr = 0; +#define VIEWER_FLAGS "" + while ((c = getopt (argc, argv, VIEWER_FLAGS)) != -1) + switch (c) + { + case 'h': + case '?': + usage(argv[0]); + exit(0); + default: + if (isprint (optopt)) + ERROR << "Unknown option `-" << char(optopt) << "'" << std::endl; + else + ERROR << "Unknown option character `" << optopt << "'" << std::endl; + abort (); + } + + for (index = optind; index < argc; index++) + style_file = std::string(argv[index]); +} + +void run_main() { + SDL_Event event; + + glEnable(GL_TEXTURE_2D); + glPolygonMode(GL_FRONT, GL_FILL); + glEnable(GL_CULL_FACE); + + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0); + + OpenGL::Camera & cam = OpenGL::CameraHolder::Instance(); + cam.setVectors( Vector3D(4, 5, 4), Vector3D(4, 0.0f, 4.0f), Vector3D(0, 0, -1) ); + cam.setFollowMode(ped.pos); + while(!global_Done && !global_EC) { + while (SDL_PollEvent(&event)) { + switch(event.type) { + case SDL_KEYDOWN: + handleKeyPress(&event.key.keysym); + break; + case SDL_KEYUP: +// handleKeyUp(&event.key.keysym); + break; + case SDL_VIDEORESIZE: + OpenGL::ScreenHolder::Instance().resize(event.resize.w, event.resize.h); + break; + case SDL_QUIT: + global_Done = 1; + break; + case SDL_MOUSEMOTION: + //std::cout << "Mouse move: x " << float(event.motion.x)/screen->w << " y " << float(event.motion.y)/screen->h << std::endl; + break; + default: + break; + } + } + Uint32 now_ticks = SDL_GetTicks(); + drawScene(now_ticks); + } +} diff --git a/spritemanager.cpp b/spritemanager.cpp new file mode 100644 index 0000000..e560c59 --- /dev/null +++ b/spritemanager.cpp @@ -0,0 +1,448 @@ +/************************************************************************ +* Copyright (c) 2005-2006 tok@openlinux.org.uk * +* * +* This software is provided as-is, without any express or implied * +* warranty. In no event will the authors be held liable for any * +* damages arising from the use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute * +* it freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must * +* not claim that you wrote the original software. If you use this * +* software in a product, an acknowledgment in the product documentation * +* would be appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must * +* not be misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source * +* distribution. * +************************************************************************/ +#include +#include "gl_spritecache.h" +#include "dataholder.h" +#include "spritemanager.h" +#include "log.h" +#include "timer.h" + +namespace OpenGTA { + SpriteManager::SpriteManager() { + drawMode = (1); + + registerAnimation(0, SpriteObject::Animation(0, 0)); // dummy + + registerAnimation(1, SpriteObject::Animation(98, 0)); // standing still + registerAnimation(2, SpriteObject::Animation(0, 7, 0.001f)); // walking + registerAnimation(3, SpriteObject::Animation(8, 7, 0.0015f)); // running + +// registerAnimation(3, SpriteObject::Animation(16, 0)); // sitting in car +// registerAnimation(4, SpriteObject::Animation(17, 7)); // car-exit +// registerAnimation(5, SpriteObject::Animation(25, 7)); // car-enter + //registerAnimation(3, SpriteObject::Animation(107, 7, 0.002f)); + //registerAnimation(4, SpriteObject::Animation(99, 7, 0.001f)); + //registerAnimation(5, SpriteObject::Animation(28, 7)); +// registerAnimation(6, SpriteObject::Animation(38, 2)); // falling + registerAnimation(7, SpriteObject::Animation(41, 0)); // sliding under + registerAnimation(8, SpriteObject::Animation(42, 1)); // death pose; maybe just 1? + registerAnimation(9, SpriteObject::Animation(44, 0)); // death-back pose + registerAnimation(10, SpriteObject::Animation(45, 1)); // shot-in-front + registerAnimation(11, SpriteObject::Animation(47, 1)); // swimming + registerAnimation(12, SpriteObject::Animation(98, 0)); // standing still + + registerAnimation(4, SpriteObject::Animation(89, 0)); // standing, gun + registerAnimation(5, SpriteObject::Animation(99, 7, 0.001f)); // walking, gun + registerAnimation(6, SpriteObject::Animation(107, 7, 0.002f)); // running, gun + /* + registerAnimation(12, SpriteObject::Animation( + registerAnimation(13, SpriteObject::Animation( + registerAnimation(14, SpriteObject::Animation( + registerAnimation(15, SpriteObject::Animation( + registerAnimation(16, SpriteObject::Animation( + registerAnimation(17, SpriteObject::Animation( + registerAnimation(18, SpriteObject::Animation( + registerAnimation(19, SpriteObject::Animation( +*/ + } + SpriteManager::~SpriteManager() { + clear(); + animations.clear(); + } + + void SpriteManager::update(Uint32 ticks) { + for (PedListType::iterator i = activePeds.begin(); i != activePeds.end(); ++i) { + for (ObjectListType::iterator j = activeObjects.begin(); j != activeObjects.end(); ++j) { + Pedestrian & ped = *i; + GameObject & obj = *j; + Vector3D d(ped.GetCenterPoint() - obj.GetCenterPoint()); + if (d.SquareMagnitude() < 25) { + INFO << "obj: " << obj.pos.x << ", " << obj.pos.y << "[" << obj.m_Extent.y << "], " << obj.pos.z << std::endl; + INFO << "ped: " << ped.pos.x << ", " << ped.pos.y <<", " << ped.pos.z << std::endl; + INFO << "ped in obj: " << ped.IsBoxInBox(obj) << std::endl; + INFO << "obj in ped: " << obj.IsBoxInBox(ped) << std::endl; + } + } + } + for (PedListType::iterator i = activePeds.begin(); i != activePeds.end(); ++i) { + i->update(ticks); + } + } + + void SpriteManager::drawInRect(SDL_Rect & r) { + for (PedListType::iterator i = activePeds.begin(); i != activePeds.end(); ++i) { + Pedestrian & ped = (*i); + if ((ped.pos.x >= r.x) && (ped.pos.x <= r.x + r.w) && + (ped.pos.z >= r.y) && (ped.pos.z <= r.y + r.h)) { + drawPed(ped); + } + } + for (ObjectListType::iterator i = activeObjects.begin(); i != activeObjects.end(); ++i) { + GameObject & obj = (*i); + if ((obj.pos.x >= r.x) && (obj.pos.x <= r.x + r.w) && + (obj.pos.z >= r.y) && (obj.pos.z <= r.y + r.h)) { + drawObject(obj); + } + } + for (CarListType::iterator i = activeCars.begin(); i != activeCars.end(); ++i) { + Car & car = *i; + if ((car.pos.x >= r.x) && (car.pos.x <= r.x + r.w) && + (car.pos.z >= r.y) && (car.pos.z <= r.y + r.h)) { + drawCar(car); + } + } + } + + void SpriteManager::clear() { + activePeds.clear(); + activeObjects.clear(); + activeCars.clear(); + } + +/* + void SpriteManager::drawCar(Car & car) { + GraphicsBase & style = StyleHolder::Instance().get(); + + GraphicsBase::SpriteInfo * info = style.getSprite(sprNum); + + OpenGL::PagedTexture t; + GLfloat w, h; + + drawGL(t, w, h); + + } + */ + +#define GL_OBJ_COMMON(o) GL_CHECKERROR; \ + glPushMatrix(); \ + glTranslatef(o.pos.x, o.pos.y, o.pos.z); \ + glRotatef(o.rot, 0, 1, 0); \ + glGetFloatv(GL_MODELVIEW_MATRIX, *o.m_M.m) + + + void SpriteManager::drawCar(Car & car) { + GL_OBJ_COMMON(car); + GraphicsBase & style = StyleHolder::Instance().get(); + OpenGL::PagedTexture t; + PHYSFS_uint16 sprNum = style.spriteNumbers.reIndex(car.sprNum + + car.anim.firstFrameOffset + car.anim.currentFrame, car.sprType); + + GraphicsBase::SpriteInfo * info = style.getSprite(sprNum); + assert(info); + float w = float(info->w) / 64.0f; + float h = float(info->h) / 64.0f; + if (OpenGL::SpriteCacheHolder::Instance().has(sprNum, car.remap)) + t = OpenGL::SpriteCacheHolder::Instance().get(sprNum, car.remap); + else { + t = OpenGL::SpriteCacheHolder::Instance().create(car.sprNum + + car.anim.firstFrameOffset + car.anim.currentFrame, + car.sprType, car.remap); + } + glBindTexture(GL_TEXTURE_2D, t.inPage); + + glBegin(GL_QUADS); + glTexCoord2f(t.coords[0].u, t.coords[1].v); + glVertex3f(-w/2, 0.0f, h/2); + glTexCoord2f(t.coords[1].u, t.coords[1].v); + glVertex3f(w/2, 0.0f, h/2); + glTexCoord2f(t.coords[1].u, t.coords[0].v); + glVertex3f(w/2, 0.0f, -h/2); + glTexCoord2f(t.coords[0].u, t.coords[0].v); + glVertex3f(-w/2, 0.0f, -h/2); + + glEnd(); + glDisable(GL_TEXTURE_2D); + if (getDrawBBox()) { + glBegin(GL_LINE_STRIP); + glVertex3f(-car.m_Extent.x, 0.0f, car.m_Extent.z); + glVertex3f(car.m_Extent.x, 0.0f, car.m_Extent.z); + glVertex3f(car.m_Extent.x, 0.0f, -car.m_Extent.z); + glVertex3f(-car.m_Extent.x, 0.0f, -car.m_Extent.z); + glVertex3f(-car.m_Extent.x, 0.0f, car.m_Extent.z); + glEnd(); + glBegin(GL_LINE_STRIP); + glVertex3f(-car.m_Extent.x, car.m_Extent.y, car.m_Extent.z); + glVertex3f(car.m_Extent.x, car.m_Extent.y, car.m_Extent.z); + glVertex3f(car.m_Extent.x, car.m_Extent.y, -car.m_Extent.z); + glVertex3f(-car.m_Extent.x, car.m_Extent.y, -car.m_Extent.z); + glVertex3f(-car.m_Extent.x, car.m_Extent.y, car.m_Extent.z); + glEnd(); + } + if (getDrawTexBorder()) { + glDisable(GL_TEXTURE_2D); + glBegin(GL_LINE_STRIP); + glColor3f(float(202)/255.0f, float(31)/255.0f, float(123)/255.0f); + glVertex3f(-w/2, 0.0f, h/2); + glVertex3f(w/2, 0.0f, h/2); + glVertex3f(w/2, 0.0f, -h/2); + glVertex3f(-w/2, 0.0f, -h/2); + glVertex3f(-w/2, 0.0f, h/2); + glEnd(); + glColor3f(1.0f, 1.0f, 1.0f); + } + glEnable(GL_TEXTURE_2D); + + glPopMatrix(); + GL_CHECKERROR; + } + + void SpriteManager::drawObject(GameObject & obj) { + GL_OBJ_COMMON(obj); + GraphicsBase & style = StyleHolder::Instance().get(); + OpenGL::PagedTexture t; + PHYSFS_uint16 sprNum = style.spriteNumbers.reIndex(obj.sprNum + + obj.anim.firstFrameOffset + obj.anim.currentFrame, obj.sprType); + + GraphicsBase::SpriteInfo * info = style.getSprite(sprNum); + assert(info); + float w = float(info->w) / 64.0f; + float h = float(info->h) / 64.0f; + if (OpenGL::SpriteCacheHolder::Instance().has(sprNum, obj.remap)) + t = OpenGL::SpriteCacheHolder::Instance().get(sprNum, obj.remap); + else { + t = OpenGL::SpriteCacheHolder::Instance().create(obj.sprNum + + obj.anim.firstFrameOffset + obj.anim.currentFrame, + obj.sprType, obj.remap); + } + glBindTexture(GL_TEXTURE_2D, t.inPage); + + glBegin(GL_QUADS); + glTexCoord2f(t.coords[0].u, t.coords[1].v); + glVertex3f(-w/2, 0.0f, h/2); + glTexCoord2f(t.coords[1].u, t.coords[1].v); + glVertex3f(w/2, 0.0f, h/2); + glTexCoord2f(t.coords[1].u, t.coords[0].v); + glVertex3f(w/2, 0.0f, -h/2); + glTexCoord2f(t.coords[0].u, t.coords[0].v); + glVertex3f(-w/2, 0.0f, -h/2); + + glEnd(); + glDisable(GL_TEXTURE_2D); + if (getDrawBBox()) { + glBegin(GL_LINE_STRIP); + glVertex3f(-obj.m_Extent.x, 0.0f, obj.m_Extent.z); + glVertex3f(obj.m_Extent.x, 0.0f, obj.m_Extent.z); + glVertex3f(obj.m_Extent.x, 0.0f, -obj.m_Extent.z); + glVertex3f(-obj.m_Extent.x, 0.0f, -obj.m_Extent.z); + glVertex3f(-obj.m_Extent.x, 0.0f, obj.m_Extent.z); + glEnd(); + glBegin(GL_LINE_STRIP); + glVertex3f(-obj.m_Extent.x, obj.m_Extent.y, obj.m_Extent.z); + glVertex3f(obj.m_Extent.x, obj.m_Extent.y, obj.m_Extent.z); + glVertex3f(obj.m_Extent.x, obj.m_Extent.y, -obj.m_Extent.z); + glVertex3f(-obj.m_Extent.x, obj.m_Extent.y, -obj.m_Extent.z); + glVertex3f(-obj.m_Extent.x, obj.m_Extent.y, obj.m_Extent.z); + glEnd(); + } + if (getDrawTexBorder()) { + glDisable(GL_TEXTURE_2D); + glBegin(GL_LINE_STRIP); + glColor3f(float(202)/255.0f, float(31)/255.0f, float(123)/255.0f); + glVertex3f(-w/2, 0.0f, h/2); + glVertex3f(w/2, 0.0f, h/2); + glVertex3f(w/2, 0.0f, -h/2); + glVertex3f(-w/2, 0.0f, -h/2); + glVertex3f(-w/2, 0.0f, h/2); + glEnd(); + glColor3f(1.0f, 1.0f, 1.0f); + } + glEnable(GL_TEXTURE_2D); + + glPopMatrix(); + GL_CHECKERROR; + + } + + void SpriteManager::drawPed(Pedestrian & ped) { + GL_OBJ_COMMON(ped); + /* + GL_CHECKERROR; + glPushMatrix(); + glTranslatef(ped.pos.x, ped.pos.y, ped.pos.z); + glRotatef(ped.rot, 0, 1, 0); + glGetFloatv(GL_MODELVIEW_MATRIX, *ped.m_M.m); + + + + for (int i=0; i < 4; i++) { + for (int j=0; j <4 ;j++) { + std::cout << ped.m_M.m[i][j] << " "; + } + std::cout << std::endl; + } + std::cout << std::endl; + */ + + GraphicsBase & style = StyleHolder::Instance().get(); + + + OpenGL::PagedTexture t; + PHYSFS_uint16 sprNum = style.spriteNumbers.reIndex(ped.sprNum + + ped.anim.firstFrameOffset + ped.anim.currentFrame, ped.sprType); + + GraphicsBase::SpriteInfo * info = style.getSprite(sprNum); + assert(info); + float w = float(info->w) / 64.0f; + float h = float(info->h) / 64.0f; + + + if (OpenGL::SpriteCacheHolder::Instance().has(sprNum, ped.remap)) + t = OpenGL::SpriteCacheHolder::Instance().get(sprNum, ped.remap); + else { + t = OpenGL::SpriteCacheHolder::Instance().create(ped.sprNum + + ped.anim.firstFrameOffset + ped.anim.currentFrame, + ped.sprType, ped.remap); + } + glBindTexture(GL_TEXTURE_2D, t.inPage); + + glBegin(GL_QUADS); + glTexCoord2f(t.coords[0].u, t.coords[1].v); + glVertex3f(-w/2, 0.0f, h/2); + glTexCoord2f(t.coords[1].u, t.coords[1].v); + glVertex3f(w/2, 0.0f, h/2); + glTexCoord2f(t.coords[1].u, t.coords[0].v); + glVertex3f(w/2, 0.0f, -h/2); + glTexCoord2f(t.coords[0].u, t.coords[0].v); + glVertex3f(-w/2, 0.0f, -h/2); + + glEnd(); + glDisable(GL_TEXTURE_2D); + if (getDrawBBox()) { + glBegin(GL_LINE_STRIP); + glVertex3f(-ped.m_Extent.x, 0.0f, ped.m_Extent.z); + glVertex3f(ped.m_Extent.x, 0.0f, ped.m_Extent.z); + glVertex3f(ped.m_Extent.x, 0.0f, -ped.m_Extent.z); + glVertex3f(-ped.m_Extent.x, 0.0f, -ped.m_Extent.z); + glVertex3f(-ped.m_Extent.x, 0.0f, ped.m_Extent.z); + glEnd(); + } + if (getDrawTexBorder()) { + glDisable(GL_TEXTURE_2D); + glBegin(GL_LINE_STRIP); + glColor3f(float(202)/255.0f, float(31)/255.0f, float(123)/255.0f); + glVertex3f(-w/2, 0.0f, h/2); + glVertex3f(w/2, 0.0f, h/2); + glVertex3f(w/2, 0.0f, -h/2); + glVertex3f(-w/2, 0.0f, -h/2); + glVertex3f(-w/2, 0.0f, h/2); + glEnd(); + glColor3f(1.0f, 1.0f, 1.0f); + } + glEnable(GL_TEXTURE_2D); + + glPopMatrix(); + GL_CHECKERROR; + } + + void SpriteManager::addPed(Pedestrian & ped) { + activePeds.push_back(ped); + } + + Pedestrian & SpriteManager::getPedById(const Uint32 & id) { + PedListType::iterator i = activePeds.begin(); + while (i != activePeds.end()) { + if (i->pedId == id) + return *i; + ++i; + } + assert(0); + return *activePeds.begin(); + } + + void SpriteManager::removePedById(const Uint32 & id) { + PedListType::iterator i = activePeds.begin(); + while (i != activePeds.end()) { + if (i->pedId == id) { + activePeds.erase(i); + return; + } + ++i; + } + WARN << "didn't find ped id " << id << " -- cannot remove"<carId == id) { + return *i; + } + ++i; + } + assert(0); + return *activeCars.begin(); + } + + void SpriteManager::addObject(GameObject & go) { + activeObjects.push_back(go); + } + + GameObject & SpriteManager::getObjectById(const Uint32 & id) { + ObjectListType::iterator i = activeObjects.begin(); + while (i != activeObjects.end()) { + if (i->objId == id) { + return *i; + } + ++i; + } + assert(0); + return *activeObjects.begin(); + } + + SpriteObject::Animation & SpriteManager::getAnimationById(const Uint32 & id) { + AnimLookupType::iterator i = animations.find(id); + assert(i != animations.end()); + return i->second; + } + + void SpriteManager::registerAnimation(const Uint32 & id, + const SpriteObject::Animation & anim) { + animations.insert(std::make_pair(id, anim)); + } + + void SpriteManager::setDrawBBox(bool v) { + if (v) + drawMode = (drawMode | 4); + else + drawMode = (drawMode & 4 ? drawMode - 4 : drawMode); + } + + void SpriteManager::setDrawTexBorder(bool v) { + if (v) + drawMode = (drawMode | 2); + else + drawMode = (drawMode & 2 ? drawMode - 2 : drawMode); + } + + void SpriteManager::setDrawTexture(bool v) { + } + + void SpriteManager::createProjectile(uint8_t typeId, float r, Vector3D p, Vector3D d, Uint32 & ticks) { + activeProjectiles.push_back(Projectile(typeId, r, p, d, ticks)); + } +} diff --git a/spritemanager.h b/spritemanager.h new file mode 100644 index 0000000..f5cdd79 --- /dev/null +++ b/spritemanager.h @@ -0,0 +1,89 @@ +/************************************************************************ +* Copyright (c) 2005-2006 tok@openlinux.org.uk * +* * +* This software is provided as-is, without any express or implied * +* warranty. In no event will the authors be held liable for any * +* damages arising from the use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute * +* it freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must * +* not claim that you wrote the original software. If you use this * +* software in a product, an acknowledgment in the product documentation * +* would be appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must * +* not be misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source * +* distribution. * +************************************************************************/ +#ifndef OGTA_SpriteManager_H +#define OGTA_SpriteManager_H + +#include +#include +#include "pedestrian.h" +#include "Singleton.h" + +namespace OpenGTA { + class SpriteManager { + public: + SpriteManager(); + ~SpriteManager(); + void drawInRect(SDL_Rect & r); + void clear(); + + void addPed(Pedestrian & ped); + Pedestrian & getPedById(const Uint32 & id); + void removePedById(const Uint32 & id); + + void addCar(Car & car); + Car & getCarById(const Uint32 & id); + void removeCarById(const Uint32 & id); + + void addObject(GameObject & go); + GameObject & getObjectById(const Uint32 & id); + void removeObjectById(const Uint32 & id); + + void update(Uint32 ticks); + SpriteObject::Animation & getAnimationById(const Uint32 & id); + void registerAnimation(const Uint32 & id, const SpriteObject::Animation & anim); + + inline bool getDrawTexture() { return (drawMode & 1); } + inline bool getDrawTexBorder() { return (drawMode & 2); } + inline bool getDrawBBox() { return (drawMode & 4); } + void setDrawTexture(bool v); + void setDrawTexBorder(bool v); + void setDrawBBox(bool v); + + void drawPed(Pedestrian & ped); + void drawCar(Car & car); + void drawObject(GameObject & obj); + + void createProjectile(uint8_t typeId, float, Vector3D p, Vector3D d, Uint32 & ticks); + void drawProjectile(Projectile & p); + void collideProjectile(Projectile & p); + + protected: + typedef std::list PedListType; + PedListType activePeds; + typedef std::list CarListType; + CarListType activeCars; + typedef std::list ObjectListType; + ObjectListType activeObjects; + typedef std::list ProjectileListType; + ProjectileListType activeProjectiles; + typedef std::map AnimLookupType; + AnimLookupType animations; + private: + Uint32 drawMode; + }; + + typedef Loki::SingletonHolder SpriteManagerHolder; +} + +#endif diff --git a/tests/sound_test1.cpp b/tests/sound_test1.cpp new file mode 100644 index 0000000..c6ff49b --- /dev/null +++ b/tests/sound_test1.cpp @@ -0,0 +1,174 @@ +#include +#include +#include +#include + +#include +#include "physfsrwops.h" +#include +//#include "wavestream2.h" + +class SoundDevice { + public: + SoundDevice(); + ~SoundDevice(); + void close(); + void open(); + void open(int r, Uint16 f, int c, int bs); + const char* getCardName() { return cardName; } + private: + char cardName[50]; + int rate; + int channels; + int bufSize; + Uint16 format; + int _status; +}; + + +SoundDevice::SoundDevice() { + memset(cardName, 0, sizeof(cardName)); + rate = 44100; + channels = 2; + bufSize = 4096; + format = AUDIO_S16; + _status = 0; +} + +SoundDevice::~SoundDevice() { + Mix_HaltGroup(-1); + Mix_HaltMusic(); + close(); +} + +void SoundDevice::close() { + if (!_status) + return; + Mix_CloseAudio(); +} + +void SoundDevice::open() { + if (_status) + close(); + if (Mix_OpenAudio(rate, format, channels, bufSize)) + throw std::string(SDL_GetError()); + SDL_AudioDriverName (cardName, sizeof (cardName)); + Mix_QuerySpec(&rate, &format, &channels); + fprintf (stderr, "opened %s at %d Hz %d bit %s (%i), %d bytes audio buffer\n", + cardName, rate, format & 0xFF, + channels > 1 ? "stereo" : "mono", channels, bufSize); + + _status = 1; +} + +void SoundDevice::open(int r, Uint16 f, int c, int bs) { + rate = r; + format = f; + channels = c; + bufSize = bs; + open(); +} + +#if 0 +void myMusicPlayer(void *udata, Uint8 *stream, int len) { + int i,act=0; + Sint16 *ptr2; + + if (stream == 0) + throw std::string("Zero music stream :-("); + + ptr2=(Sint16 *)stream; + if (playing_music) { + while(actflags&SOUND_SAMPLEFLAG_EOF)) { + /* End of file: */ + if (music_loops!=-1) { + current_music_loop++; + if (current_music_loop>music_loops) { + playing_music=false; + if (music_sound!=0) Sound_FreeSample(music_sound); + + music_sound=0; + } else { + Sound_Rewind(music_sound); + } /* if */ + } else { + Sound_Rewind(music_sound); + } /* if */ + } else { + /* In the middle of the file: */ + int decoded=0; + Sint16 *ptr; + + Sound_SetBufferSize(music_sound, len-act); + + decoded=Sound_Decode(music_sound); + ptr=(Sint16 *)music_sound->buffer; + for(i=0;i + + float slope_raw_data[45][5][4][3] = { +#include "slope1_data.h" + }; + +int main(int argc, char* argv[]) { + int slope_type, vertex; + int equal_y1, equal_y2, other_y; + int marked_axis; +#define STORE(a, b, c) { equal_y1 = a; equal_y2 = b; other_y = c;} + + printf("#include \n"); + printf("float slope_height_offset(unsigned char slope_type, float dx, float dz) {\n"); + printf(" assert((dx >= 0.0f) && (dx <= 1.0f));\n"); + printf(" assert((dz >= 0.0f) && (dz <= 1.0f));\n"); + printf(" switch(slope_type) {\n"); + + for (slope_type = 0 ; slope_type < 45; slope_type++) { + //printf("# slope %i\n", slope_type); + printf(" case %i:\n", slope_type); + if (slope_raw_data[slope_type][0][0][1] == slope_raw_data[slope_type][0][1][1]) { + //printf("0, 1\n"); + STORE(0, 1, 2); + } + else if (slope_raw_data[slope_type][0][1][1] == slope_raw_data[slope_type][0][2][1]) { + //printf("1, 2\n"); + STORE(1, 2, 0); + } + else if (slope_raw_data[slope_type][0][2][1] == slope_raw_data[slope_type][0][3][1]) { + //printf("2, 3\n"); + STORE(2, 3, 0); + } + else if (slope_raw_data[slope_type][0][0][1] == slope_raw_data[slope_type][0][2][1]) { + //printf("0, 2\n"); + STORE(0, 2, 1); + } + else if (slope_raw_data[slope_type][0][1][1] == slope_raw_data[slope_type][0][3][1]) { + //printf("1, 3\n"); + STORE(1, 3, 0); + } + else { + printf("argh?\n"); + return(1); + } + if (slope_raw_data[slope_type][0][equal_y1][0] == slope_raw_data[slope_type][0][equal_y2][0]) { + //printf("# x-axis\n"); + marked_axis = 0; + } + else if (slope_raw_data[slope_type][0][equal_y1][2] == slope_raw_data[slope_type][0][equal_y2][2]) { + //printf("# z-axis\n"); + marked_axis = 2; + } + else { + printf("not good\n"); + return(1); + } + + float low = 1.0f; + float high = 0.0f; + int low_idx = -1; + int high_idx = -1; + for (vertex = 0; vertex < 4; vertex++) { + if (slope_raw_data[slope_type][0][vertex][1] < low) { + low = slope_raw_data[slope_type][0][vertex][1]; + low_idx = vertex; + } + if (slope_raw_data[slope_type][0][vertex][1] > high) { + high = slope_raw_data[slope_type][0][vertex][1]; + high_idx = vertex; + } + } + //printf("# low: %f high: %f\n", low, high); + int increasing; + + if (slope_raw_data[slope_type][0][low_idx][marked_axis] < + slope_raw_data[slope_type][0][high_idx][marked_axis]) + increasing = 1; + else + increasing = 0; + + if (high - low == 0.0f) + printf(" return 0.0f;\n"); + else + printf(" return %ff %c d%c * %ff;\n", (increasing ? low : high), + (increasing ? '+' : '-'), + ((marked_axis == 0) ? 'x' : 'z'), (high - low)); + + /* + for (vertex = 0; vertex < 4; vertex++) { + printf("%f %f %f\n", + slope_raw_data[slope_type][0][vertex][0], + slope_raw_data[slope_type][0][vertex][1], + slope_raw_data[slope_type][0][vertex][2]); + } + */ + } + printf(" default:\n"); + printf(" break;\n"); + printf(" }\n"); + printf(" assert(0);\n"); + printf(" return 0.0f; // should never be reached\n"); + printf("}\n"); +} diff --git a/tools/analyse_lids_2.c b/tools/analyse_lids_2.c new file mode 100644 index 0000000..04ee647 --- /dev/null +++ b/tools/analyse_lids_2.c @@ -0,0 +1,98 @@ +#include +#include + + float slope_raw_data[45][5][4][3] = { +#include "slope1_data.h" + }; + +int main(int argc, char* argv[]) { + int slope_type, vertex; + int equal_y1, equal_y2, other_y; + int marked_axis; +#define STORE(a, b, c) { equal_y1 = a; equal_y2 = b; other_y = c;} + + printf("#include \n"); + printf("float slope_height_offset(unsigned char slope_type, float dx, float dz) {\n"); + printf(" assert((dx >= 0.0f) && (dx <= 1.0f));\n"); + printf(" assert((dz >= 0.0f) && (dz <= 1.0f));\n"); + printf(" switch(slope_type) {\n"); + + for (slope_type = 0 ; slope_type < 45; slope_type++) { + //printf("# slope %i\n", slope_type); + printf(" case %i:\n", slope_type); + if (slope_raw_data[slope_type][0][0][1] == slope_raw_data[slope_type][0][1][1]) { + //printf("0, 1\n"); + STORE(0, 1, 2); + } + else if (slope_raw_data[slope_type][0][1][1] == slope_raw_data[slope_type][0][2][1]) { + //printf("1, 2\n"); + STORE(1, 2, 0); + } + else if (slope_raw_data[slope_type][0][2][1] == slope_raw_data[slope_type][0][3][1]) { + //printf("2, 3\n"); + STORE(2, 3, 0); + } + else if (slope_raw_data[slope_type][0][0][1] == slope_raw_data[slope_type][0][2][1]) { + //printf("0, 2\n"); + STORE(0, 2, 1); + } + else if (slope_raw_data[slope_type][0][1][1] == slope_raw_data[slope_type][0][3][1]) { + //printf("1, 3\n"); + STORE(1, 3, 0); + } + else { + printf("argh?\n"); + return(1); + } + if (slope_raw_data[slope_type][0][equal_y1][0] == slope_raw_data[slope_type][0][equal_y2][0]) { + //printf("# x-axis\n"); + marked_axis = 0; + } + else if (slope_raw_data[slope_type][0][equal_y1][2] == slope_raw_data[slope_type][0][equal_y2][2]) { + //printf("# z-axis\n"); + marked_axis = 2; + } + else { + printf("not good\n"); + return(1); + } + + float low = 1.0f; + float high = 0.0f; + int low_idx = -1; + int high_idx = -1; + for (vertex = 0; vertex < 4; vertex++) { + if (slope_raw_data[slope_type][0][vertex][1] < low) { + low = slope_raw_data[slope_type][0][vertex][1]; + low_idx = vertex; + } + if (slope_raw_data[slope_type][0][vertex][1] > high) { + high = slope_raw_data[slope_type][0][vertex][1]; + high_idx = vertex; + } + } + //printf("# low: %f high: %f\n", low, high); + int increasing; + + if (slope_raw_data[slope_type][0][low_idx][marked_axis] < + slope_raw_data[slope_type][0][high_idx][marked_axis]) + increasing = 1; + else + increasing = 0; + + float dt = high - low; + printf("%.4ff %i %i\n", atan(dt / 1.0f) * 180.0f / M_PI, increasing, marked_axis); + for (vertex = 0; vertex < 4; vertex++) { + printf("%f %f %f\n", + slope_raw_data[slope_type][0][vertex][0], + slope_raw_data[slope_type][0][vertex][1], + slope_raw_data[slope_type][0][vertex][2]); + } + } + printf(" default:\n"); + printf(" break;\n"); + printf(" }\n"); + printf(" assert(0);\n"); + printf(" return 0.0f; // should never be reached\n"); + printf("}\n"); +} diff --git a/tools/blockview.cpp b/tools/blockview.cpp new file mode 100644 index 0000000..d0eb943 --- /dev/null +++ b/tools/blockview.cpp @@ -0,0 +1,348 @@ +#include +#include +#include "common_sdl_gl.h" +#include "opengta.h" +#include "gl_texturecache.h" + +namespace OpenGTA { + class MapViewGL { + private: + Map* currentMap; + Graphics8Bit* styleDB; + OpenGL::TextureCache* sideCache; + OpenGL::TextureCache* lidCache; + public: + MapViewGL(); + ~MapViewGL(); + int loadMap(const std::string &map, const std::string &style); + void drawMap(int32_t x, int32_t y, int32_t dx, int32_t dy); + void drawBlock(Map::BlockInfo* bi); + }; +} + +namespace OpenGTA { + MapViewGL::MapViewGL() { + currentMap = NULL; + styleDB = NULL; + sideCache = NULL; + lidCache = NULL; + } + MapViewGL::~MapViewGL() { + if (currentMap) + delete currentMap; + if (styleDB) + delete styleDB; + if (sideCache) + delete sideCache; + if (lidCache) + delete lidCache; + } + int MapViewGL::loadMap(const std::string &map, const std::string &style) { + currentMap = new Map(map); + styleDB = new Graphics8Bit(style); + sideCache = new OpenGL::TextureCache(); + lidCache = new OpenGL::TextureCache(); + //currentMap->dump(); + return 0; + } +} +extern SDL_Surface* screen; +GLfloat mapPos[3] = {12.0f, 12.0f, 20.0f}; +GLfloat camVec[3] = {0.0f, 1.0f, 0.0f}; + +OpenGTA::MapViewGL *map = NULL; + +void on_exit() { + SDL_FreeSurface(screen); + SDL_Quit(); + if (map) + delete map; + PHYSFS_deinit(); + if (global_EC) + std::cerr << "Exiting after fatal problem - please see output above" << std::endl; + else + std::cout << "Goodbye" << std::endl; +} + +void handleKeyPress( SDL_keysym *keysym ) { + switch ( keysym->sym ) { + case SDLK_ESCAPE: + global_Done = 1; + break; + case SDLK_LEFT: + mapPos[0] += 1.0f; + break; + case SDLK_RIGHT: + mapPos[0] -= 1.0f; + if (mapPos[0] < 0.0f) + mapPos[0] = 0.0f; + break; + case SDLK_UP: + mapPos[1] += 1.0f; + break; + case SDLK_DOWN: + mapPos[1] -= 1.0f; + if (mapPos[1] < 0.0f) + mapPos[1] = 0.0f; + break; + case '+': + mapPos[2] += 1.0f; + break; + case '-': + mapPos[2] -= 1.0f; + break; + case 'x': + camVec[0] = 1.0f; camVec[1] = 0.0f; camVec[2] = 0.0f; + break; + case 'y': + camVec[0] = 0.0f; camVec[1] = 1.0f; camVec[2] = 0.0f; + break; + case 'z': + camVec[0] = 0.0f; camVec[1] = 0.0f; camVec[2] = 1.0f; + break; + default: + break; + } +} + +GLuint createGLTexture(GLsizei w, GLsizei h, const void* pixels) { + GLuint tex; + glGenTextures(1, &tex); + glBindTexture(GL_TEXTURE_2D, tex); + //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, param); + //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, param); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + // GL_LINEAR_MIPMAP_LINEAR); + /*gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, w, + h, GL_RGB, GL_UNSIGNED_BYTE, pixels); + */ + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + return tex; +} + +void OpenGTA::MapViewGL::drawBlock(OpenGTA::Map::BlockInfo* bi) { + float size = 0.5f; + int jj = 0; + if (bi == NULL) + return; + glEnable(GL_TEXTURE_2D); + if (bi->left) { + // Left Face + if (!sideCache->hasTexture(bi->left)) { + styleDB->getSide(static_cast(bi->left-1), 0, true); + sideCache->addTexture(bi->left, createGLTexture(64, 64, &styleDB->tileTmpRGBA)); + } + glBindTexture(GL_TEXTURE_2D, sideCache->getTextureWithId(bi->left)); + //} + glBegin(GL_QUADS); + glTexCoord2f(0.0f, 0.0f); + //glNormal3f(-1.0f, 0.0f, 0.0f); + glVertex3f(-size, -size, -size);// Bottom Left Of The Texture and Quad + glTexCoord2f(1.0f, 0.0f); + glVertex3f(-size, -size, size);// Bottom Right Of The Texture and Quad + glTexCoord2f(1.0f, 1.0f); + glVertex3f(-size, size, size);// Top Right Of The Texture and Quad + glTexCoord2f(0.0f, 1.0f); + glVertex3f(-size, size, -size);// Top Left Of The Texture and Quad + glEnd(); + } + if (bi->right) { + // Right face + if (!sideCache->hasTexture(bi->right)) { + styleDB->getSide(static_cast(bi->right-1), 0, true); + sideCache->addTexture(bi->right, createGLTexture(64, 64, &styleDB->tileTmpRGBA)); + } + glBindTexture(GL_TEXTURE_2D, sideCache->getTextureWithId(bi->right)); + //} + glBegin(GL_QUADS); + glTexCoord2f(1.0f, 0.0f); + //glNormal3f(1.0f, 0.0f, 0.0f); + glVertex3f( size, -size, -size);// Bottom Right Of The Texture and Quad + glTexCoord2f(1.0f, 1.0f); + glVertex3f( size, size, -size);// Top Right Of The Texture and Quad + glTexCoord2f(0.0f, 1.0f); + glVertex3f( size, size, size);// Top Left Of The Texture and Quad + glTexCoord2f(0.0f, 0.0f); + glVertex3f( size, -size, size);// Bottom Left Of The Texture and Quad + glEnd(); + } + if (bi->top) { + // Back Face + if (!sideCache->hasTexture(bi->top)) { + styleDB->getSide(static_cast(bi->top-1), 0, true); + sideCache->addTexture(bi->top, createGLTexture(64, 64, &styleDB->tileTmpRGBA)); + } + glBindTexture(GL_TEXTURE_2D, sideCache->getTextureWithId(bi->top)); + //} + glBegin(GL_QUADS); + glTexCoord2f(0.0f, 1.0f); + //glNormal3f(0.0f, 0.0f, 1.0f); + glVertex3f(-size, size, -size);// Top Left Of The Texture and Quad + glTexCoord2f(0.0f, 0.0f); + glVertex3f(-size, size, size);// Bottom Left Of The Texture and Quad + glTexCoord2f(1.0f, 0.0f); + glVertex3f( size, size, size);// Bottom Right Of The Texture and Quad + glTexCoord2f(1.0f, 1.0f); + glVertex3f( size, size, -size);// Top Right Of The Texture and Quad + glEnd(); + } + if (bi->bottom) { + // Front Face + if (!sideCache->hasTexture(bi->bottom)) { + styleDB->getSide(static_cast(bi->bottom-1), 0, true); + sideCache->addTexture(bi->bottom, createGLTexture(64, 64, &styleDB->tileTmpRGBA)); + } + glBindTexture(GL_TEXTURE_2D, sideCache->getTextureWithId(bi->bottom)); + //} + glBegin(GL_QUADS); + //glNormal3f(0.0f, 0.0f, -1.0f); + glTexCoord2f(1.0f, 1.0f); + glVertex3f(-size, -size, -size);// Top Right Of The Texture and Quad + glTexCoord2f(0.0f, 1.0f); + glVertex3f( size, -size, -size);// Top Left Of The Texture and Quad + glTexCoord2f(0.0f, 0.0f); + glVertex3f( size, -size, size);// Bottom Left Of The Texture and Quad + glTexCoord2f(1.0f, 0.0f); + glVertex3f(-size, -size, size);// Bottom Right Of The Texture and Quad + glEnd(); + } + if (bi->lid) { + // Top Face + if (!lidCache->hasTexture(bi->lid)) { + styleDB->getLid(static_cast(bi->lid-1), 0, true); + lidCache->addTexture(bi->lid, createGLTexture(64, 64, &styleDB->tileTmpRGBA)); + } + if (bi->typeMapExt & 128) { + //std::cout << "blending!" << std::endl; + } + jj = 0; + if (bi->typeMap & 16384) { + jj += 2; + } + if (bi->typeMap & 32768) { + jj += 4; + } + GLfloat lidTex[8] = {0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f}; +#ifdef GLTEX_HELPER +#undef GLTEX_HELPER +#endif +#define GLTEX_HELPER \ + glTexCoord2f(lidTex[jj], lidTex[jj+1]); jj += 2; if (jj > 6) { jj = 0; } + + glBindTexture(GL_TEXTURE_2D, lidCache->getTextureWithId(bi->lid)); + //} + glBegin(GL_QUADS); + GLTEX_HELPER; + //glNormal3f(0.0f, 1.0f, 0.0f); + glVertex3f(-size, -size, size);// Bottom Left Of The Texture and Quad + GLTEX_HELPER; + glVertex3f( size, -size, size);// Bottom Right Of The Texture and Quad + GLTEX_HELPER; + glVertex3f( size, size, size);// Top Right Of The Texture and Quad + GLTEX_HELPER; + glVertex3f(-size, size, size);// Top Left Of The Texture and Quad + glEnd(); +#undef GLTEX_HELPER + } +} +void OpenGTA::MapViewGL::drawMap(int32_t x1, int32_t y1, int32_t x2, int32_t y2) { + if (x1 < 0) + x1 = 0; + if (y1 < 0) + y1 = 0; + + // FIXME: can't access 255 either x or y... WHY? + if (x2 >= 255) + x2 = 255; + if (y2 >= 255) + y2 = 255; + + //std::cout << "draw: " << x1 << " " << y1 << " - " << x2 << " " << y2 << std::endl; + sideCache->sink(); + lidCache->sink(); + for (int i = y1; i <= y2; i++) { + glPushMatrix(); + glTranslatef(0.0f, -1.0f*i, 0.0f); + for (int j= x1; j <= x2; j++) { + glPushMatrix(); + glTranslatef(1.0f*j, 0.0f, 0.0f); + PHYSFS_uint16 emptycount = currentMap->getNumBlocksAt(j,i); + /*glPushMatrix(); + * for(int c=0; c < 6 - emptycount-1; c++) */ + for (int c=6-emptycount-0; c >= 1; c--) { + drawBlock(currentMap->getBlockAt(j,i, c)); + glTranslatef(0.0f, 0.0f, 1.0f); + } + glPopMatrix(); + } + glPopMatrix(); + } + sideCache->clear(); + lidCache->clear(); +} + +void drawScene() { + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glLoadIdentity(); + gluLookAt(mapPos[0], -mapPos[1], mapPos[2], mapPos[0], -mapPos[1], 0.0f, camVec[0], camVec[1], camVec[2]); + //map->drawMap(int(mapPos[0]), int(mapPos[1])); + map->drawMap(int32_t(mapPos[0])-10, int32_t(mapPos[1])-10,int32_t(mapPos[0])+10, int32_t(mapPos[1])+10); + + SDL_GL_SwapBuffers(); +} + + +void run_main() { + SDL_Event event; + int paused = 0; + + PHYSFS_init("mapview"); + PHYSFS_addToSearchPath(PHYSFS_getBaseDir(), 1); + + glEnable(GL_TEXTURE_2D); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glEnable(GL_CULL_FACE); + glEnable(GL_BLEND); + glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + //glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + //glEnable(GL_ALPHA_TEST); + //glDisable(GL_DEPTH_TEST); + + map = new OpenGTA::MapViewGL(); + map->loadMap("NYC.CMP", "STYLE001.GRY"); + + while(!global_Done && !global_EC) { + while (SDL_PollEvent(&event)) { + switch(event.type) { + case SDL_ACTIVEEVENT: + if (event.active.gain == 0) + paused = 1; + else + paused = 0; + break; + case SDL_KEYDOWN: + handleKeyPress(&event.key.keysym); + break; + case SDL_VIDEORESIZE: + screen = SDL_SetVideoMode( event.resize.w, + event.resize.h, 32, videoFlags ); + if (!screen) + ERROR("Failed to set video mode after resize event"); + resize(event.resize.w, event.resize.h); + break; + case SDL_QUIT: + global_Done = 1; + break; + case SDL_MOUSEMOTION: + std::cout << "Mouse move: x " << float(event.motion.x)/screen->w << " y " << float(event.motion.y)/screen->h << std::endl; + break; + default: + break; + } + } + if (!paused) + drawScene(); + } +} diff --git a/tools/create_normals.cpp b/tools/create_normals.cpp new file mode 100644 index 0000000..075a528 --- /dev/null +++ b/tools/create_normals.cpp @@ -0,0 +1,38 @@ +#include +#include + +float slope_raw_data[45][5][4][3] = { +#include "slope1_data.h" +}; + +int main(int argc, char* argv[]) { + for (int which=0; which <= 44; which++) { + std::cout << " // slope " << which << std::endl; + int side = 0; + + float x1,x2,y1,y2,z1,z2 = 0.0f; + x1 = slope_raw_data[which][side][0][0] - slope_raw_data[which][side][1][0]; + y1 = slope_raw_data[which][side][0][1] - slope_raw_data[which][side][1][1]; + z1 = slope_raw_data[which][side][0][2] - slope_raw_data[which][side][1][2]; + + x2 = slope_raw_data[which][side][1][0] - slope_raw_data[which][side][2][0]; + y2 = slope_raw_data[which][side][1][1] - slope_raw_data[which][side][2][1]; + z2 = slope_raw_data[which][side][1][2] - slope_raw_data[which][side][2][2]; + + float nx,ny,nz,vLen = 0.0f; + nx = (y1 * z2) - (z1 * y2); + ny = (z1 * x2) - (x1 * z2); + nz = (x1 * y2) - (y1 * x2); + vLen = sqrt( (nx * nx) + (ny * ny) + (nz * nz) ); + if (vLen == 0.0f) + std::printf("%f %f %f\n", nx, ny, nz); + nx /= vLen; + ny /= vLen; + nz /= vLen; + + if (which < 44) + std::printf(" { %f, %f, %f },\n", nx, ny, nz); + else + std::printf(" { %f, %f, %f }\n", nx, ny, nz); + } +} diff --git a/tools/display_font.cpp b/tools/display_font.cpp new file mode 100644 index 0000000..5269fb4 --- /dev/null +++ b/tools/display_font.cpp @@ -0,0 +1,114 @@ +#include +#include +#include +#include +#include "common_sdl_gl.h" +#include "opengta.h" +#include "gl_base.h" +#include "gl_font.h" + +extern SDL_Surface* screen; +GLfloat mapPos[2] = {0.0f, 0.0f}; +GLuint lid; +OpenGL::DrawableFont * font = NULL; + +void on_exit() { + if (font != NULL) + delete font; + SDL_Quit(); + PHYSFS_deinit(); + if (global_EC) + std::cerr << "Exiting after fatal problem - please see output above" << std::endl; + else + std::cout << "Goodbye" << std::endl; +} + +void handleKeyPress( SDL_keysym *keysym ) { + switch ( keysym->sym ) { + case SDLK_ESCAPE: + global_Done = 1; + break; + case 'a': + glEnable(GL_CULL_FACE); + printf("backfaces culled\n"); + break; + case 'b': + glDisable(GL_CULL_FACE); + printf("all faces drawn\n"); + break; + default: + break; + } +} + +void drawScene() { + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glLoadIdentity(); + + // ... + + glEnable(GL_TEXTURE_2D); + glDisable(GL_DEPTH_TEST); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0,640,0,480,-1,1); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + font->drawString("HELLO 1234567890 Hello !"); + + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + glEnable(GL_DEPTH_TEST); + + SDL_GL_SwapBuffers(); +} + +void run_main() { + SDL_Event event; + int paused = 0; + + PHYSFS_init("mapview"); + PHYSFS_addToSearchPath(PHYSFS_getBaseDir(), 1); + PHYSFS_addToSearchPath("gtadata.zip", 1); + + font = new OpenGL::DrawableFont(); + + font->loadFont("STREET1.FON"); + + + glCullFace(GL_BACK); + + while(!global_Done && !global_EC) { + while (SDL_PollEvent(&event)) { + switch(event.type) { + case SDL_ACTIVEEVENT: + if (event.active.gain == 0) + paused = 1; + else + paused = 0; + break; + case SDL_KEYDOWN: + handleKeyPress(&event.key.keysym); + break; + case SDL_VIDEORESIZE: + screen = SDL_SetVideoMode( event.resize.w, + event.resize.h, 32, videoFlags ); + if (!screen) + ERROR("Failed to set video mode after resize event"); + resize(event.resize.w, event.resize.h); + break; + case SDL_QUIT: + global_Done = 1; + break; + default: + break; + } + } + if (!paused) + drawScene(); + } + +} diff --git a/tools/display_slopes.cpp b/tools/display_slopes.cpp new file mode 100644 index 0000000..28b1eb6 --- /dev/null +++ b/tools/display_slopes.cpp @@ -0,0 +1,447 @@ +#include +#include +#include +#include +#include "common_sdl_gl.h" +#include "opengta.h" + +extern SDL_Surface* screen; +GLfloat mapPos[2] = {0.0f, 0.0f}; + +OpenGTA::Map *map = NULL; + +float slope_raw_data[45][5][4][3] = { +#include "slope1_data.h" +}; + +float lid_normal_data[45][3] = { +#include "lid_normal_data.h" +}; + +int slope_idx = 0; +float angle = 0; + +GLuint north, south, west, east, lid; + +void on_exit() { + SDL_Quit(); + if (map) + delete map; + PHYSFS_deinit(); + if (global_EC) + std::cerr << "Exiting after fatal problem - please see output above" << std::endl; + else + std::cout << "Goodbye" << std::endl; +} + +void handleKeyPress( SDL_keysym *keysym ) { + switch ( keysym->sym ) { + case SDLK_ESCAPE: + global_Done = 1; + break; + case SDLK_LEFT: + mapPos[0] -= 1.0f; + break; + case SDLK_RIGHT: + mapPos[0] += 1.0f; + break; + case SDLK_UP: + mapPos[1] += 1.0f; + break; + case SDLK_DOWN: + mapPos[1] -= 1.0f; + break; + case 'a': + glEnable(GL_CULL_FACE); + printf("backfaces culled\n"); + break; + case 'b': + glDisable(GL_CULL_FACE); + printf("all faces drawn\n"); + break; + case '.': + angle += 1.0f; + break; + case ',': + angle -= 1.0f; + break; + case '+': + slope_idx++; + if (slope_idx > 44) + slope_idx = 44; + printf("now %i\n", slope_idx); + break; + case '-': + slope_idx--; + if (slope_idx < 0) + slope_idx = 0; + printf("now %i\n", slope_idx); + break; + default: + break; + } +} + +void draw_slope(float size, int which) { + float red = 0.4; + float green = 0.7; + float blue = 0.3; +#if 0 + + float x1,x2,y1,y2,z1,z2 = 0.0f; + x1 = slope_raw_data[which][0][0][0] - slope_raw_data[which][0][1][0]; + y1 = slope_raw_data[which][0][0][1] - slope_raw_data[which][0][1][1]; + z1 = slope_raw_data[which][0][0][2] - slope_raw_data[which][0][1][2]; + + x2 = slope_raw_data[which][0][1][0] - slope_raw_data[which][0][2][0]; + y2 = slope_raw_data[which][0][1][1] - slope_raw_data[which][0][2][1]; + z2 = slope_raw_data[which][0][1][2] - slope_raw_data[which][0][2][2]; + + float nx,ny,nz,vLen = 0.0f; + nx = (y1 * z2) - (z1 * y2); + ny = (z1 * x2) - (x1 * z2); + nz = (x1 * y2) - (y1 * x2); + vLen = sqrt( (nx * nx) + (ny * ny) + (nz * nz) ); + nx /= vLen; + ny /= vLen; + nz /= vLen; + //std::cout << nx << " " << ny << " " << nz << std::endl; +#endif + float nx = lid_normal_data[which][0]; + float ny = lid_normal_data[which][1]; + float nz = lid_normal_data[which][2]; + + GLfloat lidTex[8] = {1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f}; + //GLfloat lidTex[8] = {0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f}; +#ifdef GLTEX_HELPER +#undef GLTEX_HELPER +#endif +#define GLTEX_HELPER \ + glTexCoord2f(lidTex[jj], lidTex[jj+1]); jj += 2; if (jj > 6) { jj = 0; } + for (int i=0; i<5; i++) { + if (i == 0) { + red = green = 0.0f; + blue = 1.0f; + } + else if (i == 1) { + red = blue = 0.0f; + green = 1.0f; + } + else if (i == 2) { + red = 1.0f; + blue = green = 0.0f; + } + else if (i == 3) { + red = 0.0f; + blue = green = 1.0f; + } + else if (i == 4) { + red = green = 1.0f; + blue = 0.0f; + } + if (which == 41 && i == 1) + continue; + if (which == 42 && i == 2) + continue; + if (which == 43 && i == 4) + continue; + if (which == 44 && i == 3) + continue; + /* + if (i == 0) + glBindTexture(GL_TEXTURE_2D, lid); + if (i == 1) + glBindTexture(GL_TEXTURE_2D, north); + if (i == 2) + glBindTexture(GL_TEXTURE_2D, south); + if (i == 3) + glBindTexture(GL_TEXTURE_2D, east); // FIXME: flipped west<->east ??? + if (i == 4) + glBindTexture(GL_TEXTURE_2D, west); + */ + glBegin(GL_QUADS); + glColor3f(red, green, blue); + switch(i) { + case 0: + glNormal3f(nx, ny, nz); + break; + case 1: + glNormal3f(0.0f, 0.0f, 1.0f); + break; + case 2: + glNormal3f(0.0f, 0.0f, -1.0f); + break; + case 3: + glNormal3f(-1.0f, 0.0f, 0.0f); + break; + case 4: + glNormal3f(1.0f, 0.0f, 0.0f); + break; + } + int jj = 0; + for (int j=0; j < 4; j++) {/* + switch(j) { + case 0: + //glNormal3f(0.0f, 0.0f, 1.0f); + break; + case 1: + //glNormal3f(0.0f, -1.0f, 0.0f); + break; + case 2: + //glNormal3f(0.0f, 1.0f, 0.0f); + break; + }*/ + //GLTEX_HELPER; + glVertex3f(slope_raw_data[which][i][j][0], + slope_raw_data[which][i][j][1], + slope_raw_data[which][i][j][2]); + } + glEnd(); + #if 0 + if (i == 0) { + glBegin(GL_LINES); + glColor3f(0.0f, 0.0f, 0.5f); + glVertex3f(0.5f, 1.0f, -0.5f); + glColor3f(1.0f, 1.0f, 1.0f); + glVertex3f(0.5f+nx, 1.0f+ny, -0.5f+nz); + glEnd(); + } + if (i == 1) { + glBegin(GL_LINES); + glColor3f(0.0f, 0.5f, 0.0f); + glVertex3f(0.5f, 0.5f, 0.0f); + glColor3f(1.0f, 1.0f, 1.0f); + glVertex3f(0.5f, 0.5f, 1.0f); + glEnd(); + } + if (i == 2) { + glBegin(GL_LINES); + glColor3f(0.5f, 0.0f, 0.0f); + glVertex3f(0.5f, 0.5f, -1.0f); + glColor3f(1.0f, 1.0f, 1.0f); + glVertex3f(0.5f, 0.5f, -2.0f); + glEnd(); + } + if (i == 3) { + glBegin(GL_LINES); + glColor3f(0.0f, 0.5f, 0.5f); + glVertex3f(0.0f, 0.5f, -0.5f); + glColor3f(1.0f, 1.0f, 1.0f); + glVertex3f(-1.0f, 0.5f, -0.5f); + glEnd(); + } + if (i == 4) { + glBegin(GL_LINES); + glColor3f(0.5f, 0.5f, 0.0f); + glVertex3f(1.0f, 0.5f, -0.5f); + glColor3f(1.0f, 1.0f, 1.0f); + glVertex3f(2.0f, 0.5f, -0.5f); + glEnd(); + } + #endif + } +} + +#if 0 +void draw_cube(float size) { + /* thanks to lesson 6 at Nehe */ + + glDisable(GL_TEXTURE_2D); + glBegin(GL_QUADS); + glColor3f(1.0f, 0.0f, 1.0f); + // Front Face + glTexCoord2f(0.0f, 0.0f); + glNormal3f(0.0f, 1.0f, 0.0f); + glVertex3f(-size, -size, size);// Bottom Left Of The Texture and Quad + glTexCoord2f(1.0f, 0.0f); + glVertex3f( size, -size, size);// Bottom Right Of The Texture and Quad + glTexCoord2f(1.0f, 1.0f); + glVertex3f( size, size, size);// Top Right Of The Texture and Quad + glTexCoord2f(0.0f, 1.0f); + glVertex3f(-size, size, size);// Top Left Of The Texture and Quad + + glColor3f(1.0f, 0.0f, 0.0f); + // Back Face + glTexCoord2f(1.0f, 0.0f); + glNormal3f(0.0f, -1.0f, 0.0f); + glVertex3f(-size, -size, -size);// Bottom Right Of The Texture and Quad + glTexCoord2f(1.0f, 1.0f); + glVertex3f(-size, size, -size);// Top Right Of The Texture and Quad + glTexCoord2f(0.0f, 1.0f); + glVertex3f( size, size, -size);// Top Left Of The Texture and Quad + glTexCoord2f(0.0f, 0.0f); + glVertex3f( size, -size, -size);// Bottom Left Of The Texture and Quad + glColor3f(1.0f, 1.0f, 1.0f); + // Top Face + glTexCoord2f(0.0f, 1.0f); + glNormal3f(0.0f, 0.0f, 1.0f); + glVertex3f(-size, size, -size);// Top Left Of The Texture and Quad + glTexCoord2f(0.0f, 0.0f); + glVertex3f(-size, size, size);// Bottom Left Of The Texture and Quad + glTexCoord2f(1.0f, 0.0f); + glVertex3f( size, size, size);// Bottom Right Of The Texture and Quad + glTexCoord2f(1.0f, 1.0f); + glVertex3f( size, size, -size);// Top Right Of The Texture and Quad + // Bottom Face + glColor3f(1.0f, 1.0f, 1.0f); + glTexCoord2f(1.0f, 1.0f); + glNormal3f(0.0f, 0.0f, -1.0f); + glVertex3f(-size, -size, -size);// Top Right Of The Texture and Quad + glTexCoord2f(0.0f, 1.0f); + glVertex3f( size, -size, -size);// Top Left Of The Texture and Quad + glTexCoord2f(0.0f, 0.0f); + glVertex3f( size, -size, size);// Bottom Left Of The Texture and Quad + glTexCoord2f(1.0f, 0.0f); + glVertex3f(-size, -size, size);// Bottom Right Of The Texture and Quad + // Right face + glColor3f(1.0f, 1.0f, 1.0f); + glTexCoord2f(1.0f, 0.0f); + glNormal3f(1.0f, 0.0f, 0.0f); + glVertex3f( size, -size, -size);// Bottom Right Of The Texture and Quad + glTexCoord2f(1.0f, 1.0f); + glVertex3f( size, size, -size);// Top Right Of The Texture and Quad + glTexCoord2f(0.0f, 1.0f); + glVertex3f( size, size, size);// Top Left Of The Texture and Quad + glTexCoord2f(0.0f, 0.0f); + glVertex3f( size, -size, size);// Bottom Left Of The Texture and Quad + // Left Face + glColor3f(1.0f, 1.0f, 1.0f); + glTexCoord2f(0.0f, 0.0f); + glNormal3f(-1.0f, 0.0f, 0.0f); + glVertex3f(-size, -size, -size);// Bottom Left Of The Texture and Quad + glTexCoord2f(1.0f, 0.0f); + glVertex3f(-size, -size, size);// Bottom Right Of The Texture and Quad + glTexCoord2f(1.0f, 1.0f); + glVertex3f(-size, size, size);// Top Right Of The Texture and Quad + glTexCoord2f(0.0f, 1.0f); + glVertex3f(-size, size, -size);// Top Left Of The Texture and Quad + glEnd(); +} +#endif + +void drawScene() { + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glLoadIdentity(); + gluLookAt(5.0f, 5.0f, 5.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f); + glTranslatef(mapPos[0], mapPos[1], 0.0f); +/* + for (int i=0; i<50; i++) { + glPushMatrix(); + glTranslatef(0.0f, float(i), 0.0f); + for (int j=0; j<50; j++) { + glTranslatef(1.0f, 0.0f, 0.0f); + float step_col = 0.0f; + glColor3f(0.2f+step_col, 0.2f+step_col, 1.0f-step_col); + PHYSFS_uint16 emptycount = map->getNumBlocksAt(j,i); + glPushMatrix(); + for(int c=0; c < 6 - emptycount; c++) { + draw_cube(0.5f); + glTranslatef(0.0f, 0.0f, 1.0f); + step_col += 0.2; + glColor3f(0.2f+step_col, 0.1f+step_col, 1.0f-step_col); + } + glPopMatrix(); + } + glPopMatrix(); + }*/ + //draw_cube(1.0f); + glRotatef(angle, 0, 1, 0); + glEnable(GL_TEXTURE_2D); + draw_slope(1.0f, slope_idx); + SDL_GL_SwapBuffers(); +} + +GLuint createGLTexture(GLsizei w, GLsizei h, const void* pixels) { + GLuint tex; + glGenTextures(1, &tex); + glBindTexture(GL_TEXTURE_2D, tex); + glEnable(GL_COLOR_MATERIAL); + //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, param); + //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, param); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + // GL_LINEAR_MIPMAP_LINEAR); + /*gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, w, + h, GL_RGB, GL_UNSIGNED_BYTE, pixels); + */ + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels); + return tex; +} + +void run_main() { + SDL_Event event; + int paused = 0; + + PHYSFS_init("mapview"); + PHYSFS_addToSearchPath(PHYSFS_getBaseDir(), 1); + +/* + SDL_Surface * tex = IMG_Load("lid.jpg"); + SDL_LockSurface(tex); + lid = createGLTexture(tex->w, tex->h, tex->pixels); + SDL_UnlockSurface(tex); + SDL_FreeSurface(tex); + tex = IMG_Load("north.jpg"); + SDL_LockSurface(tex); + north = createGLTexture(tex->w, tex->h, tex->pixels); + SDL_UnlockSurface(tex); + SDL_FreeSurface(tex); + tex = IMG_Load("south.jpg"); + SDL_LockSurface(tex); + south = createGLTexture(tex->w, tex->h, tex->pixels); + SDL_UnlockSurface(tex); + SDL_FreeSurface(tex); + tex = IMG_Load("west.jpg"); + SDL_LockSurface(tex); + west = createGLTexture(tex->w, tex->h, tex->pixels); + SDL_UnlockSurface(tex); + SDL_FreeSurface(tex); + tex = IMG_Load("east.jpg"); + SDL_LockSurface(tex); + east = createGLTexture(tex->w, tex->h, tex->pixels); + SDL_UnlockSurface(tex); + SDL_FreeSurface(tex); + */ + + //glDisable(GL_COLOR_MATERIAL); + + glCullFace(GL_BACK); + //glEnable(GL_CULL_FACE); + //map = new OpenGTA::Map("NYC.CMP"); + + while(!global_Done && !global_EC) { + while (SDL_PollEvent(&event)) { + switch(event.type) { + case SDL_ACTIVEEVENT: + if (event.active.gain == 0) + paused = 1; + else + paused = 0; + break; + case SDL_KEYDOWN: + handleKeyPress(&event.key.keysym); + break; + case SDL_VIDEORESIZE: + screen = SDL_SetVideoMode( event.resize.w, + event.resize.h, 32, videoFlags ); + if (!screen) + ERROR("Failed to set video mode after resize event"); + resize(event.resize.w, event.resize.h); + break; + case SDL_QUIT: + global_Done = 1; + break; + default: + break; + } + } + if (!paused) + drawScene(); + } + glDeleteTextures(1, &lid); + glDeleteTextures(1, &north); + glDeleteTextures(1, &south); + glDeleteTextures(1, &west); + glDeleteTextures(1, &east); + +} diff --git a/tools/doxy_doc.sh b/tools/doxy_doc.sh new file mode 100755 index 0000000..61cf2d1 --- /dev/null +++ b/tools/doxy_doc.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +in_file=doc/hacking.txt +out_file=doxy_main.h + +function makeM4() { +sed -e "s/ ==*$/', \`/" \ + -e 's/^== /MAINPAGE(`/' \ + -e "s/^= \([^ ]*\)/') \\ +PAGE(\`\\1 \\1/" \ + $in_file +echo "')" +} + +function convertM4_Doxygen() { + m4 -DBEGIN_CPP_COMMENT='/*!' -DEND_CPP_COMMENT='*/' -DMAINPAGE='BEGIN_CPP_COMMENT \mainpage $1 +$2 +END_CPP_COMMENT' -DPAGE='BEGIN_CPP_COMMENT \page $1 +$2 +END_CPP_COMMENT' -D_='$1' - +} + +makeM4 | convertM4_Doxygen diff --git a/tools/gen_texcoords.c b/tools/gen_texcoords.c new file mode 100644 index 0000000..5cb533c --- /dev/null +++ b/tools/gen_texcoords.c @@ -0,0 +1,52 @@ +#include + + float slope_raw_data[45][5][4][3] = { +#include "slope1_data.h" + }; + +int main(int argc, char* argv[]) { + int slope_type, face_num, vertex; + int tex_x, tex_y; + + for (slope_type = 0 ; slope_type < 45; slope_type++) { + printf("{ // slope: %i\n", slope_type); + for (face_num = 1; face_num < 5; face_num++) { + switch (face_num) { + case 0: + printf(" { // lid\n"); + tex_x = 0; + tex_y = 2; + break; + case 1: + printf(" { // north\n"); + tex_x = 0; + tex_y = 1; + break; + case 2: + printf(" { // south\n"); + tex_x = 0; + tex_y = 1; + break; + case 3: + printf(" { // west\n"); + tex_x = 2; + tex_y = 1; + break; + case 4: + printf(" { // east\n"); + tex_x = 2; + tex_y = 1; + break; + } + for (vertex = 0; vertex < 4; vertex++) { + float x_tmp = ((face_num == 1) || (face_num == 3)) ? slope_raw_data[slope_type][face_num][vertex][tex_x] : + 1.0f - slope_raw_data[slope_type][face_num][vertex][tex_x]; + printf(" { %.2f, %.2f %s\n", x_tmp, + 1.0f-slope_raw_data[slope_type][face_num][vertex][tex_y], + (vertex == 3) ? "}" : "},"); + } + printf(" %s\n", (face_num == 4) ? "}" : "},"); + } + printf("%s\n", (slope_type == 44) ? "}" : "},"); + } +} diff --git a/tools/insert_copyright.sh b/tools/insert_copyright.sh new file mode 100755 index 0000000..5721a4d --- /dev/null +++ b/tools/insert_copyright.sh @@ -0,0 +1,11 @@ +#!/bin/bash +copyright_file=licenses/zlib_header.txt +if [ $# -gt 0 ]; then + input=$1 + if [ $# -eq 2 ]; then + copyright_file=$2 + fi +fi +sed '1{h; r $1 + D; } +2{x; G; }' $copyright_file $input diff --git a/tools/mapinfo.cpp b/tools/mapinfo.cpp new file mode 100644 index 0000000..8f496c4 --- /dev/null +++ b/tools/mapinfo.cpp @@ -0,0 +1,76 @@ +#include +#include "opengta.h" +#include "navdata.h" +#include "log.h" + +int main(int argc, char* argv[]) { + int x = 0; + int y = 0; + + if (argc < 2) { + std::cerr << "USAGE: mapinfo map_filename [x y]" << std::endl; + return 1; + } + + PHYSFS_init(argv[0]); + PHYSFS_addToSearchPath(PHYSFS_getBaseDir(), 1); + + std::string map_filename(argv[1]); + OpenGTA::Map map(map_filename); + + if (argc == 4) { + x = atoi(argv[2]); + y = atoi(argv[3]); + } + if (x < 0) + x = 0; + if (y < 0) + y = 0; + INFO << "Querying position: " << x << ", " << y << std::endl; + + OpenGTA::NavData::Sector* sec = map.nav->getSectorAt(x, y); + + INFO << "* " << sec->name << " *" << std::endl; + + PHYSFS_uint16 num_blocks = map.getNumBlocksAt(x, y); + OpenGTA::Map::BlockInfo* bi = NULL; + INFO << num_blocks << " empty blocks" << std::endl; + for (int c=6-num_blocks; c >= 1; c--) { + std::cout << "block " << c << std::endl; + bi = map.getBlockAt(x, y, c); + assert(bi); + std::cout << "moves: "<< int(bi->upOk()) << ", " << int(bi->downOk()) << ", " + << int(bi->leftOk()) << ", " << int(bi->rightOk()) << std::endl; + std::cout << "type: " << int(bi->blockType()) << " flat: " << + int(bi->isFlat()) << " slope-type: " << int(bi->slopeType()) << + " rot: " << int(bi->rotation()) << " remap idx: " << + int(bi->remapIndex()) << std::endl; + std::cout << "textures: left " << int(bi->left) << " right " << int(bi->right) << + " top " << int(bi->top) << " bottom " << int(bi->bottom) << " lid " << int(bi->lid) + << " flip t-b: " << int(bi->flipTopBottom()) << " flip l-r: " << + int(bi->flipLeftRight()) << std::endl; + + int v_count = 0; + if (bi->lid) + v_count += 3*4; + if (bi->isFlat()) { + if (bi->top) + v_count += 3*4*2; + if (bi->left) + v_count += 3*4*2; + } + else { + if (bi->left) + v_count += 3*4; + if (bi->right) + v_count += 3*4; + if (bi->top) + v_count += 3*4; + if (bi->bottom) + v_count += 3*4; + } + } + + PHYSFS_deinit(); + return 0; +} diff --git a/tools/minimap.cpp b/tools/minimap.cpp new file mode 100644 index 0000000..9e0abeb --- /dev/null +++ b/tools/minimap.cpp @@ -0,0 +1,105 @@ +#include +#include "opengta.h" +#include "log.h" + +int main(int argc, char* argv[]) { + + if (argc < 2) { + std::cerr << "USAGE: minimap map_filename" << std::endl; + std::cerr << "saves map as 'out.bmp'" << std::endl; + return 1; + } + + PHYSFS_init(argv[0]); + PHYSFS_addToSearchPath(PHYSFS_getBaseDir(), 1); + PHYSFS_addToSearchPath("gtadata.zip", 1); + + std::string map_filename(argv[1]); + OpenGTA::Map map(map_filename); + + uint32_t green = 0x00dd00ff; + uint32_t red = 0xdd0000ff; + uint32_t blue = 0x0000ddff; + + uint32_t field = green; + uint32_t building = 0xdf9f6fff; + uint32_t water = blue; + uint32_t road = 0xdadadaff; + uint32_t pavement = 0x8a9aa0ff; + + uint32_t map_color[] = { + 0x000000ff, + water, + road, + pavement, + field, + building, + 0xffffffff, + 0xff0000ff + }; + + + SDL_Init(SDL_INIT_VIDEO); + +// FIXME: doesn't work right +#if SDL_BYTEORDER == SDL_BIG_ENDIAN +#define rmask 0xff000000 +#define gmask 0x00ff0000 +#define bmask 0x0000ff00 +#define amask 0x000000ff +#else +#define rmask 0x000000ff +#define gmask 0x0000ff00 +#define bmask 0x00ff0000 +#define amask 0xff000000 +#endif + + SDL_Surface* surface = SDL_CreateRGBSurface(SDL_SWSURFACE|SDL_SRCALPHA, + 256, 256, 32,// rmask, gmask, bmask, amask); + 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff); + SDL_LockSurface(surface); + uint32_t* dst = static_cast(surface->pixels); + + for (int i = 0; i < 256; i++) { + for (int j = 0; j < 256; j++) { + PHYSFS_uint16 emptycount = map.getNumBlocksAt(j,i); + int found_type = 0; + for (int c=6-emptycount; c > 0; c--) { + OpenGTA::Map::BlockInfo* bi = map.getBlockAt(j, i, c); + /* + if (bi->blockType() > 0) + found_type = bi->blockType(); + */ + + /* + if (bi->railway()) + found_type = 2; + if (bi->railStation()) + found_type = 0; + if (bi->railStationTrain()) + std::cout << " train: " << i << ", " << j << std::endl; + if (bi->railStartTurn()) + std::cout << " start: " << i << ", " << j << std::endl; + if (bi->railEndTurn()) + std::cout << " end: " << i << ", " << j << std::endl; + */ + if (bi->blockType() > 0) + found_type = bi->blockType(); + if (bi->trafficLights()) + found_type = 0; + if (bi->railway()) + found_type = 7; + } + if (found_type) + *dst = map_color[found_type]; + else + *dst = 0x000000ff; + dst++; + } + } + SDL_UnlockSurface(surface); + SDL_SaveBMP(surface, "out.bmp"); + + SDL_Quit(); + return 0; +} diff --git a/tools/obj_dump.cpp b/tools/obj_dump.cpp new file mode 100644 index 0000000..70bff21 --- /dev/null +++ b/tools/obj_dump.cpp @@ -0,0 +1,92 @@ +#include +#include +#include "opengta.h" + +extern int global_EC; +extern int global_Done; +std::string map_file("undefined_map_file"); +std::string style_file("undefined_style_file"); + +void on_exit() { + PHYSFS_deinit(); + if (global_EC) + std::cerr << "Exiting after fatal problem - please see output above" << std::endl; +} + +void parse_args(int argc, char* argv[]) { +#ifdef DUMP_OBJ_IN_MAP + if (argc != 3) { + std::cout << "USAGE: " << argv[0] << " map-file 8-bit-style-file" << std::endl; + exit(1); + } + map_file = argv[1]; + style_file = argv[2]; +#else + if (argc != 2) { + std::cout << "USAGE: " << argv[0] << " map-file 8-bit-style-file" << std::endl; + exit(1); + } + map_file = ""; + style_file = argv[1]; +#endif + +} + + +void run_init() { + PHYSFS_init("mapview"); + PHYSFS_addToSearchPath(PHYSFS_getBaseDir(), 1); + PHYSFS_addToSearchPath("gtadata.zip", 1); +} + +#ifdef DUMP_OBJ_IN_MAP +void run_main() { + OpenGTA::Map map(map_file); + OpenGTA::Graphics8Bit style(style_file); + + for (int i = 0; i < map.numObjects; i++) { + OpenGTA::Map::ObjectPosition & o = map.objects[i]; + std::cout << "object: " << i << std::endl; + std::cout << o.x << ", " << o.y << ", " << o.z << " rot: " << o.rotation << + " pitch: " << o.pitch << " roll: " << o.roll << std::endl; + std::cout << "type: " << int(o.type); + if (o.remap >= 128) { + std::cout << " (a car) remap: " << int(o.remap - 128) << std::endl; + OpenGTA::GraphicsBase::CarInfo * info = style.findCarByModel(o.type); + assert(info); + std::cout << "width: " << info->width << " height: "<< info->height << + " depth " << info->depth << " sprnum: " << info->sprNum << " weight: " << info->weightDescriptor + << std::endl; + } + else { + std::cout << " (an obj) remap: " << int(o.remap) << std::endl; + OpenGTA::GraphicsBase::ObjectInfo * info = style.objectInfos[o.type]; + std::cout << "width: " << info->width << " height: "<< info->height << + " depth " << info->depth << " sprnum: " << info->sprNum << " weight: " << info->weight + << " aux: " << info->aux << " status: " << int(info->status) << " n-into: " << int(info->numInto) << std::endl; + } + std::cout << std::endl; + } +} + +#else +// dump object/sprite-infos +void run_main() { + OpenGTA::Graphics8Bit style(style_file); + std::cout << "DUMP_OBJ_INFO BEGIN" << std::endl; + for (size_t i = 0; i < style.objectInfos.size(); ++i) { + std::cout << "obj-type: " << i << " width: " << style.objectInfos[i]->width << " height: " + << style.objectInfos[i]->height << " depth: " << style.objectInfos[i]->depth << + " spr-num: " << style.objectInfos[i]->sprNum << " reindex: " << style.spriteNumbers.reIndex(style.objectInfos[i]->sprNum, OpenGTA::GraphicsBase::SpriteNumbers::OBJECT) << " weight: " << style.objectInfos[i]->weight << " aux: " << + style.objectInfos[i]->aux << " status: " << int(style.objectInfos[i]->status) << " num-into: " << + int(style.objectInfos[i]->numInto) << std::endl; + } + std::cout << "DUMP_OBJ_INFO END" << std::endl; + for (size_t i = 0; i < style.spriteInfos.size(); ++i) { + std::cout << "sprite: " << i << " width: " << int(style.spriteInfos[i]->w) << " height: " << + int(style.spriteInfos[i]->h) << " delta-count: " << int(style.spriteInfos[i]->deltaCount)<< std::endl; + } + +} + +#endif diff --git a/tools/replace_in_files.sh b/tools/replace_in_files.sh new file mode 100755 index 0000000..b19bea7 --- /dev/null +++ b/tools/replace_in_files.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +sed_term="$1" +shift +while [ $# -gt 0 ]; do + cp $1 $1.bak + sed -e "$sed_term" $1.bak >${1} + rm $1.bak + shift +done diff --git a/tools/resort_quads.c b/tools/resort_quads.c new file mode 100644 index 0000000..16f2ebf --- /dev/null +++ b/tools/resort_quads.c @@ -0,0 +1,70 @@ +#include + + float slope_raw_data[45][5][4][3] = { +#include "slope1_data.h" + }; + +int main(int argc, char* argv[]) { + int slope_type, face_num, vertex; + int tex_x, tex_y; + + for (slope_type = 0 ; slope_type < 45; slope_type++) { + printf("{ // slope: %i\n", slope_type); + for (face_num = 0; face_num < 5; face_num++) { + switch (face_num) { + case 0: + printf(" { // lid\n"); + tex_x = 0; + tex_y = 2; + break; + case 1: + printf(" { // north\n"); + tex_x = 0; + tex_y = 1; + break; + case 2: + printf(" { // south\n"); + tex_x = 0; + tex_y = 1; + break; + case 3: + printf(" { // west\n"); + tex_x = 2; + tex_y = 1; + break; + case 4: + printf(" { // east\n"); + tex_x = 2; + tex_y = 1; + break; + } + int face_lower_left_found = 0; + int face_lower_left_is = -1; + for (vertex = 0; vertex < 3; vertex++) { + if ((slope_raw_data[slope_type][face_num][vertex][tex_x] == + slope_raw_data[slope_type][face_num][vertex+1][tex_x]) && + (slope_raw_data[slope_type][face_num][vertex][tex_y] == + slope_raw_data[slope_type][face_num][vertex+1][tex_y])) + printf("// degenerate face\n"); + } + for (vertex = 0; vertex < 4; vertex++) { + char* c; + if (slope_raw_data[slope_type][face_num][vertex][tex_x] == 0.0f && + slope_raw_data[slope_type][face_num][vertex][tex_y] == 0.0f) { + c = "// *"; + face_lower_left_found += 1; + face_lower_left_is = vertex; + } + else + c = ""; + printf(" { %.2f, %.2f %.2f %s %s\n", slope_raw_data[slope_type][face_num][vertex][0], + slope_raw_data[slope_type][face_num][vertex][1], + slope_raw_data[slope_type][face_num][vertex][2], + (vertex == 3) ? "}" : "},", c); + } + printf("// %i lower faces; last: %i\n", face_lower_left_found, face_lower_left_is); + printf(" %s\n", (face_num == 4) ? "}" : "},"); + } + printf("%s\n", (slope_type == 44) ? "}" : "},"); + } +} diff --git a/tools/slope_conv.awk b/tools/slope_conv.awk new file mode 100644 index 0000000..a9c2273 --- /dev/null +++ b/tools/slope_conv.awk @@ -0,0 +1,91 @@ +BEGIN { + slope_idx = -1; + mode_seek_slope_hdr = 0; + mode_seek_type = 1; + mode_seek_modifier = 2; + mode_seek_vertices = 3; + num_vertices_read = 0; + first_pass_flag = 0; + reverse_flag = 0; + till_block_complete = -1; + mode = mode_seek_slope_hdr; +} + +{ + if (mode == mode_seek_slope_hdr) { + if ($1 == "Slope:") { + #print "** slope start " $2; + print "{ // slope: " $2; + mode = mode_seek_type; + slope_idx = $2; + till_block_complete = 5; + next; + } + } + if (mode == mode_seek_type) { + if ($1 == "LID" || $1 == "NORTH" || $1 == "SOUTH" || $1 == "WEST" || $1 == "EAST") { + #print "* type " $1; + last_type = $1; + mode = mode_seek_modifier; + first_pass_flag = 0; + reverse_flag = 0; + num_vertices_read = 0; + till_block_complete = till_block_complete - 1; + next; + } + } + if (mode == mode_seek_modifier) { + first_pass_flag = $1; + reverse_flag = $2; + mode = mode_seek_vertices; + #print "* modifier: " first_pass_flag " " reverse_flag; + next; + } + if (mode == mode_seek_vertices) { + gsub(",", ".", $0); + gsub(". ", ", ", $0); + #print "v (" num_vertices_read "): " $0; + + # FIX: switch y<->z order + gsub(",", "", $2); + # offset y coord + $2 = $2 + 1 + vertices[num_vertices_read] = $1 " " $3 ", " $2; + #vertices[num_vertices_read] = $1 " " $2 ".00, " $3; # cheat .00 back + + num_vertices_read = num_vertices_read + 1; + if (num_vertices_read == 4) { + mode = mode_seek_type; + #print "* end slope"; + print " { // " last_type; + if (reverse_flag == 0) { + for (v=3; v > 0; v--) { + print " { " vertices[v] " },"; + } + print " { " vertices[0] " }"; + if (last_type == "EAST") { + print " }"; + } + else { + print " },"; + } + } + else { + for (v=3; v >= 0; v--) { + print vertices[v]; + } + } + if (till_block_complete == 0) { + # print "** end block" + if (slope_idx == 44) { + print "}"; + } + else { + print "},"; + } + mode = mode_seek_slope_hdr; + } + } + next; + } +} diff --git a/tools/slope_exchange.sh b/tools/slope_exchange.sh new file mode 100755 index 0000000..2dccb14 --- /dev/null +++ b/tools/slope_exchange.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +INPUT=slope1_data.h + +function fetch_data () { + local k=$1 + sed -n "/{ \/\/ slope: $k$/,/^\},/p" $INPUT +} + +fetch_data 0 +fetch_data 3 +fetch_data 4 +fetch_data 1 +fetch_data 2 +for i in `seq 5 8`; do + fetch_data $i +done +for i in `seq 17 24`; do + fetch_data $i +done +for i in `seq 9 16`; do + fetch_data $i +done +for i in `seq 25 40`; do + fetch_data $i +done +fetch_data 42 +fetch_data 41 +fetch_data 43 +fetch_data 44 diff --git a/tools/style_demo.sh b/tools/style_demo.sh new file mode 100755 index 0000000..22eccad --- /dev/null +++ b/tools/style_demo.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +cat < + +

side textures of style001.g24 - slightly wrong palette

+EOF + +for idx in `seq 0 50`; do + clut=$(./g24 STYLE001.G24 $idx style1_side${idx}.bmp | grep clut-idx) + convert style1_side${idx}.bmp style1_side${idx}.png + rm style1_side${idx}.bmp + echo "


style 001, side $idx ; found $clut

" +done + +cat < + diff --git a/util/animation.cpp b/util/animation.cpp new file mode 100644 index 0000000..fa96970 --- /dev/null +++ b/util/animation.cpp @@ -0,0 +1,94 @@ +#include +#include "animation.h" +#include "m_exceptions.h" + +namespace Util { + Animation::Animation(uint16_t num, uint16_t fps) : + callback() { + status = STOPPED; + numFrames = num; + currentFrame = 0; + delay = 1000 / fps; + lastChangeTicks = 0; + } + + Animation::Animation(const Animation & other) { + status = other.status; + onDone = other.onDone; + numFrames = other.numFrames; + currentFrame = other.currentFrame; + delay = other.delay; + + lastChangeTicks = other.lastChangeTicks; + callback = other.callback; + } + + void Animation::update(const uint32_t & nowTicks) { + if (status == STOPPED) + return; + if (lastChangeTicks == 0) + lastChangeTicks = nowTicks; + if (nowTicks < lastChangeTicks + delay) + return; + lastChangeTicks = nowTicks; + if (status == PLAY_FORWARD) + flipFrame(true); + else if (status == PLAY_BACKWARD) + flipFrame(false); + } + + void Animation::flipFrame(bool forward = true) { + switch(forward) { + case true: + if (currentFrame < numFrames - 1) + ++currentFrame; + else if (currentFrame == numFrames - 1) + isDone(); + break; + case false: + if (currentFrame == 0) + isDone(); + else + --currentFrame; + } + } + + void Animation::jumpToFrame(const uint16_t num, const Status andDo) { + if (num >= numFrames) { + std::ostringstream o; + o << num << " >= " << numFrames; + throw E_OUTOFRANGE(o.str()); + } + currentFrame = num; + status = andDo; + } + + void Animation::isDone() { + if (onDone == STOP) { + status = STOPPED; + return; + } + if (onDone == REVERSE) { + status = (status == PLAY_FORWARD) ? PLAY_BACKWARD : PLAY_FORWARD; + return; + } + if (onDone == LOOP) { + if (status == PLAY_FORWARD) + jumpToFrame(0, PLAY_FORWARD); + else if (status == PLAY_BACKWARD) + jumpToFrame(numFrames - 1, PLAY_BACKWARD); + return; + } + status = STOPPED; + if (onDone == FCALLBACK) { + if (callback) + callback(); + else + ERROR << "Wanted to call callback, but nobody was there" << std::endl; + } + } +} + +void AnimCallback() { + WARN << "EmptyAnimCallback called" << std::endl; +} diff --git a/util/animation.h b/util/animation.h new file mode 100644 index 0000000..089bf48 --- /dev/null +++ b/util/animation.h @@ -0,0 +1,47 @@ +#ifndef UTIL_ANIMATION_H +#define UTIL_ANIMATION_H +#include +#include "Functor.h" +#include "log.h" + +namespace Util { + + class Animation { + public: + typedef enum { + STOPPED = 0, + PLAY_FORWARD, + PLAY_BACKWARD + } Status; + typedef enum { + STOP = 0, + REVERSE, + LOOP, + FCALLBACK + } OnDone; + Animation(uint16_t numFrames, uint16_t fps); + Animation(const Animation & o); + inline const uint16_t & getCurrentFrameNumber() { return currentFrame; } + inline void set(const Status doThis, const OnDone done = STOP) { status = doThis; onDone = done; } + inline const Status & get() { return status; } + void jumpToFrame(const uint16_t num, const Status andDo); + void update(const uint32_t & nowTicks); + typedef Loki::Functor CallbackType; + void setCallback(CallbackType & cb) { callback = cb; } + + uint16_t currentFrame; + uint16_t numFrames; + uint32_t delay; + protected: + + void flipFrame(bool forward); + void isDone(); + Status status; + OnDone onDone; + uint32_t lastChangeTicks; + + CallbackType callback; + }; +} + +#endif diff --git a/util/buffercache.cpp b/util/buffercache.cpp new file mode 100644 index 0000000..4dc1ca7 --- /dev/null +++ b/util/buffercache.cpp @@ -0,0 +1,147 @@ +/************************************************************************ +* Copyright (c) 2005-2006 tok@openlinux.org.uk * +* * +* This software is provided as-is, without any express or implied * +* warranty. In no event will the authors be held liable for any * +* damages arising from the use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute * +* it freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must * +* not claim that you wrote the original software. If you use this * +* software in a product, an acknowledgment in the product documentation * +* would be appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must * +* not be misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source * +* distribution. * +************************************************************************/ +#include +#include +#include +#include +#include "buffercache.h" +#include "log.h" +#include "m_exceptions.h" + +namespace Util { + + BufferCache::BufferCache() { + total_bytes = 0; + } + BufferCache::~BufferCache() { + BufferMap_t::const_iterator i = allocated.begin(); + while (i != allocated.end()) { + freeBuffer(i); + i++; + } + allocated.clear(); + BufferMap_T::const_iterator j = locked.begin(); + if (locked.size()) + WARN << "Locked elements remaining in cache: " << locked.size() << std::endl; + while (j != locked.end()) { + delete [] j->first; + j++; + } + } + + unsigned char* BufferCache::requestBuffer(unsigned int len) { + unsigned char *result = NULL; + BufferMap_t::const_iterator i = findMatchingBuffer(len); + if (i == allocated.end()) { + INFO << "No matching buffer for size: " << len << std::endl; + result = createBuffer(len); + if (result == NULL) + throw E_OUTOFMEMORY("Failed to allocate buffer"); + //throw std::string("Error: out of memory"); + allocated[len] =result; + printStatus(); + } + else { + result = i->second; + } + std::memset(result, 0, len); + return result; + } + + unsigned char* BufferCache::requestLockedBuffer(unsigned int len) { + unsigned char *result = requestBuffer(len); + lockBuffer(result); + return result; + } + + void BufferCache::lockBuffer(unsigned char* tb) { + BufferMap_t::const_iterator i = allocated.begin(); + while (i != allocated.end()) { + if (i->second == tb) { + locked[i->second] = i->first; + allocated.erase(i->first); + return; + } + ++i; + } + std::ostringstream o; + o << "Cannot lock unknown buffer " << tb; + throw E_UNKNOWNKEY(o.str()); + //throw std::string("Unknown buffer - cannot lock it"); + } + + void BufferCache::unlockBuffer(unsigned char* tb) { + BufferMap_T::const_iterator i = locked.find(tb); + if (i == locked.end()) { + std::ostringstream o; + o << "Cannot unlock unknown buffer " << tb; + throw E_UNKNOWNKEY(o.str()); + //throw std::string("Unknow buffer - cannot unlock it"); + } + allocated[i->second] = i->first; + locked.erase(i->first); + } + + BufferCache::BufferMap_t::const_iterator BufferCache::findMatchingBuffer(unsigned int len) { + BufferMap_t::const_iterator i = allocated.find(len); + if (i != allocated.end()) + return i; + i = allocated.begin(); + while (i != allocated.end()) { + if (i->first > len) + return i; + ++i; + } + return allocated.end(); + } + + void BufferCache::printStatus() { + INFO << "status: " << allocated.size() << " allocated " << + locked.size() << " locked buffers " << total_bytes << + " bytes" << std::endl; + } + + unsigned char* BufferCache::createBuffer(unsigned int len) { + total_bytes += len; + return new unsigned char[len]; + } + + void BufferCache::freeBuffer(BufferMap_t::const_iterator pos) { + delete [] pos->second; + total_bytes -= pos->first; + } + + BufferCache::LockedBuffer::LockedBuffer(unsigned char *b) { + assert(b); + buf_p = b; + } + + BufferCache::LockedBuffer::LockedBuffer(unsigned int len) { + buf_p = BufferCacheHolder::Instance().requestLockedBuffer(len); + } + + BufferCache::LockedBuffer::~LockedBuffer() { + BufferCacheHolder::Instance().unlockBuffer(buf_p); + } + +} diff --git a/util/buffercache.h b/util/buffercache.h new file mode 100644 index 0000000..cb8e7fb --- /dev/null +++ b/util/buffercache.h @@ -0,0 +1,116 @@ +/************************************************************************ +* Copyright (c) 2005-2006 tok@openlinux.org.uk * +* * +* This software is provided as-is, without any express or implied * +* warranty. In no event will the authors be held liable for any * +* damages arising from the use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute * +* it freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must * +* not claim that you wrote the original software. If you use this * +* software in a product, an acknowledgment in the product documentation * +* would be appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must * +* not be misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source * +* distribution. * +************************************************************************/ +#ifndef BUFFER_CACHE_H +#define BUFFER_CACHE_H +#include +#include "Singleton.h" + +using namespace Loki; + +namespace Util { + + /** A simple memory-buffer pool. + * + * - You should never manually delete a buffer created through the cache. + * - You should however \e unlock buffers that were locked once you + * no longer need them. + * - You don't need to check for NULL pointers; exceptions are used for + * errors. + * - Buffers are initialised with zeroes. + * + * Beware: + * \code + * // assuming the cache is empty + * + * a = requestBuffer(10) + * b = requestBuffer(5) + * + * a == b + * ---------------------- + * a = requestBuffer(5) + * b = requestBuffer(6) + * + * a != b + * \endcode + */ + class BufferCache { + public: + BufferCache(); + ~BufferCache(); + + /** Returns a buffer of (at least) len bytes. + * + * @param Length of requested buffer in bytes. + * @return The buffer. + * @note May throw a OutOfMemory exception. + */ + unsigned char* requestBuffer(unsigned int len); + /** Returns a locked buffer of (at least) len bytes. + * Just a convenience function. + * + * @see requestBuffer + * @see lockBuffer + */ + unsigned char* requestLockedBuffer(unsigned int len); + /** Lock a buffer (which was allocated by this cache). + * + * @param Pointer to the buffer. + * + * You need this when you request multiple buffers at the same time; lock + * the current buffer before requesting the next one. + */ + void lockBuffer(unsigned char* tb); + /** Unlock a buffer (which was previously locked). + * @param Pointer to the buffer. + * + * Returns the buffer to the shared pool. + */ + void unlockBuffer(unsigned char* tb); + class LockedBuffer { + public: + LockedBuffer(unsigned char* b); + LockedBuffer(unsigned int len); + ~LockedBuffer(); + inline unsigned char* operator()() { return buf_p; } + private: + unsigned char* buf_p; + }; + void printStatus(); + private: + typedef std::map BufferMap_t; + typedef std::map BufferMap_T; + BufferMap_t allocated; + BufferMap_T locked; + BufferMap_t::const_iterator findMatchingBuffer(unsigned int len); + BufferMap_t::iterator findLockedBuffer(unsigned char* tb); + unsigned char* createBuffer(unsigned int len); + void freeBuffer(BufferMap_t::const_iterator pos); + unsigned int total_bytes; + }; + + /** Instance of BufferCache. + */ + typedef SingletonHolder BufferCacheHolder; +} +#endif diff --git a/util/cistring.h b/util/cistring.h new file mode 100644 index 0000000..90348f8 --- /dev/null +++ b/util/cistring.h @@ -0,0 +1,43 @@ +#ifndef CASE_INSENSITIVE_STRING_H +#define CASE_INSENSITIVE_STRING_H + +namespace Util { + + static int memicmp (const char *s, const char *t, int n) { + int r = 0; + while (n-- > 0 && (r = toupper (*s++) - toupper (*t++)) == 0); + return (r); + } + + struct ci_char_traits : public std::char_traits + { + static bool eq( char c1, char c2) + { + return toupper(c1) == toupper(c2); + } + static bool lt( char c1, char c2) + { + return toupper(c1) < toupper(c2); + } + static int compare( const char *s1, const char *s2, size_t n) + { + return memicmp( s1, s2, n); // non-standard ! + } + static const char *find( const char *s, int n, char ch) + { + while ( n-- > 0 && toupper(*s) != toupper(ch) ) + { + ++s; + } + return n > 0 ? s : 0; + } + }; + + /** case-insensitive compare of std::string. + * + * Can't remember where I found this, probably derived from here + * http://www.gotw.ca/gotw/029.htm anyway. + */ + typedef std::basic_string ci_string; +} +#endif diff --git a/util/file_helper.cpp b/util/file_helper.cpp new file mode 100644 index 0000000..f2aed71 --- /dev/null +++ b/util/file_helper.cpp @@ -0,0 +1,86 @@ +#include +#include +#include +#include "m_exceptions.h" +#include "file_helper.h" + +#ifndef OGTA_DEFAULT_DATA_PATH +#define OGTA_DEFAULT_DATA_PATH "gtadata.zip" +#endif +#ifndef OGTA_DEFAULT_MOD_PATH +#define OGTA_DEFAULT_MOD_PATH "" +#endif +#ifndef OGTA_DEFAULT_HOME_PATH +#ifdef LINUX +#define OGTA_DEFAULT_HOME_PATH PHYSFS_getUserDir() +#elif WIN32 +#define OGTA_DEFAULT_HOME_PATH "config" +#endif +#endif + +namespace Util { + FileHelper::FileHelper() : + baseDataPath(OGTA_DEFAULT_DATA_PATH), + modDataPath(OGTA_DEFAULT_MOD_PATH), + userHomeDir(OGTA_DEFAULT_HOME_PATH) { + const char *e = getenv("OGTA_DATA"); + if (e != NULL) + baseDataPath = std::string(e); + e = getenv("OGTA_MOD"); + if (e != NULL) + modDataPath = std::string(e); + e = getenv("OGTA_HOME"); + if (e != NULL) + userHomeDir = std::string(e); + } + + const std::string & FileHelper::getBaseDataPath() const { + return baseDataPath; + } + + const std::string & FileHelper::getModDataPath() const { + return modDataPath; + } + + const std::string & FileHelper::getUserHomeDir() const { + return userHomeDir; + } + + bool FileHelper::existsInSystemFS(const std::string & file) const { + if (file.length() == 0) + return 0; +#ifdef LINUX + return (access(file.c_str(), R_OK) == 0); +#endif +#ifdef WIN32 + FILE * f = fopen(file.c_str(), "rb"); + bool res = (f != NULL); + if (f) + fclose(f); + return res; +#endif + } + + bool FileHelper::existsInVFS(const std::string & file) const { + if (file.length() == 0) + return 0; + return PHYSFS_exists(file.c_str()); + } + + PHYSFS_file *FileHelper::openReadVFS(const std::string & file) const { + PHYSFS_file * fd = PHYSFS_openRead(file.c_str()); + if (fd) + return fd; + // try lower case + std::string name2(file); + transform(name2.begin(), name2.end(), name2.begin(), tolower); + fd = PHYSFS_openRead(name2.c_str()); + if (!fd) { // still no joy, give up + std::ostringstream o; + o << file << " with error: " << PHYSFS_getLastError(); + throw E_FILENOTFOUND(o.str()); + } + // take this one instead + return fd; + } +} diff --git a/util/file_helper.h b/util/file_helper.h new file mode 100644 index 0000000..f5f0866 --- /dev/null +++ b/util/file_helper.h @@ -0,0 +1,31 @@ +#ifndef UTIL_FILEHELPER_H +#define UTIL_FILEHELPER_H + +#include +#include +#include "Singleton.h" + +namespace Util { + // central path storage + // Note: assumes that physfs is ready when instance is created + class FileHelper { + public: + FileHelper(); + const std::string & getBaseDataPath() const; + const std::string & getModDataPath() const; + const std::string & getUserHomeDir() const; + bool existsInSystemFS(const std::string & file) const; + bool existsInVFS(const std::string & file) const; + PHYSFS_file* openReadVFS(const std::string & file) const; + private: + std::string baseDataPath; + std::string modDataPath; + std::string userHomeDir; + }; + + typedef Loki::SingletonHolder FileHelperHolder; +} +#define GET_FILE_HELPER Util::FileHelperHolder::Instance() + +#endif diff --git a/util/gui.h b/util/gui.h new file mode 100644 index 0000000..c5cebc0 --- /dev/null +++ b/util/gui.h @@ -0,0 +1,125 @@ +#ifndef UTIL_GUI_H +#define UTIL_GUI_H +#include +#include +#include +#include +#include "animation.h" +#include "gl_pagedtexture.h" +#include "image_loader.h" +#include "font_cache.h" + +namespace GUI { + struct Object; + class Animation; + + class Manager { + public: + Manager() {} + ~Manager(); + void add(Object * obj, uint8_t onLevel); + void remove(Object * obj); + void removeById(size_t id); + Object* findObject(const size_t id); + void draw(); + void clearObjects(); + void clearCache(); + void cacheImageRAW(const std::string & file, size_t asId); + void cacheImageRAT(const std::string & file, const std::string & palette, size_t asId); + void cacheImageSDL(const std::string & file, size_t asId); + ImageUtil::WidthHeightPair cacheStyleArrowSprite(const size_t id, int remap); + const OpenGL::PagedTexture & getCachedImage(size_t Id); + void receive(SDL_MouseButtonEvent & mb_event); + Animation* findAnimation(uint16_t id); + void createAnimation(const std::vector & indices, uint16_t fps, size_t asAnimId); + void update(uint32_t nowTicks); + private: + bool isInside(Object & o, Uint16 x, Uint16 y) const; + typedef std::map< size_t, OpenGL::PagedTexture > GuiTextureCache; + GuiTextureCache::iterator findByCacheId(const size_t & Id); + + typedef std::map AnimationMap; + AnimationMap guiAnimations; + typedef std::list GuiObjectList; + typedef std::map< uint8_t, GuiObjectList > GuiObjectListMap; + GuiObjectListMap::iterator findLayer(uint8_t l); + GuiObjectListMap guiLayers; + GuiTextureCache texCache; + + }; + + class Animation : public Util::Animation { + public: + Animation(const std::vector & _indices, const uint16_t fps) : + Util::Animation(_indices.size(), fps), + indices(_indices) {} + std::vector indices; + uint16_t getCurrentFrame(); + }; + + struct Object { + Object(const SDL_Rect & r); + Object(const size_t Id, const SDL_Rect & r); + Object(const size_t Id, const SDL_Rect & r, const SDL_Color & c); + virtual ~Object() {} + size_t id; + SDL_Rect rect; + SDL_Color color; + inline void copyRect(const SDL_Rect & src) { + rect.x = src.x; + rect.y = src.y; + rect.w = src.w; + rect.h = src.h; + } + inline void copyColor(const SDL_Color & src) { + color.r = src.r; + color.g = src.g; + color.b = src.b; + color.unused = src.unused; + } + virtual void draw(); + Manager * manager; + }; + + struct TexturedObject : public Object { + TexturedObject(const SDL_Rect & r, const size_t texid) : Object(r), + texId(texid) { + } + TexturedObject(size_t Id, const SDL_Rect & r, const size_t texid) : Object(Id, r), + texId(texid) { + } + size_t texId; + void draw(); + }; + + struct AnimatedTextureObject : public Object { + AnimatedTextureObject(const SDL_Rect & r, const size_t animid) : Object(r), + animId(animid) { + animation = NULL; + } + AnimatedTextureObject(size_t Id, const SDL_Rect & r, const size_t animid) : Object(Id, r), + animId(animid) { + animation = NULL; + } + size_t animId; + Animation * animation; + void draw(); + }; + + struct Label : public Object { + Label(const SDL_Rect & r, const std::string & s, + const std::string & fontFile, const size_t fontScale) : Object(r), text(s) { + OpenGL::DrawableFont & fnt = OpenGTA::FontCacheHolder::Instance().getFont(fontFile, fontScale); + font = &fnt; + } + Label(const size_t Id, const SDL_Rect & r, const std::string & s, + const std::string & fontFile, const size_t fontScale) : Object(Id, r), text(s) { + OpenGL::DrawableFont & fnt = OpenGTA::FontCacheHolder::Instance().getFont(fontFile, fontScale); + font = &fnt; + } + OpenGL::DrawableFont * font; + std::string text; + void draw(); + }; +} +#endif diff --git a/util/image_loader.cpp b/util/image_loader.cpp new file mode 100644 index 0000000..d86f831 --- /dev/null +++ b/util/image_loader.cpp @@ -0,0 +1,155 @@ +#include +#include +#include "image_loader.h" +#include "file_helper.h" +#include "buffercache.h" +#include "log.h" +#include "cistring.h" +#include "opengta.h" + +namespace ImageUtil { +using OpenGL::PagedTexture; + + WidthHeightPair lookupImageSize(const std::string & name, const uint32_t size) { + Util::ci_string iname(name.c_str()); + uint16_t width = 0; + uint16_t height = 0; + uint32_t bpp = 0; + + // m4 tools/raw_images.m4 + if ((iname.find("CUT") == 0) && (iname.find(".RA") == 4)) { + width = 640; height = 480; + } + if ((iname.find("F_BMG.RA") == 0)) { + width = 100; height = 50; + } + if ((iname.find("F_DMA.RA") == 0)) { + width = 78; height = 109; + } + if ((iname.find("F_LOGO") == 0) && (iname.find(".RA") == 7)) { + width = 640; height = 168; + } + if ((iname.find("F_LOWER") == 0) && (iname.find(".RA") == 8)) { + width = 640; height = 312; + } + if ((iname.find("F_PLAYN.RA") == 0)) { + width = 180; height = 50; + } + if ((iname.find("F_PLAY") == 0) && (iname.find(".RA") == 7)) { + width = 102; height = 141; + } + if ((iname.find("F_UPPER.RA") == 0)) { + width = 640; height = 168; + } + + // end-of-generated code + if (iname.find(".RAW") == iname.length() - 4) + bpp = 3; + + if (iname.find(".RAT") == iname.length() - 4) + bpp = 1; + + if (!(bpp && bpp * width * height == size)) + ERROR << "could not identify image: " << name << " size: " << size << std::endl; + return std::make_pair(width, height); + } + + + OpenGL::PagedTexture loadImageRAW(const std::string & name) { + + PHYSFS_file * fd = GET_FILE_HELPER.openReadVFS(name); + + uint32_t nbytes = PHYSFS_fileLength(fd); + + WidthHeightPair whp = lookupImageSize(name, nbytes); + + if (whp.first == 0 || whp.second == 0) { + PHYSFS_close(fd); + WARN << "aborting image load" << std::endl; + } + + Util::BufferCache::LockedBuffer lbuf(nbytes); + /* + uint8_t * buffer = Util::BufferCacheHolder::Instance(). + requestBuffer(nbytes); + */ + uint8_t *buffer = lbuf(); + PHYSFS_read(fd, buffer, 1, nbytes); + PHYSFS_close(fd); + + return createEmbeddedTexture(whp.first, whp.second, false, buffer); + } + + + OpenGL::PagedTexture loadImageRATWithPalette(const std::string & name, + const std::string & palette_file) { + + Util::FileHelper & fh = GET_FILE_HELPER; + + PHYSFS_file * fd = fh.openReadVFS(name); + uint32_t nbytes = PHYSFS_fileLength(fd); + + WidthHeightPair whp = lookupImageSize(name, nbytes); + if (whp.first == 0 || whp.second == 0) { + PHYSFS_close(fd); + WARN << "aborting image load" << std::endl; + } + Util::BufferCache::LockedBuffer lb1(nbytes); + PHYSFS_read(fd, lb1(), 1, nbytes); + PHYSFS_close(fd); + + // if this causes an exception, the buffercache will cleanup + fd = fh.openReadVFS(palette_file); + OpenGTA::Graphics8Bit::RGBPalette rgb(fd); + PHYSFS_close(fd); + + Util::BufferCache::LockedBuffer lb2(nbytes * 3); + rgb.apply(nbytes, lb1(), lb2(), false); + + return createEmbeddedTexture(whp.first, whp.second, false, lb2()); + } + + GLuint createGLTexture(GLsizei w, GLsizei h, bool rgba, const void* pixels) { + GLuint tex; + glGenTextures(1, &tex); + glBindTexture(GL_TEXTURE_2D, tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + if (rgba) + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + //gluBuild2DMipmaps(GL_TEXTURE_2D, 4, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + else + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels); + //gluBuild2DMipmaps(GL_TEXTURE_2D, 3, w, h, GL_RGB, GL_UNSIGNED_BYTE, pixels); + + GL_CHECKERROR; + return tex; + } + + void copyImage2Image(uint8_t *dest, const uint8_t *src, const uint16_t + srcWidth, const uint16_t srcHeight, const uint16_t destWidth) { + uint8_t *d = dest; + uint32_t srcOff = 0; + for (uint16_t j = 0; j < srcHeight; ++j) { + memcpy(d, src + srcOff, srcWidth); + srcOff += srcWidth; + d += destWidth; + } + } + + OpenGL::PagedTexture createEmbeddedTexture(GLsizei w, GLsizei h, + bool rgba, const void* pixels) { + + NextPowerOfTwo npot(w, h); + uint32_t bpp = (rgba ? 4 : 3); + + uint32_t bufSize = npot.w * npot.h * bpp; + uint8_t* buff = Util::BufferCacheHolder::Instance().requestBuffer(bufSize); + copyImage2Image(buff, (uint8_t*)pixels, w * bpp, h, npot.w * bpp); + + GLuint tex = createGLTexture(npot.w, npot.h, rgba, buff); + return PagedTexture(tex, 0, 0, float(w) / npot.w, float(h) / npot.h); + } +} diff --git a/util/image_loader.h b/util/image_loader.h new file mode 100644 index 0000000..dfa3331 --- /dev/null +++ b/util/image_loader.h @@ -0,0 +1,42 @@ +#ifndef UTIL_IMAGE_LOADER_H +#define UTIL_IMAGE_LOADER_H + +#include +#include +#include "gl_pagedtexture.h" + +namespace ImageUtil { + + typedef std::pair WidthHeightPair; + + struct NextPowerOfTwo { + NextPowerOfTwo(uint32_t _w, uint32_t _h) { + w = 1; h = 1; + while (w < _w) { w <<= 1; } + while (h < _h) { h <<= 1; } + } + uint32_t w; + uint32_t h; + }; + + // hardcoded data for known images + WidthHeightPair lookupImageSize(const std::string & name, const uint32_t size); + // load a rgb image + OpenGL::PagedTexture loadImageRAW(const std::string & name); + // load a palette image and guess the palette filename + OpenGL::PagedTexture loadImageRAT(const std::string & name); + // load a palette image using palette file + OpenGL::PagedTexture loadImageRATWithPalette(const std::string & name, + const std::string & palette_file); + // plain simple garden-variety create-a-texture; needs to be ^2 + GLuint createGLTexture(GLsizei w, GLsizei h, bool rgba, const void* pixels); + + // blitting a buffer into another; they should exist! + void copyImage2Image(uint8_t *dest, const uint8_t *src, const uint16_t + srcWidth, const uint16_t srcHeight, const uint16_t destWidth); + + OpenGL::PagedTexture createEmbeddedTexture(GLsizei w, GLsizei h, bool rgba, + const void *pixels); +} + +#endif diff --git a/util/log.cpp b/util/log.cpp new file mode 100644 index 0000000..4a2bbe0 --- /dev/null +++ b/util/log.cpp @@ -0,0 +1,29 @@ +#include "log.h" +#include + +namespace Util { + unsigned int Log::level = 0; + std::ostream Log::emptyStream(0); + + void Log::setOutputLevel(unsigned int newLevel) { + level = newLevel; + } + + const char* Log::glErrorName(int k) { + switch(k) { + case GL_NO_ERROR: + return "GL_NO_ERROR"; + case GL_INVALID_VALUE: + return "GL_INVALID_VALUE"; + case GL_INVALID_OPERATION: + return "GL_INVALID_OPERATION"; + case GL_STACK_OVERFLOW: + return "GL_STACK_OVERFLOW"; + case GL_STACK_UNDERFLOW: + return "GL_STACK_UNDERFLOW"; + case GL_OUT_OF_MEMORY: + return "GL_OUT_OF_MEMORY"; + } + return "Unknown-Error"; + } +} diff --git a/util/log.h b/util/log.h new file mode 100644 index 0000000..c5f260e --- /dev/null +++ b/util/log.h @@ -0,0 +1,34 @@ +#ifndef LOG_FUNCS_H +#define LOG_FUNCS_H +#include + +#ifdef WIN32 +#undef ERROR +#endif + +#define INFO Util::Log::info(__FILE__, __LINE__) +#define WARN Util::Log::warn(__FILE__, __LINE__) +#define ERROR Util::Log::error(__FILE__, __LINE__) +#define ERROR_AND_EXIT(ec) error_code = ec; exit(ec); +#define GL_CHECKERROR { int _err = glGetError(); if (_err != GL_NO_ERROR) \ +Util::Log::error(__FILE__, __LINE__) << "GL error: " << _err << " = " << Util::Log::glErrorName(_err) << std::endl; } +namespace Util { + class Log { + public: + inline static std::ostream & info(const char* f, int l) { + if (level) return emptyStream; std::cout << "Info (" << f << ":" << l << "): "; return std::cout; + } + inline static std::ostream & warn(const char* f, int l) { + if (level > 1) return emptyStream; std::cerr << "Warning (" << f << ":" << l << "): "; return std::cerr; + } + inline static std::ostream & error(const char* f, int l) { + std::cerr << "Error ("<< f << ":" << l << "): "; return std::cerr; + } + static void setOutputLevel(unsigned int newLevel); + static const char* glErrorName(int k); + private: + static unsigned int level; + static std::ostream emptyStream; + }; +} +#endif diff --git a/util/m_exceptions.cpp b/util/m_exceptions.cpp new file mode 100644 index 0000000..358a5d5 --- /dev/null +++ b/util/m_exceptions.cpp @@ -0,0 +1,55 @@ +#include "m_exceptions.h" +#include + +namespace Util { + + LocalException::LocalException(const char *f, const size_t l, const char* n) : + inFile(f), typeName(n), msg(""), inLine(l) {} + + LocalException::LocalException(const char *f, const size_t l, const char* n, + const std::string _msg) : + inFile(f), typeName(n), msg(_msg), inLine(l) {} + + const char* LocalException::what() const throw() { + std::ostringstream o; + o << typeName << " (" << inFile << ":" << inLine << "): " << msg; + return o.str().c_str(); + } + + FileNotFound::FileNotFound(const char* f, const size_t l, const std::string _msg) : + LocalException(f, l, "FileNotFound", _msg) {} + + IOError::IOError(const char* f, const size_t l, const std::string _msg) : + LocalException(f, l, "IOError", _msg) {} + + InvalidFormat::InvalidFormat(const char* f, const size_t l, const std::string _msg) : + LocalException(f, l, "InvalidFormat", _msg) {} + + UnknownKey::UnknownKey(const char* f, const size_t l, const std::string _msg) : + LocalException(f, l, "UnknownKey", _msg) {} + + OutOfRange::OutOfRange(const char* f, const size_t l, const std::string _msg) : + LocalException(f, l, "OutOfRange", _msg) {} + + OutOfMemory::OutOfMemory(const char* f, const size_t l, const std::string _msg) : + LocalException(f, l, "OutOfMemory", _msg) {} + + ScriptError::ScriptError(const char* f, const size_t l, const std::string _msg) : + LocalException(f, l, "ScriptError", _msg) {} + + NotSupported::NotSupported(const char* f, const size_t l, const std::string _msg) : + LocalException(f, l, "NotSupported", _msg) {} + +} + +#if 0 +using namespace Util; +int main(int argc, char* argv[]) { + try { + throw E_FILENOTFOUND("foobar.zip"); + } + catch (Exception & e) { + std::cout << "E: " << e.what() << std::endl; + } +} +#endif diff --git a/util/m_exceptions.h b/util/m_exceptions.h new file mode 100644 index 0000000..77de11c --- /dev/null +++ b/util/m_exceptions.h @@ -0,0 +1,86 @@ +#ifndef LOCAL_EXCEPTIONS_OH_WHAT_JOY +#define LOCAL_EXCEPTIONS_OH_WHAT_JOY + +#include +#include + +namespace Util { + + struct LocalException : public std::exception { + LocalException(const char *f, const size_t l, const char* n); + LocalException(const char *f, const size_t l, const char* n, + const std::string _msg); + virtual ~LocalException() throw() {} + const char * what() const throw(); + std::string inFile; + std::string typeName; + std::string msg; + size_t inLine; + }; + + /* Actually I wouldn't make the derived destructors virtual, + * but g++ complains if I don't. No idea why. + * + * Anyway exceptions shouldn't happen that often and gcc is + * usually right about those things... + */ + + struct FileNotFound : public LocalException { + FileNotFound(const char* f, const size_t l, const std::string _msg); + virtual ~FileNotFound() throw() {} + }; + + struct IOError : public LocalException { + IOError(const char* f, const size_t l, const std::string _msg); + virtual ~IOError() throw() {} + }; + + struct InvalidFormat : public LocalException { + InvalidFormat(const char* f, const size_t l, const std::string _msg); + virtual ~InvalidFormat() throw() {} + }; + + struct UnknownKey : public LocalException { + UnknownKey(const char* f, const size_t l, const std::string _msg); + virtual ~UnknownKey() throw() {} + }; + + struct OutOfRange : public LocalException { + OutOfRange(const char* f, const size_t l, const std::string _msg); + virtual ~OutOfRange() throw () {} + }; + + struct OutOfMemory : public LocalException { + OutOfMemory(const char* f, const size_t l, const std::string _msg); + virtual ~OutOfMemory() throw() {} + }; + + struct ScriptError : public LocalException { + ScriptError(const char* f, const size_t l, const std::string _msg); + virtual ~ScriptError() throw() {} + }; + + struct NotSupported : public LocalException { + NotSupported(const char* f, const size_t l, const std::string _msg); + virtual ~NotSupported() throw() {} + }; +} + +// to avoid the need for the namespace when writing catch-all blocks +typedef Util::LocalException Exception; + +#ifdef WIN32 +#undef E_OUTOFMEMORY +#endif + +// to auto-fill line+file information where the exception was created +#define E_FILENOTFOUND(m) Util::FileNotFound(__FILE__, __LINE__, m) +#define E_IOERROR(m) Util::IOError(__FILE__, __LINE__, m) +#define E_INVALIDFORMAT(m) Util::InvalidFormat(__FILE__, __LINE__, m) +#define E_UNKNOWNKEY(m) Util::UnknownKey(__FILE__, __LINE__, m) +#define E_OUTOFRANGE(m) Util::OutOfRange(__FILE__, __LINE__, m) +#define E_OUTOFMEMORY(m) Util::OutOfMemory(__FILE__, __LINE__, m) +#define E_SCRIPTERROR(m) Util::ScriptError(__FILE__, __LINE__, m) +#define E_NOTSUPPORTED(m) Util::NotSupported(__FILE__, __LINE__, m) + +#endif diff --git a/util/sample_cache.h b/util/sample_cache.h new file mode 100644 index 0000000..81072fa --- /dev/null +++ b/util/sample_cache.h @@ -0,0 +1,77 @@ +/************************************************************************ +* Copyright (c) 2005-2006 tok@openlinux.org.uk * +* * +* This software is provided as-is, without any express or implied * +* warranty. In no event will the authors be held liable for any * +* damages arising from the use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute * +* it freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must * +* not claim that you wrote the original software. If you use this * +* software in a product, an acknowledgment in the product documentation * +* would be appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must * +* not be misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source * +* distribution. * +************************************************************************/ +#ifndef SAMPLE_CACHE_H +#define SAMPLE_CACHE_H + +#include +#include +#include "Singleton.h" +#include "SmallObj.h" + +namespace Audio { + + class SampleType_MixChunk : public Loki::SmallObject<> { + public: + SampleType_MixChunk(Uint8 *mem, Mix_Chunk *ch) : memory(mem), chunk(ch) {} + ~SampleType_MixChunk() { + delete [] memory; + Mix_FreeChunk(chunk); + } + Uint8 *memory; + Mix_Chunk *chunk; + private: + SampleType_MixChunk(const SampleType_MixChunk & other); + SampleType_MixChunk() : memory(0), chunk(0) {} + }; + + typedef size_t SampleKeyType; + + template < typename K, typename V > + class Cache { + public: + ~Cache() { + clear; + } + void clear(); + typedef std::map< K, V > CacheMapType; + V operator[](K k) { return m_map[k]; } + void add(K k, V v) { m_map.insert(std::make_pair(k, v)); } + private: + CacheMapType m_map; + + }; + + typedef Cache< SampleKeyType, SampleType_MixChunk* > SampleCache; + + template<> void SampleCache::clear() { + for (CacheMapType::iterator i = m_map.begin(); i != m_map.end(); i++) { + delete i->second; + } + m_map.clear(); + } + + typedef Loki::SingletonHolder< SampleCache , Loki::CreateUsingNew, + Loki::DefaultLifetime, Loki::SingleThreaded> SampleCacheHolder; +} + +#endif diff --git a/util/set.cpp b/util/set.cpp new file mode 100644 index 0000000..0daf0eb --- /dev/null +++ b/util/set.cpp @@ -0,0 +1,298 @@ +/************************************************************************ +* Copyright (c) 2005-2006 tok@openlinux.org.uk * +* * +* This software is provided as-is, without any express or implied * +* warranty. In no event will the authors be held liable for any * +* damages arising from the use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute * +* it freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must * +* not claim that you wrote the original software. If you use this * +* software in a product, an acknowledgment in the product documentation * +* would be appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must * +* not be misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source * +* distribution. * +************************************************************************/ +#include +#include +#include +#include +#include "set.h" + +#define INTEGRATE_OGTA +#ifdef INTEGRATE_OGTA +#include +#include "m_exceptions.h" +#endif + +namespace Util { + + // some bits of binary magic from someone on IRC + // by the name of Zhivago + // only the 8-bit variants are used +#define maskn(x, w) ((x) & ((1<>w] & (1<<(maskn(x, w)))) +#define setbn(a, x, w) ((a)[x>>w] |= (1<<(maskn(x, w)))) +#define clrbn(a, x, w) ((a)[x>>w] &= ~(1<<(maskn(x, w)))) + +#define tstb8(a, x) tstbn((a), (x), 3) +#define setb8(a, x) setbn((a), (x), 3) +#define clrb8(a, x) clrbn((a), (x), 3) + +#define tstb32(a, x) tstbn((a), (x), 5) +#define setb32(a, x) setbn((a), (x), 5) +#define clrb32(a, x) clrbn((a), (x), 5) + + // and something to move through an array to the correct byte +#define mv2byte(a, k, p) \ + if (k >= 8) { \ + p = a + k / 8; \ + k = k % 8; \ + } \ + else { \ + p = a; \ + } + + // note: given a set of 1..N values + // this means one needs to malloc(N/8 + (n%8 == 0 ? 0 : 1) bytes of mem + // the bit status (reading from left to right) corresponds + // to the existence of the index number in the set + + Set::Set() { + last = MAX_SET_COUNT; + storage = new unsigned char[MAX_SET_COUNT / 8]; + memset((void*)storage, 0, (size_t) MAX_SET_COUNT / 8); + ext_data = 0; + } + + Set::Set(int n) { + last = n; + assert(n>0); + int k = ((n % 8) > 0) ? n / 8 + 1 : n / 8; + storage = new unsigned char[k]; + memset((void*)storage, 0, (size_t)k); + ext_data = 0; + } + + Set::Set(const Set & other) { + last = other.get_last(); + unsigned char* os = other.give_storage(); + assert(last > 0); + int k = ((last % 8) > 0) ? last / 8 + 1 : last / 8; + storage = new unsigned char[k]; + memcpy(storage, os, k); + ext_data = 0; + } + + Set::Set(int k, unsigned char* data) { + storage = data; + last = k; + ext_data = 1; + } + + Set::~Set() { + if (!ext_data && storage != NULL) + delete [] storage; + } + + void Set::set_data(int n, unsigned char* data) { + if (!ext_data) { +#ifdef INTEGRATE_OGTA + throw E_NOTSUPPORTED("set_data() called on an instance with own data"); +#else + fprintf(stderr, "Set::Err: set_data() called on an instance with own data\n"); +#endif + return; + } + } + + void Set::set_last(int n) { + if (n > last) { +#ifdef INTEGRATE_OGTA + std::ostringstream o; + o << n << " > " << last << std::endl; + throw E_OUTOFRANGE(o.str()); +#else + printf("%i is larger than previous last n (%i), aborting\n", n, last); +#endif + } + last = n; + } + + int Set::get_last() const { + return(last); + } + + void Set::print_set() const { + bool first = true; + printf("{"); + for (int i = 0; i < last; i++) { + if (get_item(i)) { + if (!first) printf(", "); + printf("%d", i); + first = false; + } + } + printf("}\n"); + } + + void Set::set_item(int k, bool val) { + if (k < last) { + unsigned char* pos = NULL; + mv2byte(storage, k, pos); + if (val == true) { + setb8(pos, k); + } + else { + clrb8(pos, k); + } + } + else +#ifdef INTEGRATE_OGTA + { + std::ostringstream o; + o << k << " >= " << last << std::endl; + throw E_OUTOFRANGE(o.str()); + } +#else + assert(k < last); +#endif + } + + bool Set::get_item(int k) const { + bool b = false; + if (k < last) { + unsigned char* pos; + mv2byte(storage, k, pos); + b = tstb8(pos, k); + } + else +#ifdef INTEGRATE_OGTA + { + std::ostringstream o; + o << k << " >= " << last << std::endl; + throw E_OUTOFRANGE(o.str()); + } +#else + assert(k < last); +#endif + return b; + } + + int Set::as_int(int start, int len) const { + if (start < 0 || start > last || start + len > last) { +#ifdef INTEGRATE_OGTA + std::ostringstream o; + o << "invalid query: " << start << " length " << len << + " with data-length " << last << std::endl; + throw E_OUTOFRANGE(o.str()); +#else + fprintf(stderr, "Set::Err: queried index out of range (%i, %i ; %i)\n", start, len, last); + return -1; +#endif + } + int v = 0; + int t = 0; + for (int i=0; i< len; i++) { + if (get_item(i+start)) { + v = (int) 1 << i; + t += v; + } + } + return t; + } + + int Set::as_int2(int start, int len) const { + if (start < 0 || start > last || start + len > last) { +#ifdef INTEGRATE_OGTA + std::ostringstream o; + o << "invalid query: " << start << " length " << len << + " with data-length " << last << std::endl; + throw E_OUTOFRANGE(o.str()); +#else + fprintf(stderr, "Set::Err: queried index out of range (%i, %i ; %i)\n", start, len, last); + return -1; +#endif + } + int v = 0; + int t = 0; + for (int i=0; i< len; i++) { + if (get_item(i+start)) { + v = (int)1 << (len-i-1); + t += v; + } + } + return t; + } + + int Set::compare(const Set & other) const { + int res = 0; + unsigned char* oc; + int o_s = 0; + int l; + int both = 0; // #elements in both sets + int io = 0; // #in other set + int it = 0; // #in this set + + o_s = other.get_last(); + oc = other.give_storage(); + // find the smaller index + l = (last >= o_s)? o_s : last; + // count away ;-) + for (int i = 0; i < l; i++) { + unsigned char* o; + unsigned char* s; + int j = i; + mv2byte(storage, j, s); + j = i; + mv2byte(oc, j, o); + if (tstb8(s, j)) { + it++; + if (tstb8(o, j)) { + both++; + io++; + } + } + else { + if (tstb8(o, j)) { + io++; + } + } + } // for + //printf("both %i, it %i, io %i\n", both, it, io); + if (both >= 0) { + if (both == it && both == io) { + res = 3; // sets are equal (maybe empty) + } + else if (both == it) { + res = -1; // this set is smaller than the other, other contains this + } + else if (both == io) { + res = 1; // this set is larger and contains the other + } + else if (both > 0) { + if (io > it) { + res = -2; + } + else { + res = 2; + } + } + else res = 0; + } + return(res); + } + + unsigned char* Set::give_storage() const { + return(storage); + } + +} diff --git a/util/set.h b/util/set.h new file mode 100644 index 0000000..3d4cef4 --- /dev/null +++ b/util/set.h @@ -0,0 +1,135 @@ +/************************************************************************ +* Copyright (c) 2005-2006 tok@openlinux.org.uk * +* * +* This software is provided as-is, without any express or implied * +* warranty. In no event will the authors be held liable for any * +* damages arising from the use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute * +* it freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must * +* not claim that you wrote the original software. If you use this * +* software in a product, an acknowledgment in the product documentation * +* would be appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must * +* not be misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source * +* distribution. * +************************************************************************/ +#ifndef SET_H +#define SET_H + +namespace Util { + + /* Implements a set for non-negative integer values. + * storage is a char* which is either a member of this class, + * or given to an instance via alternative constructor + * or the set_data() function. + * Some macros are used to move to the correct char and + * read/write the binary data. + */ + class Set { + public: + /** Base constructor. + * Creates a set of size #MAX_SET_COUNT. + */ + Set(); + + /** Constructor with defined size. + * @param n the element count in an integer variable + */ + Set(int n); + /** Copy constructor, copies internal data of another Set. + * @param other reference to the other set + * + * \note This is a deep copy, rather than a shallow one; meaning + * this set will allocate memory and memcpy the data, not + * hold a pointer into the other set. + */ + Set(const Set & other); + + /** Constructor with external data set. + * @param n Size of storage (in bits) + * @param data pointer to external storage + * \note The set will not handle freeing of this memory, + * it just works on it. + */ + Set(int n, unsigned char* data); + + ~Set(); + + /** Give another storage pointer to an instance. + * @param n size + * @param data pointer to data + * @note This only works for instances that were already + * constructed with external data. + */ + void set_data(int n, unsigned char* data); + + /** Mutator for the last index. + * @param n the element count + * @note You should only shrink sets previously defined @e larger, + * not the other way round! + */ + void set_last(int n); + + /** Set2Number - least significant bits first. + */ + int as_int(int start, int len) const; + + /** Set2Number - most significant bits first. + */ + int as_int2(int start, int len) const; + + /** Accessor for the last index. + * @return n the element count + */ + int get_last() const; + + /** Mutator for the contained data. + * @param k the index of the item to change + * @param val true (k-th item in set) or false (not in set) + */ + void set_item(int k, bool val); + + /** Accessor for the contained data. + * @param k item index to query + * @return val either true (item in set) or false (not in set) + */ + bool get_item(int k) const; + + /** Test set for inclusion with another. + * the return value is either: + * 0 - the two sets are disjoint + * 1 - this set contains the other + * 2 - this set contains a subset of the other + * -1 - the other set contains this + * -2 - the other contains a subset of this + * 3 - the two sets are equal + * @param other a reference to another Set + * @return res an int value as listed above + */ + int compare(const Set & other) const; + + /** Returns the address of the internal storage data. + * KNOW WHAT YOU ARE DOING WITH THIS + * @return storage a char* to the entire storage block + */ + unsigned char* give_storage() const; + + void print_set() const; + + protected: + int last; + private: + unsigned char* storage; + bool ext_data; + // make sure this is a power of 8 + static const int MAX_SET_COUNT = 32; + }; +} +#endif diff --git a/util/sound_device.cpp b/util/sound_device.cpp new file mode 100644 index 0000000..a6d48ad --- /dev/null +++ b/util/sound_device.cpp @@ -0,0 +1,52 @@ +#include +#include +#include "sound_device.h" +#include "m_exceptions.h" +#include "log.h" + +namespace Audio { + SoundDevice::SoundDevice() { + memset(cardName, 0, sizeof(cardName)); + rate = 44100; + channels = 2; + bufSize = 4096; + format = AUDIO_S16; + status = CLOSED; + } + + SoundDevice::~SoundDevice() { + Mix_HaltGroup(-1); + Mix_HaltMusic(); + close(); + } + + void SoundDevice::close() { + if (!status) + return; + Mix_CloseAudio(); + status = CLOSED; + } + + void SoundDevice::open() { + if (status) + close(); + if (Mix_OpenAudio(rate, format, channels, bufSize)) + throw E_NOTSUPPORTED(SDL_GetError()); + SDL_AudioDriverName (cardName, sizeof (cardName)); + if (!Mix_QuerySpec(&rate, &format, &channels)) + throw E_NOTSUPPORTED(SDL_GetError()); + INFO << "Sound-device [" << cardName << "] opened at " << rate << " Hz " << + (format & 0xff) << " bit " << channels << " channels " << bufSize << + " buffer size" << std::endl; + + status = OPEN; + } + + void SoundDevice::open(int r, Uint16 f, int c, int bs) { + rate = r; + format = f; + channels = c; + bufSize = bs; + open(); + } +} diff --git a/util/sound_device.h b/util/sound_device.h new file mode 100644 index 0000000..b38f4e4 --- /dev/null +++ b/util/sound_device.h @@ -0,0 +1,34 @@ +#ifndef UTIL_SOUND_DEVICE_H +#define UTIL_SOUND_DEVICE_H + +#include + +namespace Audio { + class SoundDevice { + public: + SoundDevice(); + ~SoundDevice(); + void close(); + void open(); + void open(int _rate, Uint16 _format, int _channels, int _bufsize); + inline const char* getCardName() { return cardName; } + inline const int getRate() { return rate; } + inline const int getNumChannels() { return channels; } + inline const int getBufferSize() { return bufSize; } + enum Status { + CLOSED = 0, + OPEN, + ERROR, + }; + inline Status getStatus() const { return status; } + private: + char cardName[50]; + int rate; + int channels; + int bufSize; + Uint16 format; + Status status; + }; +} + +#endif diff --git a/util/sound_mixer.h b/util/sound_mixer.h new file mode 100644 index 0000000..0f5f02b --- /dev/null +++ b/util/sound_mixer.h @@ -0,0 +1,35 @@ +#ifndef UTIL_SOUND_MIXER_H +#define UTIL_SOUND_MIXER_H + +#include +#include +#include "sound_device.h" +#include "Singleton.h" + +namespace Audio { + class Mixer { + public: + Mixer(); + ~Mixer(); + + Audio::SoundDevice device; + + int setVolume(int channel, int volume); + int setVolumeMusic(); + int getVolume(int channel); + int getVolumeMusic(); + + void playSample(const size_t & sampleId); + + void pause(int channel); + void pauseMusic(); + private: + inline bool checkDeviceOK() { return (device.getStatus() == + Audio::SoundDevice::OPEN); } + }; + + typedef Loki::SingletonHolder MixerHolder; +} + +#endif diff --git a/util/timer.cpp b/util/timer.cpp new file mode 100644 index 0000000..335ee1d --- /dev/null +++ b/util/timer.cpp @@ -0,0 +1,159 @@ +#include +#include "timer.h" +#include + +Timer::TimeEvent::TimeEvent(const uint32_t & b, const uint32_t e, CallbackType & c) : + begin(b), end(e), callback(c) {} + +Timer::TimeEvent::TimeEvent(const uint32_t & b, CallbackType & c) : + begin(b), end(b), callback(c) {} + +Timer::TimeEvent::TimeEvent(const TimeEvent & o) : + begin(o.begin), end(o.end), callback(o.callback) {} + +Timer::Timer() { + sdlTicks = SDL_GetTicks(); + simTicks = 0; + delta = 0; + simIsRunning = false; +} + +Timer::~Timer() { + clearAllEvents(); +} + +void Timer::update() { + uint32_t nowTicks = SDL_GetTicks(); + delta = nowTicks - sdlTicks; + sdlTicks = nowTicks; + if (simIsRunning) + simTicks += delta; + + if (realTimeEvents.size() > 0) + checkRTEvents(); + + if (simIsRunning && simTimeEvents.size() > 0) + checkSimEvents(); +} + +void Timer::checkRTEvents() { + RealTimeMap::iterator i = realTimeEvents.begin(); + while (i != realTimeEvents.end() && i->first <= sdlTicks) { + TimeEvent & te = i->second; + float as_float = 0.0f; + bool doRemove = false; + if (te.begin == te.end) { + doRemove = true; + } + else { + if (sdlTicks > te.end) { + as_float = 1.0f; + doRemove = true; + } + else + as_float = float(sdlTicks - te.begin) / float(te.end - te.begin); + } + te.callback(as_float); + if (doRemove) { + RealTimeMap::iterator j = i++; + realTimeEvents.erase(j); + } + else + ++i; + } +} + +void Timer::checkSimEvents() { + SimTimeMap::iterator i = simTimeEvents.begin(); + while (i != simTimeEvents.end() && i->first <= simTicks) { + std::cout << "event start: " << i->first << " now " << simTicks << std::endl; + TimeEvent & te = i->second; + float as_float = 0.0f; + bool doRemove = false; + if (te.begin == te.end) { + doRemove = true; + } + else { + if (simTicks > te.end) { + as_float = 1.0f; + doRemove = true; + } + else + as_float = float(simTicks - te.begin) / float(te.end - te.begin); + } + te.callback(as_float); + if (doRemove) { + SimTimeMap::iterator j = i++; + simTimeEvents.erase(j); + } + else + ++i; + } +} + +void Timer::registerCallback(bool simTime, CallbackType & c, const uint32_t & b, const uint32_t & e) { + if (simTime) + simTimeEvents.insert(std::make_pair(b, TimeEvent(b, e, c))); + else + realTimeEvents.insert(std::make_pair(b, TimeEvent(b, e, c))); +} + +void Timer::registerCallback(bool simTime, CallbackType & c, const uint32_t & b) { + if (simTime) + simTimeEvents.insert(std::make_pair(b, TimeEvent(b, c))); + else + realTimeEvents.insert(std::make_pair(b, TimeEvent(b, c))); +} + +void Timer::clearAllEvents() { + realTimeEvents.clear(); + simTimeEvents.clear(); +} + + +#if 0 +void test1(float a) { + std::cout << "test1: " << a << std::endl; +} + +int main() { + SDL_Init(SDL_INIT_TIMER); + + SDL_Delay(10); + Timer * t = new Timer; + t->setSimulationRunning(true); + Timer::CallbackType cb(test1); + uint32_t target = t->getTime() + 100; + uint32_t finish = target + 100; + t->registerCallback(true, cb, target, finish); + + while (t->getRealTime() < target + 1000) { + t->update(); + SDL_Delay(10); + } + +/* + Timer * t = new Timer; + if (t->getRealTime() < 8 || t->getRealTime() > 12) + std::cout << "expected 10 got " << t->getRealTime() << std::endl; + else + std::cout << "got ~10: " << t->getRealTime() << std::endl; + + Timer::CallbackType cb(test1); + + uint32_t target = t->getRealTime() + 100; + uint32_t finish = target + 100; + t->registerCallback(cb, target, finish); + + uint32_t slept = 0; + while (t->getRealTime() < target + 200) { + t->update(); + std::cout << "now: " << t->getRealTime() << std::endl; + + SDL_Delay(10); + } +*/ + + delete t; +} +#endif diff --git a/util/timer.h b/util/timer.h new file mode 100644 index 0000000..5ae6af1 --- /dev/null +++ b/util/timer.h @@ -0,0 +1,47 @@ +#ifndef OGTA_TIMER_H +#define OGTA_TIMER_H +#include +#include "Singleton.h" +#include "Functor.h" + +class Timer { + public: + typedef Loki::Functor CallbackType; + struct TimeEvent { + TimeEvent(const uint32_t & b, const uint32_t e, CallbackType & c); + TimeEvent(const uint32_t & b, CallbackType & c); + TimeEvent(const TimeEvent & o); + const uint32_t begin; + const uint32_t end; + CallbackType callback; + }; + Timer(); + ~Timer(); + // simulation time in ticks + inline const uint32_t & getTime() const { return simTicks; } + // time since SDL_Init + inline const uint32_t & getRealTime() const { return sdlTicks; } + void update(); + inline void setSimulationRunning(bool yes) { simIsRunning = yes; } + inline bool getSimulationRunning() const { return simIsRunning; } + void registerCallback(bool simTime, CallbackType & c, const uint32_t & b, const uint32_t & e); + void registerCallback(bool simTime, CallbackType & c, const uint32_t & b); + void clearAllEvents(); + private: + void checkRTEvents(); + void checkSimEvents(); + uint32_t sdlTicks; + uint32_t simTicks; + uint32_t delta; + bool simIsRunning; + + typedef std::multimap RealTimeMap; + RealTimeMap realTimeEvents; + typedef RealTimeMap SimTimeMap; + SimTimeMap simTimeEvents; +}; + +typedef Loki::SingletonHolder TimerHolder; + +#endif diff --git a/viewer.cpp b/viewer.cpp new file mode 100644 index 0000000..dbc7ec6 --- /dev/null +++ b/viewer.cpp @@ -0,0 +1,632 @@ +/************************************************************************ +* Copyright (c) 2005-2006 tok@openlinux.org.uk * +* * +* This software is provided as-is, without any express or implied * +* warranty. In no event will the authors be held liable for any * +* damages arising from the use of this software. * +* * +* Permission is granted to anyone to use this software for any purpose, * +* including commercial applications, and to alter it and redistribute * +* it freely, subject to the following restrictions: * +* * +* 1. The origin of this software must not be misrepresented; you must * +* not claim that you wrote the original software. If you use this * +* software in a product, an acknowledgment in the product documentation * +* would be appreciated but is not required. * +* * +* 2. Altered source versions must be plainly marked as such, and must * +* not be misrepresented as being the original software. * +* * +* 3. This notice may not be removed or altered from any source * +* distribution. * +************************************************************************/ +#include +#include +#include +#include +//#include "common_sdl_gl.h" +#include "gl_screen.h" +#include "opengta.h" +#include "gl_texturecache.h" +#include "gl_cityview.h" +#include "gl_font.h" +#include "gl_camera.h" +#include "navdata.h" +#include "log.h" +#include "spritemanager.h" +#include "localplayer.h" +#include "m_exceptions.h" +#include "file_helper.h" +#include "gl_spritecache.h" +#ifdef WITH_LUA +#include "lua_addon/lua_vm.h" +#endif + +extern SDL_Surface* screen; +extern int global_EC; +extern int global_Done; +GLfloat mapPos[3] = {12.0f, 12.0f, 20.0f}; + +OpenGTA::CityView *city = NULL; +OpenGL::DrawableFont* m_font = NULL; + +int city_num = 0; +const char* styles_8[3] = { "STYLE001.GRY", "STYLE002.GRY", "STYLE003.GRY" }; +const char* styles_24[3] = { "STYLE001.G24", "STYLE002.G24", "STYLE003.G24" }; +const char* cities[3] = { "NYC.CMP", "SANB.CMP", "MIAMI.CMP" }; +std::string specific_map; +std::string specific_style; + +Uint32 num_frames_drawn = 0; +Uint32 fps = 0; +Uint32 last_tick; +Uint32 fps_last_tick; +Uint32 script_last_tick; +Uint32 arg_screen_w = 0; +Uint32 arg_screen_h = 0; +bool rotate = false; +bool cam_grav = false; +int tex_flip = 0; +int draw_arrows = 0; +int ped_anim = 0; +int bbox_toggle = 0; +int texsprite_toggle = 0; +int follow_toggle = 0; +OpenGTA::SpriteObject::Animation pedAnim(0, 0); +bool highcolor_data = false; +bool full_screen = false; +bool player_toggle_run = false; + +const char* script_file = NULL; +int paused = 0; + +/* +void ERROR(const char* s) { + std::cerr << "Error" << s << std::endl; + std::cerr << "* last SDL error was: " << SDL_GetError() << std::endl; + global_EC = 1; + exit(1); +}*/ + +void on_exit() { + SDL_Quit(); + if (city) + delete city; + if (m_font) + delete m_font; + PHYSFS_deinit(); + if (global_EC) + std::cerr << "Exiting after fatal problem - please see output above" << std::endl; + else + std::cout << "Goodbye" << std::endl; +} + +void print_position() { + Vector3D & v = OpenGL::CameraHolder::Instance().getCenter(); + Vector3D & e = OpenGL::CameraHolder::Instance().getEye(); + Vector3D & u = OpenGL::CameraHolder::Instance().getUp(); + if (!city->getViewMode()) { + INFO << cities[city_num] << ": " << city->getCurrentSector()->getFullName() << std::endl << + "camera.setCenter(" << v.x << ", " << v.y << ", " << v.z << ")" << std::endl << + "camera.setEye(" << e.x << ", " << e.y << ", " << e.z << ")" << std::endl << + "camera.setUp(" << u.x << ", " << u.y << ", " << u.z << ")" << std::endl << + "city_view:setVisibleRange(" << city->getVisibleRange() << ")" << std::endl << + "city_view:setTopDownView( false )" << std::endl; + } + else { + GLfloat* cp = city->getCamPos(); + INFO << cities[city_num] << ": " << city->getCurrentSector()->getFullName() << std::endl << + "city_view:setCamPosition(" << cp[0] << ", " << cp[1] << ", " << cp[2] << ")" << std::endl << + "city_view:setVisibleRange(" << city->getVisibleRange() << ")" << std::endl << + "city_view:setTopDownView( true )" << std::endl; + + } + +} + +void handleKeyUp( SDL_keysym *keysym) { + switch ( keysym->sym ) { + case 'j': + OpenGTA::LocalPlayer::Instance().turn = 0; + break; + case 'l': + OpenGTA::LocalPlayer::Instance().turn = 0; + break; + case 'i': + OpenGTA::LocalPlayer::Instance().move = 0; + break; + case 'k': + OpenGTA::LocalPlayer::Instance().move = 0; + break; + default: + break; + } +} + +void draw_mapmode(); + +void create_ped_at(const Vector3D v) { + OpenGTA::Pedestrian p(Vector3D(0.3f, 0.5f, 0.3f), v, 0xffffffff); + p.m_control = &OpenGTA::LocalPlayer::Instance(); + OpenGTA::SpriteManagerHolder::Instance().addPed(p); + OpenGTA::SpriteManagerHolder::Instance().getPedById(0xffffffff).switchToAnim(1); + OpenGTA::SpriteManagerHolder::Instance().getPedById(0xffffffff).giveItem(1, 255); +} + +void handleKeyPress( SDL_keysym *keysym ) { + GLfloat* cp = city->getCamPos(); + mapPos[0] = cp[0]; mapPos[1] = cp[1]; mapPos[2] = cp[2]; + OpenGL::Camera & cam = OpenGL::CameraHolder::Instance(); + switch ( keysym->sym ) { + case SDLK_ESCAPE: + global_Done = 1; + break; + case SDLK_LEFT: + mapPos[0] -= 1.0f; + cam.translateBy(Vector3D(-1, 0, 0)); + break; + case SDLK_RIGHT: + mapPos[0] += 1.0f; + cam.translateBy(Vector3D(1, 0, 0)); + break; + case SDLK_UP: + mapPos[2] -= 1.0f; + cam.translateBy(Vector3D(0, 0, -1)); + break; + case SDLK_DOWN: + mapPos[2] += 1.0f; + cam.translateBy(Vector3D(0, 0, 1)); + break; + case SDLK_SPACE: + cam.setSpeed(0.0f); + break; + case SDLK_F1: + cam.interpolate(Vector3D(254, 9, 254), 1, 20000); + break; + case SDLK_F2: + bbox_toggle = (bbox_toggle ? 0 : 1); + OpenGTA::SpriteManagerHolder::Instance().setDrawBBox(bbox_toggle); + break; + case SDLK_F3: + texsprite_toggle = (texsprite_toggle ? 0 : 1); + OpenGTA::SpriteManagerHolder::Instance().setDrawTexBorder(texsprite_toggle); + break; + case SDLK_F4: + follow_toggle = (follow_toggle ? 0 : 1); + if (follow_toggle) { + city->setViewMode(false); + Vector3D p(cam.getEye()); + create_ped_at(p); + cam.setVectors( Vector3D(p.x, 10, p.z), Vector3D(p.x, 0.0f, p.z), Vector3D(0, 0, -1) ); + cam.setFollowMode(OpenGTA::SpriteManagerHolder::Instance().getPedById(0xffffffff).pos); + } + else { + cam.setVectors(cam.getEye(), + Vector3D(cam.getEye() + Vector3D(1, -1, 1)), Vector3D(0, 1, 0)); + cam.releaseFollowMode(); + OpenGTA::SpriteManagerHolder::Instance().removePedById(0xffffffff); + } + break; + case SDLK_F5: + draw_arrows = (draw_arrows ? 0 : 1); + city->setDrawHeadingArrows(draw_arrows); + break; + case SDLK_F6: + draw_mapmode(); + break; + case SDLK_F9: + city->setDrawTextured(city->getDrawTextured() ? 0 : 1); + break; + case SDLK_F10: + city->setDrawLines(city->getDrawLines() ? 0 : 1); + break; + /* + case SDLK_F6: + tex_flip = (tex_flip ? 0 : 1); + INFO << "flipping: " << tex_flip << std::endl; + city->setTexFlipTest(tex_flip); + break; + */ + case '1': + OpenGTA::SpriteManagerHolder::Instance().getPedById(0xffffffff).equip(1); + break; + case '2': + OpenGTA::SpriteManagerHolder::Instance().getPedById(0xffffffff).equip(2); + break; + case '3': + OpenGTA::SpriteManagerHolder::Instance().getPedById(0xffffffff).equip(3); + break; + case '4': + OpenGTA::SpriteManagerHolder::Instance().getPedById(0xffffffff).equip(4); + break; + case '5': + OpenGTA::SpriteManagerHolder::Instance().getPedById(0xffffffff).equip(5); + break; + case '6': + OpenGTA::SpriteManagerHolder::Instance().getPedById(0xffffffff).equip(6); + break; + case '7': + OpenGTA::SpriteManagerHolder::Instance().getPedById(0xffffffff).equip(7); + break; + case '8': + OpenGTA::SpriteManagerHolder::Instance().getPedById(0xffffffff).equip(8); + break; + case '9': + OpenGTA::SpriteManagerHolder::Instance().getPedById(0xffffffff).equip(9); + /* + ped_anim -= 1; if (ped_anim < 0) ped_anim = 0; + pedAnim.firstFrameOffset = ped_anim; + INFO << "switching to sprite: " << ped_anim << std::endl; + OpenGTA::SpriteManagerHolder::Instance().getPedById(0xffffffff).setAnimation(pedAnim); + */ + break; + case '0': + /* + ped_anim += 1; if (ped_anim > 200) ped_anim = 200; + pedAnim.firstFrameOffset = ped_anim; + INFO << "switching to sprite: " << ped_anim << std::endl; + OpenGTA::SpriteManagerHolder::Instance().getPedById(0xffffffff).setAnimation(pedAnim); + */ + OpenGTA::SpriteManagerHolder::Instance().getPedById(0xffffffff).equip(0); + break; + case 'w': + cam.setSpeed(0.2f); + break; + case 's': + cam.setSpeed(-0.2f); + break; + case 'j': + OpenGTA::LocalPlayer::Instance().turn = 1; + break; + case 'l': + OpenGTA::LocalPlayer::Instance().turn = -1; + break; + case 'i': + OpenGTA::LocalPlayer::Instance().move = (player_toggle_run) ? 2 : 1; + break; + case 'k': + OpenGTA::LocalPlayer::Instance().move = -1; + break; + case SDLK_LSHIFT: + player_toggle_run = (player_toggle_run) ? false : true; + INFO << player_toggle_run << std::endl; + break; + case 'f': + OpenGL::ScreenHolder::Instance().toggleFullscreen(); +#ifdef WIN32 + city->resetTextures(); + m_font->resetTextures(); + OpenGL::SpriteCacheHolder::Instance().clearAll(); +#endif + break; + case 'r': + rotate = (rotate) ? false : true; + cam.setRotating(rotate); + break; + case 'g': + cam_grav = (cam_grav) ? false : true; + cam.setCamGravity(cam_grav); + break; + case 't': + mapPos[0] = mapPos[2] = 128; + mapPos[1] = 230; + city->setVisibleRange(128); + break; + case 'p': + print_position(); + break; + case '+': + mapPos[1] += 1.0f; + cam.translateBy(Vector3D(0, 1, 0)); + break; + case '-': + mapPos[1] -= 1.0f; + cam.translateBy(Vector3D(0, -1, 0)); + break; + case 'x': + city->setViewMode(false); + city->setVisibleRange(city->getVisibleRange() * 2); + break; + case 'y': + break; + case 'z': + city->setViewMode(true); + city->setVisibleRange(city->getVisibleRange() / 2); + break; + case '.': + city->setVisibleRange(city->getVisibleRange()-1); + INFO << " new visible range " << city->getVisibleRange() << std::endl; + break; + case ',': + city->setVisibleRange(city->getVisibleRange()+1); + INFO << " new visible range " << city->getVisibleRange() << std::endl; + break; + case SDLK_PRINT: + OpenGL::ScreenHolder::Instance().makeScreenshot("screenshot.bmp"); + break; + default: + return; + } + city->setPosition(mapPos[0], mapPos[1], mapPos[2]); + +} + +void drawScene(Uint32 ticks) { + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + OpenGL::ScreenHolder::Instance().set3DProjection(); + city->draw(ticks); + + OpenGL::ScreenHolder::Instance().setFlatProjection(); + + glPushMatrix(); + glTranslatef(10, 10, 0); + m_font->drawString(city->getCurrentSector()->getFullName()); + glPopMatrix(); + glTranslatef(5, 50, 0); + std::ostringstream strstr; + strstr << fps << " fps"; + m_font->drawString(strstr.str()); + + num_frames_drawn += 1; + + SDL_GL_SwapBuffers(); +} + +void draw_mapmode() { + SDL_Event event; + OpenGL::PagedTexture map_tex = city->renderMap2Texture(); + bool done_map = false; + OpenGL::Screen & screen = OpenGL::ScreenHolder::Instance(); + screen.setSystemMouseCursor(true); + GLfloat _scale = 1; + GLfloat dx = 0; + GLfloat dy = 0; + while(!done_map) { + while (SDL_PollEvent(&event)) { + switch(event.type) { + case SDL_QUIT: + done_map = true; + break; + case SDL_KEYDOWN: + switch(event.key.keysym.sym) { + case SDLK_ESCAPE: + done_map = true; + break; + case '-': + _scale += 0.1f; + break; + case '+': + _scale -= 0.1f; + break; + case SDLK_LEFT: + dx -= 0.1f; + break; + case SDLK_RIGHT: + dx += 0.1f; + break; + case SDLK_UP: + dy += 0.1f; + break; + case SDLK_DOWN: + dy -= 0.1f; + break; + default: + break; + } + break; + case SDL_MOUSEMOTION: +// std::cout << "Mouse move: x " << event.motion.x << " y " << +// event.motion.y << std::endl; + break; + default: + break; + } + } + + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + screen.setFlatProjection(); + glBindTexture(GL_TEXTURE_2D, map_tex.inPage); + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glScalef(_scale, _scale, 1); + glTranslatef(dx, dy, 0); + + glBegin(GL_QUADS); + glTexCoord2f(map_tex.coords[0].u, map_tex.coords[0].v); + glVertex2i(0, 0); + glTexCoord2f(map_tex.coords[1].u, map_tex.coords[0].v); + glVertex2i(screen.getWidth(), 0); + glTexCoord2f(map_tex.coords[1].u, map_tex.coords[1].v); + glVertex2i(screen.getWidth(), screen.getHeight()); + glTexCoord2f(map_tex.coords[0].u, map_tex.coords[1].v); + glVertex2i(0, screen.getHeight()); + glEnd(); + + SDL_GL_SwapBuffers(); + SDL_Delay(20); + + } + screen.setSystemMouseCursor(false); + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + // the texture class doesn't cleanup! + glDeleteTextures(1, &map_tex.inPage); +} + +void parse_args(int argc, char* argv[]) { + int index; + int c; + + opterr = 0; + +#ifdef WITH_LUA +#define VIEWER_FLAGS "s:w:h:c:m:g:l:f" +#else +#define VIEWER_FLAGS "w:h:c:m:g:l:f" +#endif + while ((c = getopt (argc, argv, VIEWER_FLAGS)) != -1) + switch (c) + { +#ifdef WITH_LUA + case 's': + script_file = optarg; + break; +#endif + case 'c': + highcolor_data = atoi(optarg); + break; + case 'm': + specific_map = std::string(optarg); + break; + case 'g': + specific_style = std::string(optarg); + break; + case 'w': + arg_screen_w = atoi(optarg); + break; + case 'h': + arg_screen_h = atoi(optarg); + break; + case 'l': + Util::Log::setOutputLevel(atoi(optarg)); + break; + case 'f': + full_screen = true; + break; + case '?': + if (isprint (optopt)) + ERROR << "Unknown option `-" << char(optopt) << "'" << std::endl; + else + ERROR << "Unknown option character `" << optopt << "'" << std::endl; + default: + abort (); + } + + for (index = optind; index < argc; index++) + city_num = atoi(argv[index]); + + if (city_num > 2) { + ERROR << "Invalid city number: " << city_num << std::endl; + exit(1); + } +} + +void run_init() { + PHYSFS_init("mapview"); + //PHYSFS_addToSearchPath("gtadata.zip", 1); + Util::FileHelper & fh = GET_FILE_HELPER; + if (fh.existsInSystemFS(fh.getBaseDataPath())) + PHYSFS_addToSearchPath(GET_FILE_HELPER.getBaseDataPath().c_str(), 1); + else { + WARN << "Could not load data-source: " << fh.getBaseDataPath() <<" -- fallback to current directory"<< std::endl; + PHYSFS_addToSearchPath(PHYSFS_getBaseDir(), 1); + } + if (fh.existsInSystemFS(fh.getModDataPath())) + PHYSFS_addToSearchPath(GET_FILE_HELPER.getModDataPath().c_str(), 0); + + OpenGL::Screen & screen = OpenGL::ScreenHolder::Instance(); + screen.setFullScreenFlag(full_screen); + screen.activate(arg_screen_w, arg_screen_h); + SDL_EnableKeyRepeat( 100, SDL_DEFAULT_REPEAT_INTERVAL ); +} + +void run_main() { + SDL_Event event; + + m_font = new OpenGL::DrawableFont(); + m_font->loadFont("F_MTEXT.FON"); + m_font->setScale(1); + + glEnable(GL_TEXTURE_2D); + glPolygonMode(GL_FRONT, GL_FILL); + glEnable(GL_CULL_FACE); + + //glEnable(GL_BLEND); + //glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0); + + city = new OpenGTA::CityView(); + if (specific_map.size() > 0 && specific_style.size() > 0) { + city->loadMap(specific_map, specific_style); + } + else { + if (highcolor_data) + city->loadMap(cities[city_num], styles_24[city_num]); + else + city->loadMap(cities[city_num], styles_8[city_num]); + } + city->setPosition(mapPos[0], mapPos[1], mapPos[2]); + + OpenGL::Camera & cam = OpenGL::CameraHolder::Instance(); + //cam.setVectors( Vector3D(4, 10, 4), Vector3D(4, 0.0f, 4.0f), Vector3D(0, 0, -1) ); + cam.setVectors( Vector3D(12, 20, 12), Vector3D(13.0f, 19.0f, 13.0f), Vector3D(0, 1, 0) ); + + last_tick = SDL_GetTicks(); +#ifdef WITH_LUA + OpenGTA::Script::LuaVM & vm = OpenGTA::Script::LuaVMHolder::Instance(); + vm.setCityView(*city); + if (script_file) + vm.runFile(script_file); + //vm.runString("function game_tick() print('tick...') end"); + bool vm_tick_ok = true; + script_last_tick = last_tick; +#endif + + + while(!global_Done && !global_EC) { + while (SDL_PollEvent(&event)) { + switch(event.type) { + case SDL_ACTIVEEVENT: + if (event.active.gain == 0) + paused = 1; + else + paused = 0; + break; + case SDL_KEYDOWN: + handleKeyPress(&event.key.keysym); + break; + case SDL_KEYUP: + handleKeyUp(&event.key.keysym); + break; + case SDL_VIDEORESIZE: + OpenGL::ScreenHolder::Instance().resize(event.resize.w, event.resize.h); + break; + case SDL_QUIT: + global_Done = 1; + break; + case SDL_MOUSEMOTION: + //std::cout << "Mouse move: x " << float(event.motion.x)/screen->w << " y " << float(event.motion.y)/screen->h << std::endl; + break; + default: + break; + } + } + Uint32 now_ticks = SDL_GetTicks(); + OpenGTA::SpriteManagerHolder::Instance().update(now_ticks); + if (!paused) { + drawScene(now_ticks - last_tick); + last_tick = now_ticks; +#ifdef WITH_LUA + if (vm_tick_ok && (now_ticks - script_last_tick > 100)) { + try { + vm.callSimpleFunction("game_tick"); + script_last_tick = now_ticks; + } + catch (Exception & e) { + vm_tick_ok = false; + ERROR << "Disabling script game_tick because of error: " << e.what() << std::endl; + } + } +#endif + } + if (now_ticks - fps_last_tick > 2000) { + fps = num_frames_drawn / 2; + num_frames_drawn = 0; + fps_last_tick = now_ticks; + } +// SDL_Delay(10); + } +}