Initial commit
This commit is contained in:
commit
7cad314c94
46
Makefile
Executable file
46
Makefile
Executable file
@ -0,0 +1,46 @@
|
||||
-include mk/conf.mk
|
||||
|
||||
ifndef CONFIG_STATUS
|
||||
doConfigure:
|
||||
$(error Configuration file not defined. Please run $(MAKE) -f mk/configure.mk)
|
||||
#@$(MAKE) --no-print-directory -f ../mk/configure.mk
|
||||
else
|
||||
ifeq ($(CONFIG_STATUS),INVALID)
|
||||
doConfigure:
|
||||
$(error Invalid Configuration file. Please run $(MAKE) -f mk/configure.mk)
|
||||
#@$(MAKE) --no-print-directory -f mk/configure.mk
|
||||
endif
|
||||
endif
|
||||
|
||||
default:
|
||||
@$(MAKE) -s -C lib default
|
||||
@$(MAKE) -s -C engine
|
||||
|
||||
.PHONY: tools engine dedicated docs clean
|
||||
|
||||
tools:
|
||||
@$(MAKE) -s -C tools
|
||||
|
||||
dedicated:
|
||||
@$(MAKE) -s -C lib
|
||||
@$(MAKE) -s -C engine dedicated
|
||||
|
||||
engine:
|
||||
@$(MAKE) -s -C lib
|
||||
@$(MAKE) -s -C engine engine
|
||||
|
||||
html_docs:
|
||||
# Assumes Doxygen is in your path
|
||||
@doxygen doc/doxygen/html/doxygen.html.cfg
|
||||
|
||||
php_docs:
|
||||
# Internal gg use only
|
||||
# Assumes Doxygen is in your path
|
||||
@doxygen doc/doxygen/php/doxygen.php.cfg
|
||||
|
||||
all: default tools
|
||||
|
||||
clean:
|
||||
@$(MAKE) -s -C engine clean
|
||||
@$(MAKE) -s -C lib clean
|
||||
@$(MAKE) -s -C tools clean
|
72
ReadMe.html
Executable file
72
ReadMe.html
Executable file
@ -0,0 +1,72 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
||||
<html>
|
||||
<head>
|
||||
<title>Torque Engine SDK</title>
|
||||
</head>
|
||||
<body>
|
||||
<!-------------------------------------------------------->
|
||||
|
||||
<h1>Torque Game Engine SDK</h1>
|
||||
|
||||
<p>Welcome to the <a href="http://www.garagegames.com">GarageGames</a>
|
||||
Torque Game Engine SDK.</p>
|
||||
<ul>
|
||||
<li><a href="#started">Getting Started</a></li>
|
||||
<li><a href="#homepage">SDK Home Page</a></li>
|
||||
<li><a href="#forums">Forums</a></li>
|
||||
<li><a href="#faq">Frequently Asked Questions (FAQ)</a></li>
|
||||
<li><a href="#support">Support</a></li>
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
|
||||
<hr>
|
||||
<h3 id="started">Getting Started</h3>
|
||||
|
||||
<p>
|
||||
This SDK release includes the Torque Engine, related
|
||||
tools and the demo application. Binaries and sources are included for all
|
||||
applications. All Torque SDK documentation is found online on the
|
||||
<a href="http://www.garagegames.com/docs/torque/index.php">Torque Game Engine SDK Documentation</a>
|
||||
home page. You must be logged into the GarageGames site for all the documentation to be visible.
|
||||
Documentation is divided into two main books:</p>
|
||||
<ul>
|
||||
<li><b>General Torque Documentation</b>, which includes a more complete section on getting started
|
||||
as well as information on using the demo application, compiling the torque engine, using the torque editors,
|
||||
scripting language basics, etc.</li>
|
||||
<li><b>Torque Source Code Documentation</b>, which is engine API and source level documentation. This is
|
||||
for more advanced users and is generated from documentation imbeded in the source code using a tool
|
||||
called Doxygen.</li>
|
||||
</ul>
|
||||
|
||||
<hr>
|
||||
<h3 id="homepage">SDK Home Page</h3>
|
||||
<p>The <a href="http://www.garagegames.com/mg/projects/torque1/">Torque SDK home page</a> contains links to
|
||||
all the documentation as well as tutorials, example code and all other things Torque related. This should be
|
||||
your first stop for information regarding the SDK.</p>
|
||||
|
||||
<hr>
|
||||
<h3 id="forums">Forums</h3>
|
||||
<p>The Torque SDK has a large online community located on the GarageGames
|
||||
<a href="http://www.garagegames.com/mg/forums/result.area.php">forums</a>. Forums dedicated to the Torque engine
|
||||
include both the <a href="http://www.garagegames.com/mg/forums/result.area.php?qa=8">public</a> and
|
||||
<a href="http://www.garagegames.com/mg/forums/result.area.php?qa=10">private</a> SDK forums. To access the private
|
||||
forums you must be logged in to the GarageGames site.</p>
|
||||
|
||||
<hr>
|
||||
<h3 id="faq">FAQ</h3>
|
||||
<p>Visit the online <a href="http://www.garagegames.com/index.php?sec=mg&mod=resource&page=category&qid=118">FAQ</a> resources</li>
|
||||
for frequently answered questions regarding the engine SDK and tools.</p>
|
||||
|
||||
<hr>
|
||||
<h3 id="support">Support</h3>
|
||||
<p>Support is provided exclusively through the online <a href="http://www.garagegames.com/mg/forums/result.area.php?qa=10">Torque SDK Private Forums</a>
|
||||
and <a href="http://www.garagegames.com/index.php?sec=mg&mod=resource&page=category&qid=118">FAQ</a> resources</li>.
|
||||
Please make sure you read through the FAQs before posting questions. The Private forums are only visible if you are logged into the
|
||||
GarageGames website.</p>
|
||||
|
||||
<p>- GarageGames Staff</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
BIN
bin/bison/bison.exe
Executable file
BIN
bin/bison/bison.exe
Executable file
Binary file not shown.
334
bin/bison/bison.hairy
Executable file
334
bin/bison/bison.hairy
Executable file
@ -0,0 +1,334 @@
|
||||
|
||||
extern int timeclock;
|
||||
|
||||
|
||||
int yyerror; /* Yyerror and yycost are set by guards. */
|
||||
int yycost; /* If yyerror is set to a nonzero value by a */
|
||||
/* guard, the reduction with which the guard */
|
||||
/* is associated is not performed, and the */
|
||||
/* error recovery mechanism is invoked. */
|
||||
/* Yycost indicates the cost of performing */
|
||||
/* the reduction given the attributes of the */
|
||||
/* symbols. */
|
||||
|
||||
|
||||
/* YYMAXDEPTH indicates the size of the parser's state and value */
|
||||
/* stacks. */
|
||||
|
||||
#ifndef YYMAXDEPTH
|
||||
#define YYMAXDEPTH 500
|
||||
#endif
|
||||
|
||||
/* YYMAXRULES must be at least as large as the number of rules that */
|
||||
/* could be placed in the rule queue. That number could be determined */
|
||||
/* from the grammar and the size of the stack, but, as yet, it is not. */
|
||||
|
||||
#ifndef YYMAXRULES
|
||||
#define YYMAXRULES 100
|
||||
#endif
|
||||
|
||||
#ifndef YYMAXBACKUP
|
||||
#define YYMAXBACKUP 100
|
||||
#endif
|
||||
|
||||
|
||||
short yyss[YYMAXDEPTH]; /* the state stack */
|
||||
YYSTYPE yyvs[YYMAXDEPTH]; /* the semantic value stack */
|
||||
YYLTYPE yyls[YYMAXDEPTH]; /* the location stack */
|
||||
short yyrq[YYMAXRULES]; /* the rule queue */
|
||||
int yychar; /* the lookahead symbol */
|
||||
|
||||
YYSTYPE yylval; /* the semantic value of the */
|
||||
/* lookahead symbol */
|
||||
|
||||
YYSTYPE yytval; /* the semantic value for the state */
|
||||
/* at the top of the state stack. */
|
||||
|
||||
YYSTYPE yyval; /* the variable used to return */
|
||||
/* semantic values from the action */
|
||||
/* routines */
|
||||
|
||||
YYLTYPE yylloc; /* location data for the lookahead */
|
||||
/* symbol */
|
||||
|
||||
YYLTYPE yytloc; /* location data for the state at the */
|
||||
/* top of the state stack */
|
||||
|
||||
|
||||
int yynunlexed;
|
||||
short yyunchar[YYMAXBACKUP];
|
||||
YYSTYPE yyunval[YYMAXBACKUP];
|
||||
YYLTYPE yyunloc[YYMAXBACKUP];
|
||||
|
||||
short *yygssp; /* a pointer to the top of the state */
|
||||
/* stack; only set during error */
|
||||
/* recovery. */
|
||||
|
||||
YYSTYPE *yygvsp; /* a pointer to the top of the value */
|
||||
/* stack; only set during error */
|
||||
/* recovery. */
|
||||
|
||||
YYLTYPE *yyglsp; /* a pointer to the top of the */
|
||||
/* location stack; only set during */
|
||||
/* error recovery. */
|
||||
|
||||
|
||||
/* Yyget is an interface between the parser and the lexical analyzer. */
|
||||
/* It is costly to provide such an interface, but it avoids requiring */
|
||||
/* the lexical analyzer to be able to back up the scan. */
|
||||
|
||||
yyget()
|
||||
{
|
||||
if (yynunlexed > 0)
|
||||
{
|
||||
yynunlexed--;
|
||||
yychar = yyunchar[yynunlexed];
|
||||
yylval = yyunval[yynunlexed];
|
||||
yylloc = yyunloc[yynunlexed];
|
||||
}
|
||||
else if (yychar <= 0)
|
||||
yychar = 0;
|
||||
else
|
||||
{
|
||||
yychar = yylex();
|
||||
if (yychar < 0)
|
||||
yychar = 0;
|
||||
else yychar = YYTRANSLATE(yychar);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
yyunlex(chr, val, loc)
|
||||
int chr;
|
||||
YYSTYPE val;
|
||||
YYLTYPE loc;
|
||||
{
|
||||
yyunchar[yynunlexed] = chr;
|
||||
yyunval[yynunlexed] = val;
|
||||
yyunloc[yynunlexed] = loc;
|
||||
yynunlexed++;
|
||||
}
|
||||
|
||||
|
||||
|
||||
yyrestore(first, last)
|
||||
register short *first;
|
||||
register short *last;
|
||||
{
|
||||
register short *ssp;
|
||||
register short *rp;
|
||||
register int symbol;
|
||||
register int state;
|
||||
register int tvalsaved;
|
||||
|
||||
ssp = yygssp;
|
||||
yyunlex(yychar, yylval, yylloc);
|
||||
|
||||
tvalsaved = 0;
|
||||
while (first != last)
|
||||
{
|
||||
symbol = yystos[*ssp];
|
||||
if (symbol < YYNTBASE)
|
||||
{
|
||||
yyunlex(symbol, yytval, yytloc);
|
||||
tvalsaved = 1;
|
||||
ssp--;
|
||||
}
|
||||
|
||||
ssp--;
|
||||
|
||||
if (first == yyrq)
|
||||
first = yyrq + YYMAXRULES;
|
||||
|
||||
first--;
|
||||
|
||||
for (rp = yyrhs + yyprhs[*first]; symbol = *rp; rp++)
|
||||
{
|
||||
if (symbol < YYNTBASE)
|
||||
state = yytable[yypact[*ssp] + symbol];
|
||||
else
|
||||
{
|
||||
state = yypgoto[symbol - YYNTBASE] + *ssp;
|
||||
|
||||
if (state >= 0 && state <= YYLAST && yycheck[state] == *ssp)
|
||||
state = yytable[state];
|
||||
else
|
||||
state = yydefgoto[symbol - YYNTBASE];
|
||||
}
|
||||
|
||||
*++ssp = state;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! tvalsaved && ssp > yyss)
|
||||
{
|
||||
yyunlex(yystos[*ssp], yytval, yytloc);
|
||||
ssp--;
|
||||
}
|
||||
|
||||
yygssp = ssp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int
|
||||
yyparse()
|
||||
{
|
||||
register int yystate;
|
||||
register int yyn;
|
||||
register short *yyssp;
|
||||
register short *yyrq0;
|
||||
register short *yyptr;
|
||||
register YYSTYPE *yyvsp;
|
||||
|
||||
int yylen;
|
||||
YYLTYPE *yylsp;
|
||||
short *yyrq1;
|
||||
short *yyrq2;
|
||||
|
||||
yystate = 0;
|
||||
yyssp = yyss - 1;
|
||||
yyvsp = yyvs - 1;
|
||||
yylsp = yyls - 1;
|
||||
yyrq0 = yyrq;
|
||||
yyrq1 = yyrq0;
|
||||
yyrq2 = yyrq0;
|
||||
|
||||
yychar = yylex();
|
||||
if (yychar < 0)
|
||||
yychar = 0;
|
||||
else yychar = YYTRANSLATE(yychar);
|
||||
|
||||
yynewstate:
|
||||
|
||||
if (yyssp >= yyss + YYMAXDEPTH - 1)
|
||||
{
|
||||
yyabort("Parser Stack Overflow");
|
||||
YYABORT;
|
||||
}
|
||||
|
||||
*++yyssp = yystate;
|
||||
|
||||
yyresume:
|
||||
|
||||
yyn = yypact[yystate];
|
||||
if (yyn == YYFLAG)
|
||||
goto yydefault;
|
||||
|
||||
yyn += yychar;
|
||||
if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != yychar)
|
||||
goto yydefault;
|
||||
|
||||
yyn = yytable[yyn];
|
||||
if (yyn < 0)
|
||||
{
|
||||
yyn = -yyn;
|
||||
goto yyreduce;
|
||||
}
|
||||
else if (yyn == 0)
|
||||
goto yyerrlab;
|
||||
|
||||
yystate = yyn;
|
||||
|
||||
yyptr = yyrq2;
|
||||
while (yyptr != yyrq1)
|
||||
{
|
||||
yyn = *yyptr++;
|
||||
yylen = yyr2[yyn];
|
||||
yyvsp -= yylen;
|
||||
yylsp -= yylen;
|
||||
|
||||
yyguard(yyn, yyvsp, yylsp);
|
||||
if (yyerror)
|
||||
goto yysemerr;
|
||||
|
||||
yyaction(yyn, yyvsp, yylsp);
|
||||
*++yyvsp = yyval;
|
||||
|
||||
yylsp++;
|
||||
if (yylen == 0)
|
||||
{
|
||||
yylsp->timestamp = timeclock;
|
||||
yylsp->first_line = yytloc.first_line;
|
||||
yylsp->first_column = yytloc.first_column;
|
||||
yylsp->last_line = (yylsp-1)->last_line;
|
||||
yylsp->last_column = (yylsp-1)->last_column;
|
||||
yylsp->text = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
yylsp->last_line = (yylsp+yylen-1)->last_line;
|
||||
yylsp->last_column = (yylsp+yylen-1)->last_column;
|
||||
}
|
||||
|
||||
if (yyptr == yyrq + YYMAXRULES)
|
||||
yyptr = yyrq;
|
||||
}
|
||||
|
||||
if (yystate == YYFINAL)
|
||||
YYACCEPT;
|
||||
|
||||
yyrq2 = yyptr;
|
||||
yyrq1 = yyrq0;
|
||||
|
||||
*++yyvsp = yytval;
|
||||
*++yylsp = yytloc;
|
||||
yytval = yylval;
|
||||
yytloc = yylloc;
|
||||
yyget();
|
||||
|
||||
goto yynewstate;
|
||||
|
||||
yydefault:
|
||||
|
||||
yyn = yydefact[yystate];
|
||||
if (yyn == 0)
|
||||
goto yyerrlab;
|
||||
|
||||
yyreduce:
|
||||
|
||||
*yyrq0++ = yyn;
|
||||
|
||||
if (yyrq0 == yyrq + YYMAXRULES)
|
||||
yyrq0 = yyrq;
|
||||
|
||||
if (yyrq0 == yyrq2)
|
||||
{
|
||||
yyabort("Parser Rule Queue Overflow");
|
||||
YYABORT;
|
||||
}
|
||||
|
||||
yyssp -= yyr2[yyn];
|
||||
yyn = yyr1[yyn];
|
||||
|
||||
yystate = yypgoto[yyn - YYNTBASE] + *yyssp;
|
||||
if (yystate >= 0 && yystate <= YYLAST && yycheck[yystate] == *yyssp)
|
||||
yystate = yytable[yystate];
|
||||
else
|
||||
yystate = yydefgoto[yyn - YYNTBASE];
|
||||
|
||||
goto yynewstate;
|
||||
|
||||
yysemerr:
|
||||
*--yyptr = yyn;
|
||||
yyrq2 = yyptr;
|
||||
yyvsp += yyr2[yyn];
|
||||
|
||||
yyerrlab:
|
||||
|
||||
yygssp = yyssp;
|
||||
yygvsp = yyvsp;
|
||||
yyglsp = yylsp;
|
||||
yyrestore(yyrq0, yyrq2);
|
||||
yyrecover();
|
||||
yystate = *yygssp;
|
||||
yyssp = yygssp;
|
||||
yyvsp = yygvsp;
|
||||
yyrq0 = yyrq;
|
||||
yyrq1 = yyrq0;
|
||||
yyrq2 = yyrq0;
|
||||
goto yyresume;
|
||||
}
|
||||
|
||||
$
|
4501
bin/bison/bison.html
Executable file
4501
bin/bison/bison.html
Executable file
File diff suppressed because it is too large
Load Diff
686
bin/bison/bison.simple
Executable file
686
bin/bison/bison.simple
Executable file
@ -0,0 +1,686 @@
|
||||
/* -*-C-*- Note some compilers choke on comments on `#line' lines. */
|
||||
#line 3 "bison.simple"
|
||||
|
||||
/* Skeleton output parser for bison,
|
||||
Copyright (C) 1984, 1989, 1990 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||||
|
||||
/* As a special exception, when this file is copied by Bison into a
|
||||
Bison output file, you may use that output file without restriction.
|
||||
This special exception was added by the Free Software Foundation
|
||||
in version 1.24 of Bison. */
|
||||
|
||||
#ifndef alloca
|
||||
#ifdef __GNUC__
|
||||
#define alloca __builtin_alloca
|
||||
#else /* not GNU C. */
|
||||
#if (!defined (__STDC__) && defined (sparc)) || defined (__sparc__) || defined (__sparc) || defined (__sgi)
|
||||
#include <alloca.h>
|
||||
#else /* not sparc */
|
||||
#if defined (MSDOS) && !defined (__TURBOC__)
|
||||
#include <malloc.h>
|
||||
#else /* not MSDOS, or __TURBOC__ */
|
||||
#if defined(_AIX)
|
||||
#include <malloc.h>
|
||||
#pragma alloca
|
||||
#else /* not MSDOS, __TURBOC__, or _AIX */
|
||||
#ifdef __hpux
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
void *alloca (unsigned int);
|
||||
};
|
||||
#else /* not __cplusplus */
|
||||
void *alloca ();
|
||||
#endif /* not __cplusplus */
|
||||
#endif /* __hpux */
|
||||
#endif /* not _AIX */
|
||||
#endif /* not MSDOS, or __TURBOC__ */
|
||||
#endif /* not sparc. */
|
||||
#endif /* not GNU C. */
|
||||
#endif /* alloca not defined. */
|
||||
|
||||
/* This is the parser code that is written into each bison parser
|
||||
when the %semantic_parser declaration is not specified in the grammar.
|
||||
It was written by Richard Stallman by simplifying the hairy parser
|
||||
used when %semantic_parser is specified. */
|
||||
|
||||
/* Note: there must be only one dollar sign in this file.
|
||||
It is replaced by the list of actions, each action
|
||||
as one case of the switch. */
|
||||
|
||||
#define yyerrok (yyerrstatus = 0)
|
||||
#define yyclearin (yychar = YYEMPTY)
|
||||
#define YYEMPTY -2
|
||||
#define YYEOF 0
|
||||
#define YYACCEPT return(0)
|
||||
#define YYABORT return(1)
|
||||
#define YYERROR goto yyerrlab1
|
||||
/* Like YYERROR except do call yyerror.
|
||||
This remains here temporarily to ease the
|
||||
transition to the new meaning of YYERROR, for GCC.
|
||||
Once GCC version 2 has supplanted version 1, this can go. */
|
||||
#define YYFAIL goto yyerrlab
|
||||
#define YYRECOVERING() (!!yyerrstatus)
|
||||
#define YYBACKUP(token, value) \
|
||||
do \
|
||||
if (yychar == YYEMPTY && yylen == 1) \
|
||||
{ yychar = (token), yylval = (value); \
|
||||
yychar1 = YYTRANSLATE (yychar); \
|
||||
YYPOPSTACK; \
|
||||
goto yybackup; \
|
||||
} \
|
||||
else \
|
||||
{ yyerror ("syntax error: cannot back up"); YYERROR; } \
|
||||
while (0)
|
||||
|
||||
#define YYTERROR 1
|
||||
#define YYERRCODE 256
|
||||
|
||||
#ifndef YYPURE
|
||||
#define YYLEX yylex()
|
||||
#endif
|
||||
|
||||
#ifdef YYPURE
|
||||
#ifdef YYLSP_NEEDED
|
||||
#ifdef YYLEX_PARAM
|
||||
#define YYLEX yylex(&yylval, &yylloc, YYLEX_PARAM)
|
||||
#else
|
||||
#define YYLEX yylex(&yylval, &yylloc)
|
||||
#endif
|
||||
#else /* not YYLSP_NEEDED */
|
||||
#ifdef YYLEX_PARAM
|
||||
#define YYLEX yylex(&yylval, YYLEX_PARAM)
|
||||
#else
|
||||
#define YYLEX yylex(&yylval)
|
||||
#endif
|
||||
#endif /* not YYLSP_NEEDED */
|
||||
#endif
|
||||
|
||||
/* If nonreentrant, generate the variables here */
|
||||
|
||||
#ifndef YYPURE
|
||||
|
||||
int yychar; /* the lookahead symbol */
|
||||
YYSTYPE yylval; /* the semantic value of the */
|
||||
/* lookahead symbol */
|
||||
|
||||
#ifdef YYLSP_NEEDED
|
||||
YYLTYPE yylloc; /* location data for the lookahead */
|
||||
/* symbol */
|
||||
#endif
|
||||
|
||||
int yynerrs; /* number of parse errors so far */
|
||||
#endif /* not YYPURE */
|
||||
|
||||
#if YYDEBUG != 0
|
||||
int yydebug; /* nonzero means print parse trace */
|
||||
/* Since this is uninitialized, it does not stop multiple parsers
|
||||
from coexisting. */
|
||||
#endif
|
||||
|
||||
/* YYINITDEPTH indicates the initial size of the parser's stacks */
|
||||
|
||||
#ifndef YYINITDEPTH
|
||||
#define YYINITDEPTH 200
|
||||
#endif
|
||||
|
||||
/* YYMAXDEPTH is the maximum size the stacks can grow to
|
||||
(effective only if the built-in stack extension method is used). */
|
||||
|
||||
#if YYMAXDEPTH == 0
|
||||
#undef YYMAXDEPTH
|
||||
#endif
|
||||
|
||||
#ifndef YYMAXDEPTH
|
||||
#define YYMAXDEPTH 10000
|
||||
#endif
|
||||
|
||||
/* Prevent warning if -Wstrict-prototypes. */
|
||||
#ifdef __GNUC__
|
||||
int yyparse (void);
|
||||
#endif
|
||||
|
||||
#if __GNUC__ > 1 /* GNU C and GNU C++ define this. */
|
||||
#define __yy_memcpy(FROM,TO,COUNT) __builtin_memcpy(TO,FROM,COUNT)
|
||||
#else /* not GNU C or C++ */
|
||||
#ifndef __cplusplus
|
||||
|
||||
/* This is the most reliable way to avoid incompatibilities
|
||||
in available built-in functions on various systems. */
|
||||
static void
|
||||
__yy_memcpy (from, to, count)
|
||||
char *from;
|
||||
char *to;
|
||||
int count;
|
||||
{
|
||||
register char *f = from;
|
||||
register char *t = to;
|
||||
register int i = count;
|
||||
|
||||
while (i-- > 0)
|
||||
*t++ = *f++;
|
||||
}
|
||||
|
||||
#else /* __cplusplus */
|
||||
|
||||
/* This is the most reliable way to avoid incompatibilities
|
||||
in available built-in functions on various systems. */
|
||||
static void
|
||||
__yy_memcpy (char *from, char *to, int count)
|
||||
{
|
||||
register char *f = from;
|
||||
register char *t = to;
|
||||
register int i = count;
|
||||
|
||||
while (i-- > 0)
|
||||
*t++ = *f++;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#line 192 "bison.simple"
|
||||
|
||||
/* The user can define YYPARSE_PARAM as the name of an argument to be passed
|
||||
into yyparse. The argument should have type void *.
|
||||
It should actually point to an object.
|
||||
Grammar actions can access the variable by casting it
|
||||
to the proper pointer type. */
|
||||
|
||||
#ifdef YYPARSE_PARAM
|
||||
#define YYPARSE_PARAM_DECL void *YYPARSE_PARAM;
|
||||
#else
|
||||
#define YYPARSE_PARAM
|
||||
#define YYPARSE_PARAM_DECL
|
||||
#endif
|
||||
|
||||
int
|
||||
yyparse(YYPARSE_PARAM)
|
||||
YYPARSE_PARAM_DECL
|
||||
{
|
||||
register int yystate;
|
||||
register int yyn;
|
||||
register short *yyssp;
|
||||
register YYSTYPE *yyvsp;
|
||||
int yyerrstatus; /* number of tokens to shift before error messages enabled */
|
||||
int yychar1 = 0; /* lookahead token as an internal (translated) token number */
|
||||
|
||||
short yyssa[YYINITDEPTH]; /* the state stack */
|
||||
YYSTYPE yyvsa[YYINITDEPTH]; /* the semantic value stack */
|
||||
|
||||
short *yyss = yyssa; /* refer to the stacks thru separate pointers */
|
||||
YYSTYPE *yyvs = yyvsa; /* to allow yyoverflow to reallocate them elsewhere */
|
||||
|
||||
#ifdef YYLSP_NEEDED
|
||||
YYLTYPE yylsa[YYINITDEPTH]; /* the location stack */
|
||||
YYLTYPE *yyls = yylsa;
|
||||
YYLTYPE *yylsp;
|
||||
|
||||
#define YYPOPSTACK (yyvsp--, yyssp--, yylsp--)
|
||||
#else
|
||||
#define YYPOPSTACK (yyvsp--, yyssp--)
|
||||
#endif
|
||||
|
||||
int yystacksize = YYINITDEPTH;
|
||||
|
||||
#ifdef YYPURE
|
||||
int yychar;
|
||||
YYSTYPE yylval;
|
||||
int yynerrs;
|
||||
#ifdef YYLSP_NEEDED
|
||||
YYLTYPE yylloc;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
YYSTYPE yyval; /* the variable used to return */
|
||||
/* semantic values from the action */
|
||||
/* routines */
|
||||
|
||||
int yylen;
|
||||
|
||||
#if YYDEBUG != 0
|
||||
if (yydebug)
|
||||
fprintf(stderr, "Starting parse\n");
|
||||
#endif
|
||||
|
||||
yystate = 0;
|
||||
yyerrstatus = 0;
|
||||
yynerrs = 0;
|
||||
yychar = YYEMPTY; /* Cause a token to be read. */
|
||||
|
||||
/* Initialize stack pointers.
|
||||
Waste one element of value and location stack
|
||||
so that they stay on the same level as the state stack.
|
||||
The wasted elements are never initialized. */
|
||||
|
||||
yyssp = yyss - 1;
|
||||
yyvsp = yyvs;
|
||||
#ifdef YYLSP_NEEDED
|
||||
yylsp = yyls;
|
||||
#endif
|
||||
|
||||
/* Push a new state, which is found in yystate . */
|
||||
/* In all cases, when you get here, the value and location stacks
|
||||
have just been pushed. so pushing a state here evens the stacks. */
|
||||
yynewstate:
|
||||
|
||||
*++yyssp = yystate;
|
||||
|
||||
if (yyssp >= yyss + yystacksize - 1)
|
||||
{
|
||||
/* Give user a chance to reallocate the stack */
|
||||
/* Use copies of these so that the &'s don't force the real ones into memory. */
|
||||
YYSTYPE *yyvs1 = yyvs;
|
||||
short *yyss1 = yyss;
|
||||
#ifdef YYLSP_NEEDED
|
||||
YYLTYPE *yyls1 = yyls;
|
||||
#endif
|
||||
|
||||
/* Get the current used size of the three stacks, in elements. */
|
||||
int size = yyssp - yyss + 1;
|
||||
|
||||
#ifdef yyoverflow
|
||||
/* Each stack pointer address is followed by the size of
|
||||
the data in use in that stack, in bytes. */
|
||||
#ifdef YYLSP_NEEDED
|
||||
/* This used to be a conditional around just the two extra args,
|
||||
but that might be undefined if yyoverflow is a macro. */
|
||||
yyoverflow("parser stack overflow",
|
||||
&yyss1, size * sizeof (*yyssp),
|
||||
&yyvs1, size * sizeof (*yyvsp),
|
||||
&yyls1, size * sizeof (*yylsp),
|
||||
&yystacksize);
|
||||
#else
|
||||
yyoverflow("parser stack overflow",
|
||||
&yyss1, size * sizeof (*yyssp),
|
||||
&yyvs1, size * sizeof (*yyvsp),
|
||||
&yystacksize);
|
||||
#endif
|
||||
|
||||
yyss = yyss1; yyvs = yyvs1;
|
||||
#ifdef YYLSP_NEEDED
|
||||
yyls = yyls1;
|
||||
#endif
|
||||
#else /* no yyoverflow */
|
||||
/* Extend the stack our own way. */
|
||||
if (yystacksize >= YYMAXDEPTH)
|
||||
{
|
||||
yyerror("parser stack overflow");
|
||||
return 2;
|
||||
}
|
||||
yystacksize *= 2;
|
||||
if (yystacksize > YYMAXDEPTH)
|
||||
yystacksize = YYMAXDEPTH;
|
||||
yyss = (short *) alloca (yystacksize * sizeof (*yyssp));
|
||||
__yy_memcpy ((char *)yyss1, (char *)yyss, size * sizeof (*yyssp));
|
||||
yyvs = (YYSTYPE *) alloca (yystacksize * sizeof (*yyvsp));
|
||||
__yy_memcpy ((char *)yyvs1, (char *)yyvs, size * sizeof (*yyvsp));
|
||||
#ifdef YYLSP_NEEDED
|
||||
yyls = (YYLTYPE *) alloca (yystacksize * sizeof (*yylsp));
|
||||
__yy_memcpy ((char *)yyls1, (char *)yyls, size * sizeof (*yylsp));
|
||||
#endif
|
||||
#endif /* no yyoverflow */
|
||||
|
||||
yyssp = yyss + size - 1;
|
||||
yyvsp = yyvs + size - 1;
|
||||
#ifdef YYLSP_NEEDED
|
||||
yylsp = yyls + size - 1;
|
||||
#endif
|
||||
|
||||
#if YYDEBUG != 0
|
||||
if (yydebug)
|
||||
fprintf(stderr, "Stack size increased to %d\n", yystacksize);
|
||||
#endif
|
||||
|
||||
if (yyssp >= yyss + yystacksize - 1)
|
||||
YYABORT;
|
||||
}
|
||||
|
||||
#if YYDEBUG != 0
|
||||
if (yydebug)
|
||||
fprintf(stderr, "Entering state %d\n", yystate);
|
||||
#endif
|
||||
|
||||
goto yybackup;
|
||||
yybackup:
|
||||
|
||||
/* Do appropriate processing given the current state. */
|
||||
/* Read a lookahead token if we need one and don't already have one. */
|
||||
/* yyresume: */
|
||||
|
||||
/* First try to decide what to do without reference to lookahead token. */
|
||||
|
||||
yyn = yypact[yystate];
|
||||
if (yyn == YYFLAG)
|
||||
goto yydefault;
|
||||
|
||||
/* Not known => get a lookahead token if don't already have one. */
|
||||
|
||||
/* yychar is either YYEMPTY or YYEOF
|
||||
or a valid token in external form. */
|
||||
|
||||
if (yychar == YYEMPTY)
|
||||
{
|
||||
#if YYDEBUG != 0
|
||||
if (yydebug)
|
||||
fprintf(stderr, "Reading a token: ");
|
||||
#endif
|
||||
yychar = YYLEX;
|
||||
}
|
||||
|
||||
/* Convert token to internal form (in yychar1) for indexing tables with */
|
||||
|
||||
if (yychar <= 0) /* This means end of input. */
|
||||
{
|
||||
yychar1 = 0;
|
||||
yychar = YYEOF; /* Don't call YYLEX any more */
|
||||
|
||||
#if YYDEBUG != 0
|
||||
if (yydebug)
|
||||
fprintf(stderr, "Now at end of input.\n");
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
yychar1 = YYTRANSLATE(yychar);
|
||||
|
||||
#if YYDEBUG != 0
|
||||
if (yydebug)
|
||||
{
|
||||
fprintf (stderr, "Next token is %d (%s", yychar, yytname[yychar1]);
|
||||
/* Give the individual parser a way to print the precise meaning
|
||||
of a token, for further debugging info. */
|
||||
#ifdef YYPRINT
|
||||
YYPRINT (stderr, yychar, yylval);
|
||||
#endif
|
||||
fprintf (stderr, ")\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
yyn += yychar1;
|
||||
if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != yychar1)
|
||||
goto yydefault;
|
||||
|
||||
yyn = yytable[yyn];
|
||||
|
||||
/* yyn is what to do for this token type in this state.
|
||||
Negative => reduce, -yyn is rule number.
|
||||
Positive => shift, yyn is new state.
|
||||
New state is final state => don't bother to shift,
|
||||
just return success.
|
||||
0, or most negative number => error. */
|
||||
|
||||
if (yyn < 0)
|
||||
{
|
||||
if (yyn == YYFLAG)
|
||||
goto yyerrlab;
|
||||
yyn = -yyn;
|
||||
goto yyreduce;
|
||||
}
|
||||
else if (yyn == 0)
|
||||
goto yyerrlab;
|
||||
|
||||
if (yyn == YYFINAL)
|
||||
YYACCEPT;
|
||||
|
||||
/* Shift the lookahead token. */
|
||||
|
||||
#if YYDEBUG != 0
|
||||
if (yydebug)
|
||||
fprintf(stderr, "Shifting token %d (%s), ", yychar, yytname[yychar1]);
|
||||
#endif
|
||||
|
||||
/* Discard the token being shifted unless it is eof. */
|
||||
if (yychar != YYEOF)
|
||||
yychar = YYEMPTY;
|
||||
|
||||
*++yyvsp = yylval;
|
||||
#ifdef YYLSP_NEEDED
|
||||
*++yylsp = yylloc;
|
||||
#endif
|
||||
|
||||
/* count tokens shifted since error; after three, turn off error status. */
|
||||
if (yyerrstatus) yyerrstatus--;
|
||||
|
||||
yystate = yyn;
|
||||
goto yynewstate;
|
||||
|
||||
/* Do the default action for the current state. */
|
||||
yydefault:
|
||||
|
||||
yyn = yydefact[yystate];
|
||||
if (yyn == 0)
|
||||
goto yyerrlab;
|
||||
|
||||
/* Do a reduction. yyn is the number of a rule to reduce with. */
|
||||
yyreduce:
|
||||
yylen = yyr2[yyn];
|
||||
if (yylen > 0)
|
||||
yyval = yyvsp[1-yylen]; /* implement default value of the action */
|
||||
|
||||
#if YYDEBUG != 0
|
||||
if (yydebug)
|
||||
{
|
||||
int i;
|
||||
|
||||
fprintf (stderr, "Reducing via rule %d (line %d), ",
|
||||
yyn, yyrline[yyn]);
|
||||
|
||||
/* Print the symbols being reduced, and their result. */
|
||||
for (i = yyprhs[yyn]; yyrhs[i] > 0; i++)
|
||||
fprintf (stderr, "%s ", yytname[yyrhs[i]]);
|
||||
fprintf (stderr, " -> %s\n", yytname[yyr1[yyn]]);
|
||||
}
|
||||
#endif
|
||||
|
||||
$ /* the action file gets copied in in place of this dollarsign */
|
||||
#line 487 "bison.simple"
|
||||
|
||||
yyvsp -= yylen;
|
||||
yyssp -= yylen;
|
||||
#ifdef YYLSP_NEEDED
|
||||
yylsp -= yylen;
|
||||
#endif
|
||||
|
||||
#if YYDEBUG != 0
|
||||
if (yydebug)
|
||||
{
|
||||
short *ssp1 = yyss - 1;
|
||||
fprintf (stderr, "state stack now");
|
||||
while (ssp1 != yyssp)
|
||||
fprintf (stderr, " %d", *++ssp1);
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
*++yyvsp = yyval;
|
||||
|
||||
#ifdef YYLSP_NEEDED
|
||||
yylsp++;
|
||||
if (yylen == 0)
|
||||
{
|
||||
yylsp->first_line = yylloc.first_line;
|
||||
yylsp->first_column = yylloc.first_column;
|
||||
yylsp->last_line = (yylsp-1)->last_line;
|
||||
yylsp->last_column = (yylsp-1)->last_column;
|
||||
yylsp->text = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
yylsp->last_line = (yylsp+yylen-1)->last_line;
|
||||
yylsp->last_column = (yylsp+yylen-1)->last_column;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Now "shift" the result of the reduction.
|
||||
Determine what state that goes to,
|
||||
based on the state we popped back to
|
||||
and the rule number reduced by. */
|
||||
|
||||
yyn = yyr1[yyn];
|
||||
|
||||
yystate = yypgoto[yyn - YYNTBASE] + *yyssp;
|
||||
if (yystate >= 0 && yystate <= YYLAST && yycheck[yystate] == *yyssp)
|
||||
yystate = yytable[yystate];
|
||||
else
|
||||
yystate = yydefgoto[yyn - YYNTBASE];
|
||||
|
||||
goto yynewstate;
|
||||
|
||||
yyerrlab: /* here on detecting error */
|
||||
|
||||
if (! yyerrstatus)
|
||||
/* If not already recovering from an error, report this error. */
|
||||
{
|
||||
++yynerrs;
|
||||
|
||||
#ifdef YYERROR_VERBOSE
|
||||
yyn = yypact[yystate];
|
||||
|
||||
if (yyn > YYFLAG && yyn < YYLAST)
|
||||
{
|
||||
int size = 0;
|
||||
char *msg;
|
||||
int x, count;
|
||||
|
||||
count = 0;
|
||||
/* Start X at -yyn if nec to avoid negative indexes in yycheck. */
|
||||
for (x = (yyn < 0 ? -yyn : 0);
|
||||
x < (sizeof(yytname) / sizeof(char *)); x++)
|
||||
if (yycheck[x + yyn] == x)
|
||||
size += strlen(yytname[x]) + 15, count++;
|
||||
msg = (char *) malloc(size + 15);
|
||||
if (msg != 0)
|
||||
{
|
||||
strcpy(msg, "parse error");
|
||||
|
||||
if (count < 5)
|
||||
{
|
||||
count = 0;
|
||||
for (x = (yyn < 0 ? -yyn : 0);
|
||||
x < (sizeof(yytname) / sizeof(char *)); x++)
|
||||
if (yycheck[x + yyn] == x)
|
||||
{
|
||||
strcat(msg, count == 0 ? ", expecting `" : " or `");
|
||||
strcat(msg, yytname[x]);
|
||||
strcat(msg, "'");
|
||||
count++;
|
||||
}
|
||||
}
|
||||
yyerror(msg);
|
||||
free(msg);
|
||||
}
|
||||
else
|
||||
yyerror ("parse error; also virtual memory exceeded");
|
||||
}
|
||||
else
|
||||
#endif /* YYERROR_VERBOSE */
|
||||
yyerror("parse error");
|
||||
}
|
||||
|
||||
goto yyerrlab1;
|
||||
yyerrlab1: /* here on error raised explicitly by an action */
|
||||
|
||||
if (yyerrstatus == 3)
|
||||
{
|
||||
/* if just tried and failed to reuse lookahead token after an error, discard it. */
|
||||
|
||||
/* return failure if at end of input */
|
||||
if (yychar == YYEOF)
|
||||
YYABORT;
|
||||
|
||||
#if YYDEBUG != 0
|
||||
if (yydebug)
|
||||
fprintf(stderr, "Discarding token %d (%s).\n", yychar, yytname[yychar1]);
|
||||
#endif
|
||||
|
||||
yychar = YYEMPTY;
|
||||
}
|
||||
|
||||
/* Else will try to reuse lookahead token
|
||||
after shifting the error token. */
|
||||
|
||||
yyerrstatus = 3; /* Each real token shifted decrements this */
|
||||
|
||||
goto yyerrhandle;
|
||||
|
||||
yyerrdefault: /* current state does not do anything special for the error token. */
|
||||
|
||||
#if 0
|
||||
/* This is wrong; only states that explicitly want error tokens
|
||||
should shift them. */
|
||||
yyn = yydefact[yystate]; /* If its default is to accept any token, ok. Otherwise pop it.*/
|
||||
if (yyn) goto yydefault;
|
||||
#endif
|
||||
|
||||
yyerrpop: /* pop the current state because it cannot handle the error token */
|
||||
|
||||
if (yyssp == yyss) YYABORT;
|
||||
yyvsp--;
|
||||
yystate = *--yyssp;
|
||||
#ifdef YYLSP_NEEDED
|
||||
yylsp--;
|
||||
#endif
|
||||
|
||||
#if YYDEBUG != 0
|
||||
if (yydebug)
|
||||
{
|
||||
short *ssp1 = yyss - 1;
|
||||
fprintf (stderr, "Error: state stack now");
|
||||
while (ssp1 != yyssp)
|
||||
fprintf (stderr, " %d", *++ssp1);
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
yyerrhandle:
|
||||
|
||||
yyn = yypact[yystate];
|
||||
if (yyn == YYFLAG)
|
||||
goto yyerrdefault;
|
||||
|
||||
yyn += YYTERROR;
|
||||
if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != YYTERROR)
|
||||
goto yyerrdefault;
|
||||
|
||||
yyn = yytable[yyn];
|
||||
if (yyn < 0)
|
||||
{
|
||||
if (yyn == YYFLAG)
|
||||
goto yyerrpop;
|
||||
yyn = -yyn;
|
||||
goto yyreduce;
|
||||
}
|
||||
else if (yyn == 0)
|
||||
goto yyerrpop;
|
||||
|
||||
if (yyn == YYFINAL)
|
||||
YYACCEPT;
|
||||
|
||||
#if YYDEBUG != 0
|
||||
if (yydebug)
|
||||
fprintf(stderr, "Shifting error token, ");
|
||||
#endif
|
||||
|
||||
*++yyvsp = yylval;
|
||||
#ifdef YYLSP_NEEDED
|
||||
*++yylsp = yylloc;
|
||||
#endif
|
||||
|
||||
yystate = yyn;
|
||||
goto yynewstate;
|
||||
}
|
BIN
bin/flex/flex.exe
Executable file
BIN
bin/flex/flex.exe
Executable file
Binary file not shown.
4079
bin/flex/flex_1.html
Executable file
4079
bin/flex/flex_1.html
Executable file
File diff suppressed because it is too large
Load Diff
357
bin/nasm/Licence
Executable file
357
bin/nasm/Licence
Executable file
@ -0,0 +1,357 @@
|
||||
|
||||
|
||||
|
||||
Terms and Conditions for the use of the Netwide Assembler
|
||||
|
||||
|
||||
=========================================================
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Can I have the gist without reading the legalese?
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Basically, NASM is free. You can't charge for it. You can copy it as
|
||||
|
||||
|
||||
much as you like. You can incorporate it, or bits of it, into other
|
||||
|
||||
|
||||
free programs if you want. (But we want to know about it if you do,
|
||||
|
||||
|
||||
and we want to be mentioned in the credits.) We may well allow you
|
||||
|
||||
|
||||
to incorporate it into commercial software too, but we'll probably
|
||||
|
||||
|
||||
demand some money for it, and we'll certainly demand to be given
|
||||
|
||||
|
||||
credit. And in extreme cases (although I can't immediately think of
|
||||
|
||||
|
||||
a reason we might actually want to do this) we may refuse to let you
|
||||
|
||||
|
||||
do it at all.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
NASM LICENCE AGREEMENT
|
||||
|
||||
|
||||
======================
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
By "the Software" this licence refers to the complete contents of
|
||||
|
||||
|
||||
the NASM archive, excluding this licence document itself, and
|
||||
|
||||
|
||||
excluding the contents of the `test' directory. The Netwide
|
||||
|
||||
|
||||
Disassembler, NDISASM, is specifically included under this licence.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
I. The Software is freely redistributable; anyone may copy the
|
||||
|
||||
|
||||
Software, or parts of the Software, and give away as many copies as
|
||||
|
||||
|
||||
they like to anyone, as long as this licence document is kept with
|
||||
|
||||
|
||||
the Software. Charging a fee for the Software is prohibited,
|
||||
|
||||
|
||||
although a fee may be charged for the act of transferring a copy,
|
||||
|
||||
|
||||
and you can offer warranty protection and charge a fee for that.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
II. The Software, or parts thereof, may be incorporated into other
|
||||
|
||||
|
||||
freely redistributable software (by which we mean software that may
|
||||
|
||||
|
||||
be obtained free of charge) without requiring permission from the
|
||||
|
||||
|
||||
authors, as long as due credit is given to the authors of the
|
||||
|
||||
|
||||
Software in the resulting work, as long as the authors are informed
|
||||
|
||||
|
||||
of this action if possible, and as long as those parts of the
|
||||
|
||||
|
||||
Software that are used remain under this licence.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
III. Modified forms of the Software may be created and distributed
|
||||
|
||||
|
||||
as long as the authors are informed of this action if possible, as
|
||||
|
||||
|
||||
long as the resulting work remains under this licence, as long as
|
||||
|
||||
|
||||
the modified form of the Software is distributed with documentation
|
||||
|
||||
|
||||
which still gives credit to the original authors of the Software,
|
||||
|
||||
|
||||
and as long as the modified form of the Software is distributed with
|
||||
|
||||
|
||||
a clear statement that it is not the original form of the Software
|
||||
|
||||
|
||||
in the form that it was distributed by the authors.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
IV. The Software, or parts thereof, may be incorporated into other
|
||||
|
||||
|
||||
software which is not freely redistributable (i.e. software for
|
||||
|
||||
|
||||
which a fee is charged), as long as permission is granted from the
|
||||
|
||||
|
||||
authors of the Software. The authors reserve the right to grant this
|
||||
|
||||
|
||||
permission only for a fee, which may at our option take the form of
|
||||
|
||||
|
||||
royalty payments. The authors also reserve the right to refuse to
|
||||
|
||||
|
||||
grant permission if they deem it necessary. For further information
|
||||
|
||||
|
||||
about who exactly the authors are, see clause XI below.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
V. The Software may be incorporated, in its original archive form,
|
||||
|
||||
|
||||
into software collections or archives which are not freely
|
||||
|
||||
|
||||
redistributable, as long as it is clearly stated that the Software
|
||||
|
||||
|
||||
itself remains freely redistributable and remains under this licence
|
||||
|
||||
|
||||
and no other. Such collections are deemed not to fall under article
|
||||
|
||||
|
||||
IV of this licence.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
VI. Object files or programs generated by the Software as output do
|
||||
|
||||
|
||||
not fall under this licence at all, and may be placed under any
|
||||
|
||||
|
||||
licence the author wishes. The authors explicitly lay no claim to,
|
||||
|
||||
|
||||
and assert no rights over, any programs written by other people and
|
||||
|
||||
|
||||
assembled into object form by the Software.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
VII. You may not copy, modify or distribute the Software except
|
||||
|
||||
|
||||
under the terms given in this licence document. You may not
|
||||
|
||||
|
||||
sublicense the Software or in any way place it under any other
|
||||
|
||||
|
||||
licence than this one. Since you have not signed this licence, you
|
||||
|
||||
|
||||
are not of course required to accept it; however, no other licence
|
||||
|
||||
|
||||
applies to the Software, and nothing else grants you any permission
|
||||
|
||||
|
||||
to copy, modify, sublicense or distribute the Software in any way.
|
||||
|
||||
|
||||
These actions are therefore prohibited if you do not accept this
|
||||
|
||||
|
||||
licence.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
VIII. There is no warranty for the Software, to the extent permitted
|
||||
|
||||
|
||||
by applicable law. The authors provide the Software "as is" without
|
||||
|
||||
|
||||
warranty of any kind, either expressed or implied, including but not
|
||||
|
||||
|
||||
limited to the implied warranties of merchantability and fitness for
|
||||
|
||||
|
||||
a particular purpose. The entire risk as to the quality and
|
||||
|
||||
|
||||
performance of the Software is with you. Should the Software prove
|
||||
|
||||
|
||||
defective, you assume the cost of all necessary servicing, repair or
|
||||
|
||||
|
||||
correction.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
IX. In no event, unless required by applicable law or agreed to in
|
||||
|
||||
|
||||
writing, will any of the authors be liable to you for damages,
|
||||
|
||||
|
||||
including any general, special, incidental or consequential damages,
|
||||
|
||||
|
||||
arising out of the use or the inability to use the Software,
|
||||
|
||||
|
||||
including but not limited to loss of data or data being rendered
|
||||
|
||||
|
||||
inaccurate or a failure of the Software to operate with any other
|
||||
|
||||
|
||||
programs, even if you have been advised of the possibility of such
|
||||
|
||||
|
||||
damages.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
X. In addition to what this Licence otherwise provides, the Software
|
||||
|
||||
|
||||
may be distributed in such a way as to be compliant with the GNU
|
||||
|
||||
|
||||
General Public Licence, as published by the Free Software Foundation,
|
||||
|
||||
|
||||
Cambridge, MA, USA; version 2, or, at your option, any later version;
|
||||
|
||||
|
||||
incorporated herein by reference. You must include a copy of this
|
||||
|
||||
|
||||
Licence with such distribution. Furthermore, patches sent to the
|
||||
|
||||
|
||||
authors for the purpose of inclusion in the official release version
|
||||
|
||||
|
||||
are considered cleared for release under the full terms of this
|
||||
|
||||
|
||||
Licence.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
XI. The authors of NASM are the original authors (Simon Tatham and
|
||||
|
||||
|
||||
Julian Hall) and all those who the original authors feel have
|
||||
|
||||
|
||||
contributed significantly to the overall project. If you wish to
|
||||
|
||||
|
||||
contact the authors, Julian Hall (jules@earthcorp.com) should be your
|
||||
|
||||
|
||||
first port of call.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
XII. Should any part of this agreement be deemed unenforcable, it is
|
||||
|
||||
|
||||
intended that the remainder of the agreement be held in force.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
END OF LICENCE AGREEMENT
|
||||
|
||||
|
179
bin/nasm/Readme
Executable file
179
bin/nasm/Readme
Executable file
@ -0,0 +1,179 @@
|
||||
This is a distribution of NASM, the Netwide Assembler. NASM is a
|
||||
prototype general-purpose x86 assembler. It will currently output
|
||||
flat-form binary files, a.out, COFF and ELF Unix object files,
|
||||
Microsoft Win32 and 16-bit DOS object files, OS/2 object files, the
|
||||
as86 object format, and a home-grown format called RDOFF.
|
||||
|
||||
Also included is NDISASM, a prototype x86 binary-file disassembler
|
||||
which uses the same instruction table as NASM.
|
||||
|
||||
To install NASM on UNIX or Linux, type `./configure', then `make', and
|
||||
then either `make install', or copy the file `nasm' (and maybe
|
||||
`ndisasm') to a directory on your search path (maybe /usr/local/bin,
|
||||
or ~/bin if you don't have root access). You may also want to copy the
|
||||
man page `nasm.1' (and maybe `ndisasm.1') to somewhere sensible. Note
|
||||
that source and binaries are also available in RPM format; to install
|
||||
an RPM on a system which uses this packaging format (mostly Linux
|
||||
distributions) simply do "rpm -Uivh filename.rpm".
|
||||
|
||||
To install under DOS, if you don't need to rebuild from the sources,
|
||||
you can just copy either nasm.exe and ndisasm.exe (32-bit DOS-extended
|
||||
versions), nasmr.exe and ndisasmr.exe (16-bit classical DOS
|
||||
executables), or nasmw.exe and ndisasmw.exe (Win32 console
|
||||
applications - less likely to run out of memory), to somewhere on your
|
||||
PATH.
|
||||
|
||||
The 32-bit applications require a DPMI server. If you're running
|
||||
under plain DOS and don't have a DPMI server already, you can get
|
||||
CSDPMI from ftp://ftp.simtel.net/pub/simtelnet/gnu/djgpp/v2misc/.
|
||||
|
||||
To rebuild the DOS sources, various makefiles are provided:
|
||||
|
||||
- Makefile.dos, the one I build the standard 16-bit releases from,
|
||||
designed for a hybrid system using Microsoft C and Borland Make
|
||||
(don't ask why :-)
|
||||
- Makefile.vc, for Microsoft Visual C++ compiling to a Win32
|
||||
command-line application. This is the one I build the standard
|
||||
Win32 release binaries from.
|
||||
|
||||
- Makefile.bor, for Borland C.
|
||||
- Makefile.bc2, also for Borland C, contributed by Fox Cutter.
|
||||
Reported to work better than Makefile.bor on some systems.
|
||||
|
||||
- Makefile.sc, for Symantec C++, compiling to a 32-bit extended DOS
|
||||
executable.. Contributed by Mark Junker.
|
||||
- Makefile.scw, also for Symantec C++, compiling to a Win32 command-
|
||||
line application. Also contributed by Mark Junker.
|
||||
|
||||
- Makefile.wc, for Watcom C, compiling to a 32-bit extended DOS
|
||||
executable. Contributed by Dominik Behr.
|
||||
- Makefile.wcw, also for Watcom C, compiling to a Win32 command-
|
||||
line application. Also contributed by Dominik Behr.
|
||||
|
||||
- Makefile.dj, for DJGPP, compiling to a 32-bit extended DOS
|
||||
executable. Contributed by Dominik Behr.
|
||||
|
||||
- Makefile.lcc, for lcc-win32, compiling to a Win32 command line
|
||||
application. (The lcc-win32 compiler and tools are available from
|
||||
http://www.remcomp.com/lcc-win32/)
|
||||
|
||||
I can't guarantee that all of those makefiles work, because I don't
|
||||
have all of those compilers. However, Makefile.dos and Makefile.vc
|
||||
work on my system, and so do Makefile.bor and Makefile.bc2.
|
||||
|
||||
Be careful with Borland C: there have been various conflicting
|
||||
reports about how reliable the Huge memory model is. If you try to
|
||||
compile NASM in Large model, you may get DGROUP overflows due to the
|
||||
vast quantity of data in the instruction tables. I've had reports
|
||||
from some people that Huge model doesn't work at all (and also
|
||||
reports from others that it works fine), so if you don't want to try
|
||||
moving to Huge, you could try adding the option `-dc' to the
|
||||
compiler command line instead, which causes string literals to be
|
||||
moved from DGROUP to the code segments and might make Large model
|
||||
start working. (Either solution works for me.)
|
||||
|
||||
To rebuild truly from scratch, or to make any changes to insns.dat or
|
||||
standard.mac, you need a Perl interpreter installed. Perl
|
||||
interpreters are available for a number of platforms, from:
|
||||
|
||||
http://www.cpan.org/ports/
|
||||
|
||||
For DOS you can also get one from:
|
||||
|
||||
ftp://ftp.simtel.net/pub/simtelnet/gnu/djgpp/v2gnu/
|
||||
|
||||
Dominik Behr has also contributed the file misc/pmw.bat, which is a
|
||||
batch file to turn the output from Makefile.wc (NASM.EXE and
|
||||
NDISASM.EXE) into standalone executables incorporating Tran's
|
||||
PMODE/W DOS extender, rather than depending on an external extender
|
||||
program.
|
||||
|
||||
Some of the Windows makefiles produce executables called nasmw.exe
|
||||
and ndisasmw.exe, and some don't. Be prepared for either...
|
||||
|
||||
If you want to build a restricted version of NASM containing only
|
||||
some of the object file formats, you can achieve this by adding
|
||||
#defines to `outform.h' (see the file itself for documentation), or
|
||||
equivalently by adding compiler command line options in the
|
||||
Makefile.
|
||||
|
||||
There is a machine description file for the `LCC' retargetable C
|
||||
compiler (version 4.0), in the directory `lcc', along with
|
||||
instructions for its use. This means that NASM can now be used as
|
||||
the code-generator back end for a useful C compiler.
|
||||
|
||||
Michael `Wuschel' Tippach has ported his DOS extender `WDOSX' to
|
||||
enable it to work with the 32-bit binary files NASM can output: the
|
||||
original extender and his port `WDOSX/N' are available from his web
|
||||
page, http://www.geocities.com/SiliconValley/Park/4493.
|
||||
|
||||
Matt Mastracci has written a document explaining how to write
|
||||
assembly language modules in DJGPP programs using NASM: it's on his
|
||||
web site at http://www.ucalgary.ca/~mmastrac/djgppasm.doc.
|
||||
|
||||
The `misc' directory contains `nasm.sl', a NASM editing mode for the
|
||||
JED programmers' editor (see http://space.mit.edu/~davis/jed.html
|
||||
for details about JED). The comment at the start of the file gives
|
||||
instructions on how to install the mode. This directory also
|
||||
contains a file (`magic') containing lines to add to /etc/magic on
|
||||
Unix systems to allow the `file' command to recognise RDF files, and
|
||||
a zip file (`exasm.zip') containing the necessary files for syntax
|
||||
highlighting in the Aurora DOS editor. (The Aurora files were
|
||||
contributed by <U993847220@aol.com>; I haven't tested them as I
|
||||
don't have Aurora.)
|
||||
|
||||
The `rdoff' directory contains sources for a linker and loader for
|
||||
the RDF object file format, to run under Linux, and also
|
||||
documentation on the internal structure of RDF files.
|
||||
|
||||
For information about how you can distribute and use NASM, see the
|
||||
file Licence. We were tempted to put NASM under the GPL, but decided
|
||||
that in many ways it was too restrictive for developers.
|
||||
|
||||
For release 0.98 and later, the NASM source distribution contains the
|
||||
following components:
|
||||
|
||||
Unix Windows, OS/2 DOS
|
||||
|
||||
nasm-X.XX.tar.gz nasm-X.XX.zip nasmXXXs.zip
|
||||
|
||||
Main archive: Contain all sources you need to build NASM,
|
||||
plus the documentation source code. If you have a Perl
|
||||
interpreter and (possibly) Winhelp compiler installed, you
|
||||
don't need any additional files.
|
||||
|
||||
nasm-X.XX-xdoc.tar.gz nasm-X.XX-xdoc.zip nasmXXXd.zip
|
||||
|
||||
Documentation in HTML, INFO, text, and PostScript format.
|
||||
You can compile these files yourself from the main archive
|
||||
if you have a Perl interpreter installed.
|
||||
|
||||
N/A nasm-X.XX-whlp.zip N/A
|
||||
|
||||
Documentation in Windows help format. You can compile this
|
||||
file yourself from the main archive if you have a Perl
|
||||
interpreter and a Winhelp compiler installed.
|
||||
|
||||
The Unix, Windows-OS/2, and DOS versions differ in the following ways:
|
||||
|
||||
The Unix version uses the Unix line ending convention (LF), and long file
|
||||
names with upper and lower case.
|
||||
|
||||
The Windows-OS/2 version uses the Microsoft line ending convention
|
||||
(CR+LF), and long file names with upper and lower case.
|
||||
|
||||
The DOS version uses the Microsoft line ending convention (CR+LF),
|
||||
with all filenames in 8.3 monocase.
|
||||
|
||||
For information about how to use NASM, see the various forms of
|
||||
documentation in the `doc' directory: documentation is provided in
|
||||
HTML, PostScript, plain text, Texinfo, and Windows Help formats. For
|
||||
information about how to use NDISASM, see `ndisasm.doc'. For
|
||||
information about the internal structure of NASM, see
|
||||
`internal.doc'. (In particular, _please_ read `internal.doc' before
|
||||
writing any code for us...)
|
||||
|
||||
The NASM web page is at http://www.cryogen.com/Nasm/
|
||||
|
||||
Bug reports (and patches if you can) should be sent to
|
||||
<hpa@zytor.com>.
|
BIN
bin/nasm/nasmw.exe
Executable file
BIN
bin/nasm/nasmw.exe
Executable file
Binary file not shown.
BIN
bin/nasm/ndisasmw.exe
Executable file
BIN
bin/nasm/ndisasmw.exe
Executable file
Binary file not shown.
33
engine/Makefile
Executable file
33
engine/Makefile
Executable file
@ -0,0 +1,33 @@
|
||||
|
||||
#--------------------------------------
|
||||
# include and verify the users mk/conf.mk
|
||||
|
||||
-include ../mk/conf.mk
|
||||
|
||||
ifndef CONFIG_STATUS
|
||||
doConfigure:
|
||||
$(error Configuration file not defined. Please run $(MAKE) -f mk/configure.mk)
|
||||
#@$(MAKE) --no-print-directory -f ../mk/configure.mk
|
||||
else
|
||||
ifeq ($(CONFIG_STATUS),INVALID)
|
||||
doConfigure:
|
||||
$(error Invalid Configuration file. Please run $(MAKE) -f mk/configure.mk)
|
||||
#@$(MAKE) --no-print-directory -f mk/configure.mk
|
||||
else
|
||||
include ../mk/conf.$(COMPILER).$(OS).mk
|
||||
include ../mk/conf.$(COMPILER).mk
|
||||
endif
|
||||
endif
|
||||
|
||||
include targets.torque.mk
|
||||
|
||||
include ../mk/conf.common.mk
|
||||
|
||||
|
||||
#default:
|
||||
# echo default.
|
||||
|
||||
ifneq ($(MAKECMDGOALS),clean)
|
||||
-include $(addprefix $(DIR.OBJ)/, $(addsuffix $(EXT.DEP), $(basename $(filter %.cc %.c,$(SOURCE.ALL)))))
|
||||
endif
|
||||
|
2487
engine/audio/audio.cc
Executable file
2487
engine/audio/audio.cc
Executable file
File diff suppressed because it is too large
Load Diff
16
engine/audio/audio.h
Executable file
16
engine/audio/audio.h
Executable file
@ -0,0 +1,16 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _AUDIO_H_
|
||||
#define _AUDIO_H_
|
||||
|
||||
#ifndef _PLATFORMAUDIO_H_
|
||||
#include "platform/platformAudio.h"
|
||||
#endif
|
||||
#ifndef _AUDIODATABLOCK_H_
|
||||
#include "audio/audioDataBlock.h"
|
||||
#endif
|
||||
|
||||
#endif // _H_AUDIO_
|
428
engine/audio/audioBuffer.cc
Executable file
428
engine/audio/audioBuffer.cc
Executable file
@ -0,0 +1,428 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platformAL.h"
|
||||
#include "audio/audioBuffer.h"
|
||||
#include "core/stream.h"
|
||||
#include "console/console.h"
|
||||
#include "core/frameAllocator.h"
|
||||
|
||||
#ifndef TORQUE_NO_OGGVORBIS
|
||||
#include "vorbis/codec.h"
|
||||
#endif
|
||||
|
||||
#if defined(TORQUE_BIG_ENDIAN)
|
||||
#define ENDIAN 1
|
||||
#else
|
||||
#define ENDIAN 0
|
||||
#endif
|
||||
|
||||
//#define LOG_SOUND_LOADS
|
||||
|
||||
/// WAV File-header
|
||||
struct WAVFileHdr
|
||||
{
|
||||
ALubyte id[4];
|
||||
ALsizei size;
|
||||
ALubyte type[4];
|
||||
};
|
||||
|
||||
//// WAV Fmt-header
|
||||
struct WAVFmtHdr
|
||||
{
|
||||
ALushort format;
|
||||
ALushort channels;
|
||||
ALuint samplesPerSec;
|
||||
ALuint bytesPerSec;
|
||||
ALushort blockAlign;
|
||||
ALushort bitsPerSample;
|
||||
};
|
||||
|
||||
/// WAV FmtEx-header
|
||||
struct WAVFmtExHdr
|
||||
{
|
||||
ALushort size;
|
||||
ALushort samplesPerBlock;
|
||||
};
|
||||
|
||||
/// WAV Smpl-header
|
||||
struct WAVSmplHdr
|
||||
{
|
||||
ALuint manufacturer;
|
||||
ALuint product;
|
||||
ALuint samplePeriod;
|
||||
ALuint note;
|
||||
ALuint fineTune;
|
||||
ALuint SMPTEFormat;
|
||||
ALuint SMPTEOffest;
|
||||
ALuint loops;
|
||||
ALuint samplerData;
|
||||
struct
|
||||
{
|
||||
ALuint identifier;
|
||||
ALuint type;
|
||||
ALuint start;
|
||||
ALuint end;
|
||||
ALuint fraction;
|
||||
ALuint count;
|
||||
} loop[1];
|
||||
};
|
||||
|
||||
/// WAV Chunk-header
|
||||
struct WAVChunkHdr
|
||||
{
|
||||
ALubyte id[4];
|
||||
ALuint size;
|
||||
};
|
||||
|
||||
#define CHUNKSIZE 4096
|
||||
|
||||
|
||||
|
||||
//--------------------------------------
|
||||
AudioBuffer::AudioBuffer(StringTableEntry filename)
|
||||
{
|
||||
AssertFatal(StringTable->lookup(filename), "AudioBuffer:: filename is not a string table entry");
|
||||
|
||||
mFilename = filename;
|
||||
mLoading = false;
|
||||
malBuffer = 0;
|
||||
}
|
||||
|
||||
AudioBuffer::~AudioBuffer()
|
||||
{
|
||||
if( malBuffer != 0 ) {
|
||||
alDeleteBuffers( 1, &malBuffer );
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
Resource<AudioBuffer> AudioBuffer::find(const char *filename)
|
||||
{
|
||||
U32 mark = FrameAllocator::getWaterMark();
|
||||
char * f2 = NULL;
|
||||
|
||||
Resource<AudioBuffer> buffer = ResourceManager->load(filename);
|
||||
if (bool(buffer) == false)
|
||||
{
|
||||
// wav file doesn't exist, try ogg file instead
|
||||
S32 len = dStrlen(filename);
|
||||
if (len>3 && !dStricmp(filename+len-4,".wav"))
|
||||
{
|
||||
f2 = (char*)FrameAllocator::alloc(len+1);
|
||||
dStrcpy(f2,filename);
|
||||
f2[len-3] = 'o';
|
||||
f2[len-2] = 'g';
|
||||
f2[len-1] = 'g';
|
||||
buffer = ResourceManager->load(filename);
|
||||
}
|
||||
}
|
||||
|
||||
// if resource still not there, try to create it if file exists
|
||||
if (bool(buffer) == false)
|
||||
{
|
||||
// see if the file exists -- first try default, then try ogg
|
||||
if (ResourceManager->getPathOf(filename))
|
||||
{
|
||||
AudioBuffer *temp = new AudioBuffer(StringTable->insert(filename));
|
||||
ResourceManager->add(filename, temp);
|
||||
buffer = ResourceManager->load(filename);
|
||||
}
|
||||
else if (f2 && ResourceManager->getPathOf(f2))
|
||||
{
|
||||
AudioBuffer *temp = new AudioBuffer(StringTable->insert(f2));
|
||||
ResourceManager->add(f2, temp);
|
||||
buffer = ResourceManager->load(f2);
|
||||
}
|
||||
}
|
||||
|
||||
FrameAllocator::setWaterMark(mark);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
ResourceInstance* AudioBuffer::construct(Stream &)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
ALuint AudioBuffer::getALBuffer()
|
||||
{
|
||||
if (!alcGetCurrentContext())
|
||||
return 0;
|
||||
|
||||
// clear the error state
|
||||
alGetError();
|
||||
|
||||
// Intangir> fix for newest openAL from creative (it returns true, yea right 0 is not a valid buffer)
|
||||
// it MIGHT not work at all for all i know.
|
||||
if (malBuffer && alIsBuffer(malBuffer))
|
||||
return malBuffer;
|
||||
|
||||
alGenBuffers(1, &malBuffer);
|
||||
if(alGetError() != AL_NO_ERROR)
|
||||
return 0;
|
||||
|
||||
ResourceObject * obj = ResourceManager->find(mFilename);
|
||||
if(obj)
|
||||
{
|
||||
bool readSuccess = false;
|
||||
S32 len = dStrlen(mFilename);
|
||||
|
||||
if(len > 3 && !dStricmp(mFilename + len - 4, ".wav"))
|
||||
{
|
||||
#ifdef LOG_SOUND_LOADS
|
||||
Con::printf("Reading WAV: %s\n", mFilename);
|
||||
#endif
|
||||
readSuccess = readWAV(obj);
|
||||
}
|
||||
#ifndef TORQUE_NO_OGGVORBIS
|
||||
else if(len > 3 && !dStricmp(mFilename + len - 4, ".ogg"))
|
||||
{
|
||||
# ifdef LOG_SOUND_LOADS
|
||||
Con::printf("Reading Ogg: %s\n", mFilename);
|
||||
# endif
|
||||
readSuccess = readOgg(obj);
|
||||
}
|
||||
#endif
|
||||
if(readSuccess)
|
||||
return(malBuffer);
|
||||
}
|
||||
|
||||
alDeleteBuffers(1, &malBuffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! The Read a WAV file from the given ResourceObject and initialize
|
||||
an alBuffer with it.
|
||||
*/
|
||||
bool AudioBuffer::readWAV(ResourceObject *obj)
|
||||
{
|
||||
WAVChunkHdr chunkHdr;
|
||||
WAVFmtExHdr fmtExHdr;
|
||||
WAVFileHdr fileHdr;
|
||||
WAVSmplHdr smplHdr;
|
||||
WAVFmtHdr fmtHdr;
|
||||
|
||||
ALenum format = AL_FORMAT_MONO16;
|
||||
char *data = NULL;
|
||||
ALsizei size = 0;
|
||||
ALsizei freq = 22050;
|
||||
ALboolean loop = AL_FALSE;
|
||||
|
||||
Stream *stream = ResourceManager->openStream(obj);
|
||||
if (!stream)
|
||||
return false;
|
||||
|
||||
stream->read(4, &fileHdr.id[0]);
|
||||
stream->read(&fileHdr.size);
|
||||
stream->read(4, &fileHdr.type[0]);
|
||||
|
||||
fileHdr.size=((fileHdr.size+1)&~1)-4;
|
||||
|
||||
stream->read(4, &chunkHdr.id[0]);
|
||||
stream->read(&chunkHdr.size);
|
||||
// unread chunk data rounded up to nearest WORD
|
||||
S32 chunkRemaining = chunkHdr.size + (chunkHdr.size&1);
|
||||
|
||||
while ((fileHdr.size!=0) && (stream->getStatus() != Stream::EOS))
|
||||
{
|
||||
// WAV Format header
|
||||
if (!dStrncmp((const char*)chunkHdr.id,"fmt ",4))
|
||||
{
|
||||
stream->read(&fmtHdr.format);
|
||||
stream->read(&fmtHdr.channels);
|
||||
stream->read(&fmtHdr.samplesPerSec);
|
||||
stream->read(&fmtHdr.bytesPerSec);
|
||||
stream->read(&fmtHdr.blockAlign);
|
||||
stream->read(&fmtHdr.bitsPerSample);
|
||||
|
||||
if (fmtHdr.format==0x0001)
|
||||
{
|
||||
format=(fmtHdr.channels==1?
|
||||
(fmtHdr.bitsPerSample==8?AL_FORMAT_MONO8:AL_FORMAT_MONO16):
|
||||
(fmtHdr.bitsPerSample==8?AL_FORMAT_STEREO8:AL_FORMAT_STEREO16));
|
||||
freq=fmtHdr.samplesPerSec;
|
||||
chunkRemaining -= sizeof(WAVFmtHdr);
|
||||
}
|
||||
else
|
||||
{
|
||||
stream->read(sizeof(WAVFmtExHdr), &fmtExHdr);
|
||||
chunkRemaining -= sizeof(WAVFmtExHdr);
|
||||
}
|
||||
}
|
||||
// WAV Format header
|
||||
else if (!dStrncmp((const char*)chunkHdr.id,"data",4))
|
||||
{
|
||||
if (fmtHdr.format==0x0001)
|
||||
{
|
||||
size=chunkHdr.size;
|
||||
data=new char[chunkHdr.size];
|
||||
if (data)
|
||||
{
|
||||
stream->read(chunkHdr.size, data);
|
||||
#if defined(TORQUE_BIG_ENDIAN)
|
||||
// need to endian-flip the 16-bit data.
|
||||
if (fmtHdr.bitsPerSample==16) // !!!TBD we don't handle stereo, so may be RL flipped.
|
||||
{
|
||||
U16 *ds = (U16*)data;
|
||||
U16 *de = (U16*)(data+size);
|
||||
while (ds<de)
|
||||
{
|
||||
*ds = convertLEndianToHost(*ds);
|
||||
ds++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
chunkRemaining -= chunkHdr.size;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
else if (fmtHdr.format==0x0011)
|
||||
{
|
||||
//IMA ADPCM
|
||||
}
|
||||
else if (fmtHdr.format==0x0055)
|
||||
{
|
||||
//MP3 WAVE
|
||||
}
|
||||
}
|
||||
// WAV Loop header
|
||||
else if (!dStrncmp((const char*)chunkHdr.id,"smpl",4))
|
||||
{
|
||||
// this struct read is NOT endian safe but it is ok because
|
||||
// we are only testing the loops field against ZERO
|
||||
stream->read(sizeof(WAVSmplHdr), &smplHdr);
|
||||
loop = (smplHdr.loops ? AL_TRUE : AL_FALSE);
|
||||
chunkRemaining -= sizeof(WAVSmplHdr);
|
||||
}
|
||||
|
||||
// either we have unread chunk data or we found an unknown chunk type
|
||||
// loop and read up to 1K bytes at a time until we have
|
||||
// read to the end of this chunk
|
||||
char buffer[1024];
|
||||
AssertFatal(chunkRemaining >= 0, "AudioBuffer::readWAV: remaining chunk data should never be less than zero.");
|
||||
while (chunkRemaining > 0)
|
||||
{
|
||||
S32 readSize = getMin(1024, chunkRemaining);
|
||||
stream->read(readSize, buffer);
|
||||
chunkRemaining -= readSize;
|
||||
}
|
||||
|
||||
fileHdr.size-=(((chunkHdr.size+1)&~1)+8);
|
||||
|
||||
// read next chunk header...
|
||||
stream->read(4, &chunkHdr.id[0]);
|
||||
stream->read(&chunkHdr.size);
|
||||
// unread chunk data rounded up to nearest WORD
|
||||
chunkRemaining = chunkHdr.size + (chunkHdr.size&1);
|
||||
}
|
||||
|
||||
ResourceManager->closeStream(stream);
|
||||
if (data)
|
||||
{
|
||||
alBufferData(malBuffer, format, data, size, freq);
|
||||
delete [] data;
|
||||
return (alGetError() == AL_NO_ERROR);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifndef TORQUE_NO_OGGVORBIS
|
||||
/*! The Read an Ogg Vorbis file from the given ResourceObject and initialize
|
||||
an alBuffer with it.
|
||||
*/
|
||||
bool AudioBuffer::readOgg(ResourceObject *obj)
|
||||
{
|
||||
OggVorbisFile vf;
|
||||
vorbis_info *vi;
|
||||
|
||||
ALenum format = AL_FORMAT_MONO16;
|
||||
char *data = NULL;
|
||||
ALsizei size = 0;
|
||||
ALsizei freq = 22050;
|
||||
ALboolean loop = AL_FALSE;
|
||||
int current_section = 0;
|
||||
|
||||
#if defined(TORQUE_BIG_ENDIAN)
|
||||
int endian = 1;
|
||||
#else
|
||||
int endian = 0;
|
||||
#endif
|
||||
|
||||
int eof = 0;
|
||||
|
||||
Stream *stream = ResourceManager->openStream(obj);
|
||||
if (!stream)
|
||||
return false;
|
||||
|
||||
if(vf.ov_open(stream, NULL, 0) < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//Read Vorbis File Info
|
||||
vi = vf.ov_info(-1);
|
||||
freq = vi->rate;
|
||||
|
||||
long samples = (long)vf.ov_pcm_total(-1);
|
||||
|
||||
if(vi->channels == 1) {
|
||||
format = AL_FORMAT_MONO16;
|
||||
size = 2 * samples;
|
||||
} else {
|
||||
format = AL_FORMAT_STEREO16;
|
||||
size = 4 * samples;
|
||||
}
|
||||
|
||||
|
||||
data=new char[size];
|
||||
|
||||
if (data)
|
||||
{
|
||||
long ret = oggRead(&vf, data, size, endian, ¤t_section);
|
||||
}
|
||||
|
||||
/* cleanup */
|
||||
vf.ov_clear();
|
||||
|
||||
ResourceManager->closeStream(stream);
|
||||
if (data)
|
||||
{
|
||||
alBufferData(malBuffer, format, data, size, freq);
|
||||
delete [] data;
|
||||
return (alGetError() == AL_NO_ERROR);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// ov_read() only returns a maximum of one page worth of data
|
||||
// this helper function will repeat the read until buffer is full
|
||||
long AudioBuffer::oggRead(OggVorbisFile* vf, char *buffer,int length,
|
||||
int bigendianp,int *bitstream)
|
||||
{
|
||||
long bytesRead = 0;
|
||||
long totalBytes = 0;
|
||||
long offset = 0;
|
||||
long bytesToRead = 0;
|
||||
//while((offset + CHUNKSIZE) < length) {
|
||||
while((offset) < length)
|
||||
{
|
||||
if((length - offset) < CHUNKSIZE)
|
||||
bytesToRead = length - offset;
|
||||
else
|
||||
bytesToRead = CHUNKSIZE;
|
||||
|
||||
bytesRead = vf->ov_read(buffer, bytesToRead, bigendianp, bitstream);
|
||||
if(bytesRead <= 0)
|
||||
break;
|
||||
offset += bytesRead;
|
||||
buffer += bytesRead;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
#endif
|
56
engine/audio/audioBuffer.h
Executable file
56
engine/audio/audioBuffer.h
Executable file
@ -0,0 +1,56 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _AUDIOBUFFER_H_
|
||||
#define _AUDIOBUFFER_H_
|
||||
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "platform/platform.h"
|
||||
#endif
|
||||
#ifndef _PLATFORMAL_H_
|
||||
#include "platform/platformAL.h"
|
||||
#endif
|
||||
#ifndef _RESMANAGER_H_
|
||||
#include "core/resManager.h"
|
||||
#endif
|
||||
|
||||
// MLH - don't need oggbvorbis in tools
|
||||
#ifndef TORQUE_NO_OGGVORBIS
|
||||
#include "audio/vorbisStream.h"
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
class AudioBuffer: public ResourceInstance
|
||||
{
|
||||
friend class AudioThread;
|
||||
|
||||
private:
|
||||
StringTableEntry mFilename;
|
||||
bool mLoading;
|
||||
ALuint malBuffer;
|
||||
|
||||
bool readRIFFchunk(Stream &s, const char *seekLabel, U32 *size);
|
||||
bool readWAV(ResourceObject *obj);
|
||||
|
||||
#ifndef TORQUE_NO_OGGVORBIS
|
||||
bool readOgg(ResourceObject *obj);
|
||||
long oggRead(OggVorbisFile* vf, char *buffer,int length,
|
||||
int bigendianp,int *bitstream);
|
||||
#endif
|
||||
|
||||
public:
|
||||
AudioBuffer(StringTableEntry filename);
|
||||
~AudioBuffer();
|
||||
ALuint getALBuffer();
|
||||
bool isLoading() {return(mLoading);}
|
||||
|
||||
static Resource<AudioBuffer> find(const char *filename);
|
||||
static ResourceInstance* construct(Stream& stream);
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // _H_AUDIOBUFFER_
|
526
engine/audio/audioDataBlock.cc
Executable file
526
engine/audio/audioDataBlock.cc
Executable file
@ -0,0 +1,526 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "audio/audioDataBlock.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "platform/platformAL.h"
|
||||
#include "sim/netConnection.h"
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
namespace
|
||||
{
|
||||
void writeRangedF32(BitStream * bstream, F32 val, F32 min, F32 max, U32 numBits)
|
||||
{
|
||||
val = (mClampF(val, min, max) - min) / (max - min);
|
||||
bstream->writeInt(val * ((1 << numBits) - 1), numBits);
|
||||
}
|
||||
|
||||
F32 readRangedF32(BitStream * bstream, F32 min, F32 max, U32 numBits)
|
||||
{
|
||||
return(min + (F32(bstream->readInt(numBits)) / F32((1 << numBits) - 1)) * (max - min));
|
||||
}
|
||||
|
||||
void writeRangedS32(BitStream * bstream, S32 val, S32 min, S32 max)
|
||||
{
|
||||
bstream->writeRangedU32((val - min), 0, (max - min));
|
||||
}
|
||||
|
||||
S32 readRangedS32(BitStream * bstream, S32 min, S32 max)
|
||||
{
|
||||
return(bstream->readRangedU32(0, (max - min)) + min);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Class AudioEnvironment:
|
||||
//--------------------------------------------------------------------------
|
||||
IMPLEMENT_CO_DATABLOCK_V1(AudioEnvironment);
|
||||
|
||||
AudioEnvironment::AudioEnvironment()
|
||||
{
|
||||
mUseRoom = true;
|
||||
mRoom = EAX_ENVIRONMENT_GENERIC;
|
||||
mRoomHF = 0;
|
||||
mReflections = 0;
|
||||
mReverb = 0;
|
||||
mRoomRolloffFactor = 0.1f;
|
||||
mDecayTime = 0.1f;
|
||||
mDecayHFRatio = 0.1f;
|
||||
mReflectionsDelay = 0.f;
|
||||
mReverbDelay = 0.f;
|
||||
mRoomVolume = 0;
|
||||
mEffectVolume = 0.f;
|
||||
mDamping = 0.f;
|
||||
mEnvironmentSize = 10.f;
|
||||
mEnvironmentDiffusion = 1.f;
|
||||
mAirAbsorption = 0.f;
|
||||
mFlags = 0;
|
||||
}
|
||||
|
||||
static EnumTable::Enums roomEnums[] =
|
||||
{
|
||||
{ EAX_ENVIRONMENT_GENERIC, "GENERIC" }, // 0
|
||||
{ EAX_ENVIRONMENT_PADDEDCELL, "PADDEDCELL" },
|
||||
{ EAX_ENVIRONMENT_ROOM, "ROOM" },
|
||||
{ EAX_ENVIRONMENT_BATHROOM, "BATHROOM" },
|
||||
{ EAX_ENVIRONMENT_LIVINGROOM, "LIVINGROOM" },
|
||||
{ EAX_ENVIRONMENT_STONEROOM, "STONEROOM" }, // 5
|
||||
{ EAX_ENVIRONMENT_AUDITORIUM, "AUDITORIUM" },
|
||||
{ EAX_ENVIRONMENT_CONCERTHALL, "CONCERTHALL" },
|
||||
{ EAX_ENVIRONMENT_CAVE, "CAVE" },
|
||||
{ EAX_ENVIRONMENT_ARENA, "ARENA" },
|
||||
{ EAX_ENVIRONMENT_HANGAR, "HANGAR" }, // 10
|
||||
{ EAX_ENVIRONMENT_CARPETEDHALLWAY, "CARPETEDHALLWAY" },
|
||||
{ EAX_ENVIRONMENT_HALLWAY, "HALLWAY" },
|
||||
{ EAX_ENVIRONMENT_STONECORRIDOR, "STONECORRIDOR" },
|
||||
{ EAX_ENVIRONMENT_ALLEY, "ALLEY" },
|
||||
{ EAX_ENVIRONMENT_FOREST, "FOREST" }, // 15
|
||||
{ EAX_ENVIRONMENT_CITY, "CITY" },
|
||||
{ EAX_ENVIRONMENT_MOUNTAINS, "MOUNTAINS" },
|
||||
{ EAX_ENVIRONMENT_QUARRY, "QUARRY" },
|
||||
{ EAX_ENVIRONMENT_PLAIN, "PLAIN" },
|
||||
{ EAX_ENVIRONMENT_PARKINGLOT, "PARKINGLOT" }, // 20
|
||||
{ EAX_ENVIRONMENT_SEWERPIPE, "SEWERPIPE" },
|
||||
{ EAX_ENVIRONMENT_UNDERWATER, "UNDERWATER" },
|
||||
{ EAX_ENVIRONMENT_DRUGGED, "DRUGGED" },
|
||||
{ EAX_ENVIRONMENT_DIZZY, "DIZZY" },
|
||||
{ EAX_ENVIRONMENT_PSYCHOTIC, "PSYCHOTIC" } // 25
|
||||
};
|
||||
static EnumTable gAudioEnvironmentRoomTypes(sizeof(roomEnums) / sizeof(roomEnums[0]), &roomEnums[0]);
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
IMPLEMENT_CONSOLETYPE(AudioEnvironment)
|
||||
IMPLEMENT_GETDATATYPE(AudioEnvironment)
|
||||
IMPLEMENT_SETDATATYPE(AudioEnvironment)
|
||||
|
||||
void AudioEnvironment::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
|
||||
addField("useRoom", TypeBool, Offset(mUseRoom, AudioEnvironment));
|
||||
addField("room", TypeEnum, Offset(mRoom, AudioEnvironment), 1, &gAudioEnvironmentRoomTypes);
|
||||
addField("roomHF", TypeS32, Offset(mRoomHF, AudioEnvironment));
|
||||
addField("reflections", TypeS32, Offset(mReflections, AudioEnvironment));
|
||||
addField("reverb", TypeS32, Offset(mReverb, AudioEnvironment));
|
||||
addField("roomRolloffFactor", TypeF32, Offset(mRoomRolloffFactor, AudioEnvironment));
|
||||
addField("decayTime", TypeF32, Offset(mDecayTime, AudioEnvironment));
|
||||
addField("decayHFRatio", TypeF32, Offset(mDecayHFRatio, AudioEnvironment));
|
||||
addField("reflectionsDelay", TypeF32, Offset(mReflectionsDelay, AudioEnvironment));
|
||||
addField("reverbDelay", TypeF32, Offset(mReverbDelay, AudioEnvironment));
|
||||
addField("roomVolume", TypeS32, Offset(mRoomVolume, AudioEnvironment));
|
||||
addField("effectVolume", TypeF32, Offset(mEffectVolume, AudioEnvironment));
|
||||
addField("damping", TypeF32, Offset(mDamping, AudioEnvironment));
|
||||
addField("environmentSize", TypeF32, Offset(mEnvironmentSize, AudioEnvironment));
|
||||
addField("environmentDiffusion", TypeF32, Offset(mEnvironmentDiffusion, AudioEnvironment));
|
||||
addField("airAbsorption", TypeF32, Offset(mAirAbsorption, AudioEnvironment));
|
||||
addField("flags", TypeS32, Offset(mFlags, AudioEnvironment));
|
||||
}
|
||||
|
||||
|
||||
void AudioEnvironment::packData(BitStream* stream)
|
||||
{
|
||||
Parent::packData(stream);
|
||||
if(stream->writeFlag(mUseRoom))
|
||||
stream->writeRangedU32(mRoom, EAX_ENVIRONMENT_GENERIC, EAX_ENVIRONMENT_COUNT);
|
||||
else
|
||||
{
|
||||
writeRangedS32(stream, mRoomHF, -10000, 0);
|
||||
writeRangedS32(stream, mReflections, -10000, 10000);
|
||||
writeRangedS32(stream, mReverb, -10000, 2000);
|
||||
writeRangedF32(stream, mRoomRolloffFactor, 0.1f, 10.f, 8);
|
||||
writeRangedF32(stream, mDecayTime, 0.1f, 20.f, 8);
|
||||
writeRangedF32(stream, mDecayHFRatio, 0.1f, 20.f, 8);
|
||||
writeRangedF32(stream, mReflectionsDelay, 0.f, 0.3f, 9);
|
||||
writeRangedF32(stream, mReverbDelay, 0.f, 0.1f, 7);
|
||||
writeRangedS32(stream, mRoomVolume, -10000, 0);
|
||||
writeRangedF32(stream, mEffectVolume, 0.f, 1.f, 8);
|
||||
writeRangedF32(stream, mDamping, 0.f, 2.f, 9);
|
||||
writeRangedF32(stream, mEnvironmentSize, 1.f, 100.f, 10);
|
||||
writeRangedF32(stream, mEnvironmentDiffusion, 0.f, 1.f, 8);
|
||||
writeRangedF32(stream, mAirAbsorption, -100.f, 0.f, 10);
|
||||
stream->writeInt(mFlags, 6);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioEnvironment::unpackData(BitStream* stream)
|
||||
{
|
||||
Parent::unpackData(stream);
|
||||
mUseRoom = stream->readFlag();
|
||||
if(mUseRoom)
|
||||
mRoom = stream->readRangedU32(EAX_ENVIRONMENT_GENERIC, EAX_ENVIRONMENT_COUNT);
|
||||
else
|
||||
{
|
||||
mRoomHF = readRangedS32(stream, -10000, 0);
|
||||
mReflections = readRangedS32(stream, -10000, 10000);
|
||||
mReverb = readRangedS32(stream, -10000, 2000);
|
||||
mRoomRolloffFactor = readRangedF32(stream, 0.1f, 10.f, 8);
|
||||
mDecayTime = readRangedF32(stream, 0.1f, 20.f, 8);
|
||||
mDecayHFRatio = readRangedF32(stream, 0.1f, 20.f, 8);
|
||||
mReflectionsDelay = readRangedF32(stream, 0.f, 0.3f, 9);
|
||||
mReverbDelay = readRangedF32(stream, 0.f, 0.1f, 7);
|
||||
mRoomVolume = readRangedS32(stream, -10000, 0);
|
||||
mEffectVolume = readRangedF32(stream, 0.f, 1.f, 8);
|
||||
mDamping = readRangedF32(stream, 0.f, 2.f, 9);
|
||||
mEnvironmentSize = readRangedF32(stream, 1.f, 100.f, 10);
|
||||
mEnvironmentDiffusion = readRangedF32(stream, 0.f, 1.f, 8);
|
||||
mAirAbsorption = readRangedF32(stream, -100.f, 0.f, 10);
|
||||
mFlags = stream->readInt(6);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Class AudioEnvironmentProfile:
|
||||
//--------------------------------------------------------------------------
|
||||
IMPLEMENT_CO_DATABLOCK_V1(AudioSampleEnvironment);
|
||||
|
||||
AudioSampleEnvironment::AudioSampleEnvironment()
|
||||
{
|
||||
mDirect = 0;
|
||||
mDirectHF = 0;
|
||||
mRoom = 0;
|
||||
mRoomHF = 0;
|
||||
mObstruction = 0.f;
|
||||
mObstructionLFRatio = 0.f;
|
||||
mOcclusion = 0.f;
|
||||
mOcclusionLFRatio = 0.f;
|
||||
mOcclusionRoomRatio = 0.f;
|
||||
mRoomRolloff = 0.f;
|
||||
mAirAbsorption = 0.f;
|
||||
mOutsideVolumeHF = 0.f;
|
||||
mFlags = 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
IMPLEMENT_CONSOLETYPE(AudioSampleEnvironment)
|
||||
IMPLEMENT_GETDATATYPE(AudioSampleEnvironment)
|
||||
IMPLEMENT_SETDATATYPE(AudioSampleEnvironment)
|
||||
|
||||
void AudioSampleEnvironment::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
|
||||
addField("direct", TypeS32, Offset(mDirect, AudioSampleEnvironment));
|
||||
addField("directHF", TypeS32, Offset(mDirectHF, AudioSampleEnvironment));
|
||||
addField("room", TypeS32, Offset(mRoom, AudioSampleEnvironment));
|
||||
addField("obstruction", TypeF32, Offset(mObstruction, AudioSampleEnvironment));
|
||||
addField("obstructionLFRatio", TypeF32, Offset(mObstructionLFRatio, AudioSampleEnvironment));
|
||||
addField("occlusion", TypeF32, Offset(mOcclusion, AudioSampleEnvironment));
|
||||
addField("occlusionLFRatio", TypeF32, Offset(mOcclusionLFRatio, AudioSampleEnvironment));
|
||||
addField("occlusionRoomRatio", TypeF32, Offset(mOcclusionRoomRatio, AudioSampleEnvironment));
|
||||
addField("roomRolloff", TypeF32, Offset(mRoomRolloff, AudioSampleEnvironment));
|
||||
addField("airAbsorption", TypeF32, Offset(mAirAbsorption, AudioSampleEnvironment));
|
||||
addField("outsideVolumeHF", TypeS32, Offset(mOutsideVolumeHF, AudioSampleEnvironment));
|
||||
addField("flags", TypeS32, Offset(mFlags, AudioSampleEnvironment));
|
||||
}
|
||||
|
||||
void AudioSampleEnvironment::packData(BitStream* stream)
|
||||
{
|
||||
Parent::packData(stream);
|
||||
writeRangedS32(stream, mDirect, -10000, 1000);
|
||||
writeRangedS32(stream, mDirectHF, -10000, 0);
|
||||
writeRangedS32(stream, mRoom, -10000, 1000);
|
||||
writeRangedS32(stream, mRoomHF, -10000, 0);
|
||||
writeRangedF32(stream, mObstruction, 0.f, 1.f, 9);
|
||||
writeRangedF32(stream, mObstructionLFRatio, 0.f, 1.f, 8);
|
||||
writeRangedF32(stream, mOcclusion, 0.f, 1.f, 9);
|
||||
writeRangedF32(stream, mOcclusionLFRatio, 0.f, 1.f, 8);
|
||||
writeRangedF32(stream, mOcclusionRoomRatio, 0.f, 10.f, 9);
|
||||
writeRangedF32(stream, mRoomRolloff, 0.f, 10.f, 9);
|
||||
writeRangedF32(stream, mAirAbsorption, 0.f, 10.f, 9);
|
||||
writeRangedS32(stream, mOutsideVolumeHF, -10000, 0);
|
||||
stream->writeInt(mFlags, 3);
|
||||
}
|
||||
|
||||
void AudioSampleEnvironment::unpackData(BitStream* stream)
|
||||
{
|
||||
Parent::unpackData(stream);
|
||||
mDirect = readRangedS32(stream, -10000, 1000);
|
||||
mDirectHF = readRangedS32(stream, -10000, 0);
|
||||
mRoom = readRangedS32(stream, -10000, 1000);
|
||||
mRoomHF = readRangedS32(stream, -10000, 0);
|
||||
mObstruction = readRangedF32(stream, 0.f, 1.f, 9);
|
||||
mObstructionLFRatio = readRangedF32(stream, 0.f, 1.f, 8);
|
||||
mOcclusion = readRangedF32(stream, 0.f, 1.f, 9);
|
||||
mOcclusionLFRatio = readRangedF32(stream, 0.f, 1.f, 8);
|
||||
mOcclusionRoomRatio = readRangedF32(stream, 0.f, 10.f, 9);
|
||||
mRoomRolloff = readRangedF32(stream, 0.f, 10.f, 9);
|
||||
mAirAbsorption = readRangedF32(stream, 0.f, 10.f, 9);
|
||||
mOutsideVolumeHF = readRangedS32(stream, -10000, 0);
|
||||
mFlags = stream->readInt(3);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Class AudioDescription:
|
||||
//--------------------------------------------------------------------------
|
||||
IMPLEMENT_CO_DATABLOCK_V1(AudioDescription);
|
||||
|
||||
AudioDescription::AudioDescription()
|
||||
{
|
||||
mDescription.mVolume = 1.0f;
|
||||
mDescription.mIsLooping = false;
|
||||
mDescription.mIsStreaming = false;
|
||||
mDescription.mIs3D = false;
|
||||
mDescription.mReferenceDistance = 1.0f;
|
||||
mDescription.mMaxDistance = 100.0f;
|
||||
mDescription.mConeInsideAngle = 360;
|
||||
mDescription.mConeOutsideAngle = 360;
|
||||
mDescription.mConeOutsideVolume = 1.0f;
|
||||
mDescription.mConeVector.set(0, 0, 1);
|
||||
mDescription.mEnvironmentLevel = 0.f;
|
||||
mDescription.mLoopCount = -1;
|
||||
mDescription.mMinLoopGap = 0;
|
||||
mDescription.mMaxLoopGap = 0;
|
||||
mDescription.mType = 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
IMPLEMENT_CONSOLETYPE(AudioDescription)
|
||||
IMPLEMENT_GETDATATYPE(AudioDescription)
|
||||
IMPLEMENT_SETDATATYPE(AudioDescription)
|
||||
|
||||
void AudioDescription::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
|
||||
addField("volume", TypeF32, Offset(mDescription.mVolume, AudioDescription));
|
||||
addField("isLooping", TypeBool, Offset(mDescription.mIsLooping, AudioDescription));
|
||||
addField("isStreaming", TypeBool, Offset(mDescription.mIsStreaming, AudioDescription));
|
||||
addField("is3D", TypeBool, Offset(mDescription.mIs3D, AudioDescription));
|
||||
addField("referenceDistance", TypeF32, Offset(mDescription.mReferenceDistance, AudioDescription));
|
||||
addField("maxDistance", TypeF32, Offset(mDescription.mMaxDistance, AudioDescription));
|
||||
addField("coneInsideAngle", TypeS32, Offset(mDescription.mConeInsideAngle, AudioDescription));
|
||||
addField("coneOutsideAngle", TypeS32, Offset(mDescription.mConeOutsideAngle, AudioDescription));
|
||||
addField("coneOutsideVolume", TypeF32, Offset(mDescription.mConeOutsideVolume, AudioDescription));
|
||||
addField("coneVector", TypePoint3F, Offset(mDescription.mConeVector, AudioDescription));
|
||||
addField("environmentLevel", TypeF32, Offset(mDescription.mEnvironmentLevel, AudioDescription));
|
||||
addField("loopCount", TypeS32, Offset(mDescription.mLoopCount, AudioDescription));
|
||||
addField("minLoopGap", TypeS32, Offset(mDescription.mMinLoopGap, AudioDescription));
|
||||
addField("maxLoopGap", TypeS32, Offset(mDescription.mMaxLoopGap, AudioDescription));
|
||||
addField("type", TypeS32, Offset(mDescription.mType, AudioDescription));
|
||||
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
bool AudioDescription::onAdd()
|
||||
{
|
||||
if (!Parent::onAdd())
|
||||
return false;
|
||||
|
||||
// validate the data
|
||||
mDescription.mVolume = mClampF(mDescription.mVolume, 0.0f, 1.0f);
|
||||
mDescription.mLoopCount = mClamp(mDescription.mLoopCount, -1, mDescription.mLoopCount);
|
||||
mDescription.mMaxLoopGap = mClamp(mDescription.mMaxLoopGap, mDescription.mMinLoopGap, mDescription.mMaxLoopGap);
|
||||
mDescription.mMinLoopGap = mClamp(mDescription.mMinLoopGap, 0, mDescription.mMaxLoopGap);
|
||||
|
||||
if (mDescription.mIs3D)
|
||||
{
|
||||
// validate the data
|
||||
mDescription.mReferenceDistance = mClampF(mDescription.mReferenceDistance, 0.f, mDescription.mReferenceDistance);
|
||||
mDescription.mMaxDistance = (mDescription.mMaxDistance > mDescription.mReferenceDistance) ? mDescription.mMaxDistance : (mDescription.mReferenceDistance+0.01f);
|
||||
mDescription.mConeInsideAngle = mClamp(mDescription.mConeInsideAngle, 0, 360);
|
||||
mDescription.mConeOutsideAngle = mClamp(mDescription.mConeOutsideAngle, mDescription.mConeInsideAngle, 360);
|
||||
mDescription.mConeOutsideVolume = mClampF(mDescription.mConeOutsideVolume, 0.0f, 1.0f);
|
||||
mDescription.mConeVector.normalize();
|
||||
mDescription.mEnvironmentLevel = mClampF(mDescription.mEnvironmentLevel, 0.f, 1.f);
|
||||
}
|
||||
|
||||
if(mDescription.mType >= Audio::NumAudioTypes)
|
||||
mDescription.mType = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void AudioDescription::packData(BitStream* stream)
|
||||
{
|
||||
Parent::packData(stream);
|
||||
stream->writeFloat(mDescription.mVolume, 6);
|
||||
if(stream->writeFlag(mDescription.mIsLooping))
|
||||
{
|
||||
stream->write(mDescription.mLoopCount);
|
||||
stream->write(mDescription.mMinLoopGap);
|
||||
stream->write(mDescription.mMaxLoopGap);
|
||||
}
|
||||
|
||||
stream->writeFlag(mDescription.mIsStreaming);
|
||||
stream->writeFlag(mDescription.mIs3D);
|
||||
if (mDescription.mIs3D)
|
||||
{
|
||||
stream->write(mDescription.mReferenceDistance);
|
||||
stream->write(mDescription.mMaxDistance);
|
||||
stream->writeInt(mDescription.mConeInsideAngle, 9);
|
||||
stream->writeInt(mDescription.mConeOutsideAngle, 9);
|
||||
stream->writeInt(mDescription.mConeOutsideVolume, 6);
|
||||
stream->writeNormalVector(mDescription.mConeVector, 8);
|
||||
stream->write(mDescription.mEnvironmentLevel);
|
||||
}
|
||||
stream->writeInt(mDescription.mType, 3);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void AudioDescription::unpackData(BitStream* stream)
|
||||
{
|
||||
Parent::unpackData(stream);
|
||||
mDescription.mVolume = stream->readFloat(6);
|
||||
mDescription.mIsLooping = stream->readFlag();
|
||||
if(mDescription.mIsLooping)
|
||||
{
|
||||
stream->read(&mDescription.mLoopCount);
|
||||
stream->read(&mDescription.mMinLoopGap);
|
||||
stream->read(&mDescription.mMaxLoopGap);
|
||||
}
|
||||
|
||||
mDescription.mIsStreaming = stream->readFlag();
|
||||
mDescription.mIs3D = stream->readFlag();
|
||||
if ( mDescription.mIs3D )
|
||||
{
|
||||
stream->read(&mDescription.mReferenceDistance);
|
||||
stream->read(&mDescription.mMaxDistance);
|
||||
mDescription.mConeInsideAngle = stream->readInt(9);
|
||||
mDescription.mConeOutsideAngle = stream->readInt(9);
|
||||
mDescription.mConeOutsideVolume = stream->readFloat(6);
|
||||
stream->readNormalVector(&mDescription.mConeVector, 8);
|
||||
stream->read(&mDescription.mEnvironmentLevel);
|
||||
}
|
||||
mDescription.mType = stream->readInt(3);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Class AudioProfile:
|
||||
//--------------------------------------------------------------------------
|
||||
IMPLEMENT_CO_DATABLOCK_V1(AudioProfile);
|
||||
|
||||
AudioProfile::AudioProfile()
|
||||
{
|
||||
mFilename = NULL;
|
||||
mDescriptionObject = NULL;
|
||||
mSampleEnvironment = 0;
|
||||
mPreload = false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
IMPLEMENT_CONSOLETYPE(AudioProfile)
|
||||
IMPLEMENT_GETDATATYPE(AudioProfile)
|
||||
IMPLEMENT_SETDATATYPE(AudioProfile)
|
||||
|
||||
void AudioProfile::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
|
||||
addField("filename", TypeFilename, Offset(mFilename, AudioProfile));
|
||||
addField("description", TypeAudioDescriptionPtr, Offset(mDescriptionObject, AudioProfile));
|
||||
addField("environment", TypeAudioSampleEnvironmentPtr, Offset(mSampleEnvironment, AudioProfile));
|
||||
addField("preload", TypeBool, Offset(mPreload, AudioProfile));
|
||||
}
|
||||
|
||||
bool AudioProfile::preload(bool server, char errorBuffer[256])
|
||||
{
|
||||
if(!Parent::preload(server, errorBuffer))
|
||||
return false;
|
||||
|
||||
if(!server && NetConnection::filesWereDownloaded() && !bool(AudioBuffer::find(mFilename)))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
bool AudioProfile::onAdd()
|
||||
{
|
||||
if (!Parent::onAdd())
|
||||
return false;
|
||||
|
||||
// if this is client side, make sure that description is as well
|
||||
if(mDescriptionObject)
|
||||
{ // client side dataBlock id's are not in the dataBlock id range
|
||||
if (getId() >= DataBlockObjectIdFirst && getId() <= DataBlockObjectIdLast)
|
||||
{
|
||||
SimObjectId pid = mDescriptionObject->getId();
|
||||
if (pid < DataBlockObjectIdFirst || pid > DataBlockObjectIdLast)
|
||||
{
|
||||
Con::errorf(ConsoleLogEntry::General,"AudioProfile: data dataBlock not networkable (use datablock to create).");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(mPreload && mFilename != NULL && alcGetCurrentContext())
|
||||
{
|
||||
mBuffer = AudioBuffer::find(mFilename);
|
||||
if(bool(mBuffer))
|
||||
{
|
||||
ALuint bufferId = mBuffer->getALBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void AudioProfile::packData(BitStream* stream)
|
||||
{
|
||||
Parent::packData(stream);
|
||||
|
||||
// audio description:
|
||||
if (stream->writeFlag(mDescriptionObject))
|
||||
stream->writeRangedU32(mDescriptionObject->getId(), DataBlockObjectIdFirst,
|
||||
DataBlockObjectIdLast);
|
||||
|
||||
// environmental info:
|
||||
if (stream->writeFlag(mSampleEnvironment))
|
||||
stream->writeRangedU32(mSampleEnvironment->getId(), DataBlockObjectIdFirst,
|
||||
DataBlockObjectIdLast);
|
||||
|
||||
//
|
||||
char buffer[256];
|
||||
if(!mFilename)
|
||||
buffer[0] = 0;
|
||||
else
|
||||
dStrcpy(buffer, mFilename);
|
||||
// S32 len = dStrlen(buffer);
|
||||
// if(len > 3 && !dStricmp(buffer + len - 4, ".wav"))
|
||||
// buffer[len-4] = 0;
|
||||
stream->writeString(buffer);
|
||||
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void AudioProfile::unpackData(BitStream* stream)
|
||||
{
|
||||
Parent::unpackData(stream);
|
||||
|
||||
// audio datablock:
|
||||
if (stream->readFlag()) {
|
||||
SimObjectId id = stream->readRangedU32(DataBlockObjectIdFirst,
|
||||
DataBlockObjectIdLast);
|
||||
Sim::findObject(id, mDescriptionObject);
|
||||
}
|
||||
|
||||
// sample environment:
|
||||
if (stream->readFlag()) {
|
||||
SimObjectId id = stream->readRangedU32(DataBlockObjectIdFirst,
|
||||
DataBlockObjectIdLast);
|
||||
Sim::findObject(id, mSampleEnvironment);
|
||||
}
|
||||
|
||||
char buffer[256];
|
||||
stream->readString(buffer);
|
||||
// dStrcat(buffer, ".wav");
|
||||
|
||||
mFilename = StringTable->insert(buffer);
|
||||
|
||||
// Doh! Something missing from the unpackData...don't want to break
|
||||
// network protocol, so set it to true always here. This is good for
|
||||
// ThinkTanks only. In the future, simply send the preload bit.
|
||||
mPreload = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
139
engine/audio/audioDataBlock.h
Executable file
139
engine/audio/audioDataBlock.h
Executable file
@ -0,0 +1,139 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _AUDIODATABLOCK_H_
|
||||
#define _AUDIODATABLOCK_H_
|
||||
|
||||
#ifndef _PLATFORMAUDIO_H_
|
||||
#include "platform/platformAudio.h"
|
||||
#endif
|
||||
#ifndef _AUDIOBUFFER_H_
|
||||
#include "audio/audioBuffer.h"
|
||||
#endif
|
||||
#ifndef _BITSTREAM_H_
|
||||
#include "core/bitStream.h"
|
||||
#endif
|
||||
#ifndef _SIMBASE_H_
|
||||
#include "console/simBase.h"
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
class AudioEnvironment : public SimDataBlock
|
||||
{
|
||||
typedef SimDataBlock Parent;
|
||||
|
||||
public:
|
||||
|
||||
bool mUseRoom;
|
||||
S32 mRoom;
|
||||
S32 mRoomHF;
|
||||
S32 mReflections;
|
||||
S32 mReverb;
|
||||
F32 mRoomRolloffFactor;
|
||||
F32 mDecayTime;
|
||||
F32 mDecayHFRatio;
|
||||
F32 mReflectionsDelay;
|
||||
F32 mReverbDelay;
|
||||
S32 mRoomVolume;
|
||||
F32 mEffectVolume;
|
||||
F32 mDamping;
|
||||
F32 mEnvironmentSize;
|
||||
F32 mEnvironmentDiffusion;
|
||||
F32 mAirAbsorption;
|
||||
S32 mFlags;
|
||||
|
||||
AudioEnvironment();
|
||||
|
||||
static void initPersistFields();
|
||||
void packData(BitStream* stream);
|
||||
void unpackData(BitStream* stream);
|
||||
|
||||
DECLARE_CONOBJECT(AudioEnvironment);
|
||||
};
|
||||
DECLARE_CONSOLETYPE(AudioEnvironment)
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
class AudioSampleEnvironment : public SimDataBlock
|
||||
{
|
||||
typedef SimDataBlock Parent;
|
||||
|
||||
public:
|
||||
|
||||
S32 mDirect;
|
||||
S32 mDirectHF;
|
||||
S32 mRoom;
|
||||
S32 mRoomHF;
|
||||
F32 mObstruction;
|
||||
F32 mObstructionLFRatio;
|
||||
F32 mOcclusion;
|
||||
F32 mOcclusionLFRatio;
|
||||
F32 mOcclusionRoomRatio;
|
||||
F32 mRoomRolloff;
|
||||
F32 mAirAbsorption;
|
||||
S32 mOutsideVolumeHF;
|
||||
S32 mFlags;
|
||||
|
||||
AudioSampleEnvironment();
|
||||
static void initPersistFields();
|
||||
|
||||
void packData(BitStream* stream);
|
||||
void unpackData(BitStream* stream);
|
||||
|
||||
DECLARE_CONOBJECT(AudioSampleEnvironment);
|
||||
};
|
||||
DECLARE_CONSOLETYPE(AudioSampleEnvironment)
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
class AudioDescription: public SimDataBlock
|
||||
{
|
||||
private:
|
||||
typedef SimDataBlock Parent;
|
||||
|
||||
public:
|
||||
// field info
|
||||
Audio::Description mDescription;
|
||||
|
||||
AudioDescription();
|
||||
DECLARE_CONOBJECT(AudioDescription);
|
||||
static void initPersistFields();
|
||||
virtual bool onAdd();
|
||||
virtual void packData(BitStream* stream);
|
||||
virtual void unpackData(BitStream* stream);
|
||||
|
||||
const Audio::Description* getDescription() const { return &mDescription; }
|
||||
};
|
||||
DECLARE_CONSOLETYPE(AudioDescription)
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
class AudioProfile: public SimDataBlock
|
||||
{
|
||||
private:
|
||||
typedef SimDataBlock Parent;
|
||||
|
||||
Resource<AudioBuffer> mBuffer;
|
||||
|
||||
public:
|
||||
// field info
|
||||
AudioDescription *mDescriptionObject;
|
||||
AudioSampleEnvironment *mSampleEnvironment;
|
||||
|
||||
StringTableEntry mFilename;
|
||||
bool mPreload;
|
||||
|
||||
AudioProfile();
|
||||
DECLARE_CONOBJECT(AudioProfile);
|
||||
static void initPersistFields();
|
||||
|
||||
virtual bool onAdd();
|
||||
virtual void packData(BitStream* stream);
|
||||
virtual void unpackData(BitStream* stream);
|
||||
virtual bool preload(bool server, char errorBuffer[256]);
|
||||
|
||||
const Audio::Description* getDescription() const { return mDescriptionObject ? mDescriptionObject->getDescription() : NULL; }
|
||||
bool isPreload() { return mPreload; }
|
||||
};
|
||||
DECLARE_CONSOLETYPE(AudioProfile)
|
||||
|
||||
#endif // _H_AUDIODATABLOCK_
|
554
engine/audio/audioFunctions.cc
Executable file
554
engine/audio/audioFunctions.cc
Executable file
@ -0,0 +1,554 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "platform/platformAudio.h"
|
||||
#include "console/simBase.h"
|
||||
#include "audio/audioDataBlock.h"
|
||||
|
||||
|
||||
extern ALuint alxGetWaveLen(ALuint buffer);
|
||||
|
||||
extern F32 mAudioTypeVolume[Audio::NumAudioTypes];
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Expose all al get/set methods...
|
||||
//--------------------------------------------------------------------------
|
||||
enum AL_GetSetBits{
|
||||
Source = BIT(0),
|
||||
Listener = BIT(1),
|
||||
Context = BIT(2),
|
||||
Environment = BIT(3),
|
||||
Get = BIT(4),
|
||||
Set = BIT(5),
|
||||
Int = BIT(6),
|
||||
Float = BIT(7),
|
||||
Float3 = BIT(8),
|
||||
Float6 = BIT(9)
|
||||
};
|
||||
|
||||
static ALenum getEnum(const char * name, U32 flags)
|
||||
{
|
||||
AssertFatal(name, "Audio getEnum: bad param");
|
||||
|
||||
static struct {
|
||||
char * mName;
|
||||
ALenum mAlenum;
|
||||
U32 mFlags;
|
||||
} table[] = {
|
||||
//-----------------------------------------------------------------------------------------------------------------
|
||||
// "name" ENUM Flags
|
||||
//-----------------------------------------------------------------------------------------------------------------
|
||||
{ "AL_GAIN", AL_GAIN, (Source|Listener|Get|Set|Float) },
|
||||
{ "AL_GAIN_LINEAR", AL_GAIN_LINEAR, (Source|Listener|Get|Set|Float) },
|
||||
{ "AL_PITCH", AL_PITCH, (Source|Get|Set|Float) },
|
||||
{ "AL_REFERENCE_DISTANCE", AL_REFERENCE_DISTANCE, (Source|Get|Set|Float) },
|
||||
{ "AL_MAX_DISTANCE", AL_MAX_DISTANCE, (Source|Get|Set|Float) },
|
||||
{ "AL_CONE_OUTER_GAIN", AL_CONE_OUTER_GAIN, (Source|Get|Set|Float) },
|
||||
{ "AL_POSITION", AL_POSITION, (Source|Listener|Get|Set|Float3) },
|
||||
{ "AL_DIRECTION", AL_DIRECTION, (Source|Get|Set|Float3) },
|
||||
{ "AL_VELOCITY", AL_VELOCITY, (Source|Listener|Get|Set|Float3) },
|
||||
{ "AL_ORIENTATION", AL_ORIENTATION, (Listener|Set|Float6) },
|
||||
{ "AL_CONE_INNER_ANGLE", AL_CONE_INNER_ANGLE, (Source|Get|Set|Int) },
|
||||
{ "AL_CONE_OUTER_ANGLE", AL_CONE_OUTER_ANGLE, (Source|Get|Set|Int) },
|
||||
{ "AL_LOOPING", AL_LOOPING, (Source|Get|Set|Int) },
|
||||
//{ "AL_STREAMING", AL_STREAMING, (Source|Get|Set|Int) },
|
||||
//{ "AL_BUFFER", AL_BUFFER, (Source|Get|Set|Int) },
|
||||
|
||||
{ "AL_VENDOR", AL_VENDOR, (Context|Get) },
|
||||
{ "AL_VERSION", AL_VERSION, (Context|Get) },
|
||||
{ "AL_RENDERER", AL_RENDERER, (Context|Get) },
|
||||
{ "AL_EXTENSIONS", AL_EXTENSIONS, (Context|Get) },
|
||||
|
||||
/*
|
||||
// environment
|
||||
{ "AL_ENV_ROOM_IASIG", AL_ENV_ROOM_IASIG, (Environment|Get|Set|Int) },
|
||||
{ "AL_ENV_ROOM_HIGH_FREQUENCY_IASIG", AL_ENV_ROOM_HIGH_FREQUENCY_IASIG, (Environment|Get|Set|Int) },
|
||||
{ "AL_ENV_REFLECTIONS_IASIG", AL_ENV_REFLECTIONS_IASIG, (Environment|Get|Set|Int) },
|
||||
{ "AL_ENV_REVERB_IASIG", AL_ENV_REVERB_IASIG, (Environment|Get|Set|Int) },
|
||||
{ "AL_ENV_ROOM_ROLLOFF_FACTOR_IASIG", AL_ENV_ROOM_ROLLOFF_FACTOR_IASIG, (Environment|Get|Set|Float) },
|
||||
{ "AL_ENV_DECAY_TIME_IASIG", AL_ENV_DECAY_TIME_IASIG, (Environment|Get|Set|Float) },
|
||||
{ "AL_ENV_DECAY_HIGH_FREQUENCY_RATIO_IASIG", AL_ENV_DECAY_HIGH_FREQUENCY_RATIO_IASIG, (Environment|Get|Set|Float) },
|
||||
{ "AL_ENV_REFLECTIONS_DELAY_IASIG", AL_ENV_REFLECTIONS_DELAY_IASIG, (Environment|Get|Set|Float) },
|
||||
{ "AL_ENV_REVERB_DELAY_IASIG", AL_ENV_REVERB_DELAY_IASIG, (Environment|Get|Set|Float) },
|
||||
{ "AL_ENV_DIFFUSION_IASIG", AL_ENV_DIFFUSION_IASIG, (Environment|Get|Set|Float) },
|
||||
{ "AL_ENV_DENSITY_IASIG", AL_ENV_DENSITY_IASIG, (Environment|Get|Set|Float) },
|
||||
{ "AL_ENV_HIGH_FREQUENCY_REFERENCE_IASIG", AL_ENV_HIGH_FREQUENCY_REFERENCE_IASIG, (Environment|Get|Set|Float) },
|
||||
|
||||
{ "AL_ENV_ROOM_VOLUME_EXT", AL_ENV_ROOM_VOLUME_EXT, (Environment|Get|Set|Int) },
|
||||
{ "AL_ENV_FLAGS_EXT", AL_ENV_FLAGS_EXT, (Environment|Get|Set|Int) },
|
||||
{ "AL_ENV_EFFECT_VOLUME_EXT", AL_ENV_EFFECT_VOLUME_EXT, (Environment|Get|Set|Float) },
|
||||
{ "AL_ENV_DAMPING_EXT", AL_ENV_DAMPING_EXT, (Environment|Get|Set|Float) },
|
||||
{ "AL_ENV_ENVIRONMENT_SIZE_EXT", AL_ENV_ENVIRONMENT_SIZE_EXT, (Environment|Get|Set|Float) },
|
||||
|
||||
// sample environment
|
||||
{ "AL_ENV_SAMPLE_DIRECT_EXT", AL_ENV_SAMPLE_DIRECT_EXT, (Source|Get|Set|Int) },
|
||||
{ "AL_ENV_SAMPLE_DIRECT_HF_EXT", AL_ENV_SAMPLE_DIRECT_HF_EXT, (Source|Get|Set|Int) },
|
||||
{ "AL_ENV_SAMPLE_ROOM_EXT", AL_ENV_SAMPLE_ROOM_EXT, (Source|Get|Set|Int) },
|
||||
{ "AL_ENV_SAMPLE_ROOM_HF_EXT", AL_ENV_SAMPLE_ROOM_HF_EXT, (Source|Get|Set|Int) },
|
||||
{ "AL_ENV_SAMPLE_OUTSIDE_VOLUME_HF_EXT", AL_ENV_SAMPLE_OUTSIDE_VOLUME_HF_EXT, (Source|Get|Set|Int) },
|
||||
{ "AL_ENV_SAMPLE_FLAGS_EXT", AL_ENV_SAMPLE_FLAGS_EXT, (Source|Get|Set|Int) },
|
||||
|
||||
{ "AL_ENV_SAMPLE_REVERB_MIX_EXT", AL_ENV_SAMPLE_REVERB_MIX_EXT, (Source|Get|Set|Float) },
|
||||
{ "AL_ENV_SAMPLE_OBSTRUCTION_EXT", AL_ENV_SAMPLE_OBSTRUCTION_EXT, (Source|Get|Set|Float) },
|
||||
{ "AL_ENV_SAMPLE_OBSTRUCTION_LF_RATIO_EXT", AL_ENV_SAMPLE_OBSTRUCTION_LF_RATIO_EXT, (Source|Get|Set|Float) },
|
||||
{ "AL_ENV_SAMPLE_OCCLUSION_EXT", AL_ENV_SAMPLE_OCCLUSION_EXT, (Source|Get|Set|Float) },
|
||||
{ "AL_ENV_SAMPLE_OCCLUSION_LF_RATIO_EXT", AL_ENV_SAMPLE_OCCLUSION_LF_RATIO_EXT, (Source|Get|Set|Float) },
|
||||
{ "AL_ENV_SAMPLE_OCCLUSION_ROOM_RATIO_EXT", AL_ENV_SAMPLE_OCCLUSION_ROOM_RATIO_EXT, (Source|Get|Set|Float) },
|
||||
{ "AL_ENV_SAMPLE_ROOM_ROLLOFF_EXT", AL_ENV_SAMPLE_ROOM_ROLLOFF_EXT, (Source|Get|Set|Float) },
|
||||
{ "AL_ENV_SAMPLE_AIR_ABSORPTION_EXT", AL_ENV_SAMPLE_AIR_ABSORPTION_EXT, (Source|Get|Set|Float) },
|
||||
*/
|
||||
};
|
||||
for(U32 i = 0; i < (sizeof(table) / sizeof(table[0])); i++)
|
||||
{
|
||||
if((table[i].mFlags & flags) != flags)
|
||||
continue;
|
||||
|
||||
if(dStricmp(table[i].mName, name) == 0)
|
||||
return(table[i].mAlenum);
|
||||
}
|
||||
|
||||
return(AL_INVALID);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------
|
||||
ConsoleFunctionGroupBegin(Audio, "Functions dealing with the OpenAL audio layer.\n\n"
|
||||
"@see www.OpenAL.org for what these functions do. Variances from posted"
|
||||
" behaviour is described below.");
|
||||
|
||||
ConsoleFunction(OpenALInitDriver, bool, 1, 1, "Initializes the OpenAL driver.\n\n"
|
||||
"@note You must call this before any sounds will work!")
|
||||
{
|
||||
if (Audio::OpenALInit())
|
||||
{
|
||||
static bool registered = false;
|
||||
if (!registered) {
|
||||
ResourceManager->registerExtension(".wav", AudioBuffer::construct);
|
||||
ResourceManager->registerExtension(".ogg", AudioBuffer::construct);
|
||||
}
|
||||
registered = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------
|
||||
ConsoleFunction(OpenALShutdownDriver, void, 1, 1, "OpenALShutdownDriver()")
|
||||
{
|
||||
Audio::OpenALShutdown();
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------
|
||||
ConsoleFunction(OpenALRegisterExtensions, void, 1, 1, "OpenALRegisterExtensions()")
|
||||
{
|
||||
}
|
||||
|
||||
//-----------------------------------------------
|
||||
ConsoleFunction(alGetString, const char *, 2, 2, "(string item)\n\n"
|
||||
"This wraps alGetString().")
|
||||
{
|
||||
argc;
|
||||
ALenum e = getEnum(argv[1], (Context|Get));
|
||||
if(e == AL_INVALID)
|
||||
{
|
||||
Con::errorf(ConsoleLogEntry::General, "alGetString: invalid enum name '%s'", argv[1]);
|
||||
return "";
|
||||
}
|
||||
|
||||
return (const char*)alGetString(e);
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Soundfile
|
||||
//--------------------------------------------------------------------------
|
||||
ConsoleFunction(alxGetWaveLen, S32, 2, 2, "(string filename)\n\n"
|
||||
"@param filename File to determine length of.\n"
|
||||
"@returns Length in milliseconds.")
|
||||
{
|
||||
Resource<AudioBuffer> buffer = AudioBuffer::find(argv[1]);
|
||||
if (bool(buffer)) {
|
||||
ALuint alBuffer = buffer->getALBuffer();
|
||||
return alxGetWaveLen(alBuffer);
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Source
|
||||
//--------------------------------------------------------------------------
|
||||
ConsoleFunction(alxCreateSource, S32, 2, 6,
|
||||
"(profile) or "
|
||||
"(profile, x,y,z) or "
|
||||
"(description, filename) or "
|
||||
"(description, filename, x,y,z)")
|
||||
{
|
||||
AudioDescription *description = NULL;
|
||||
AudioProfile *profile = dynamic_cast<AudioProfile*>( Sim::findObject( argv[1] ) );
|
||||
if (profile == NULL)
|
||||
{
|
||||
description = dynamic_cast<AudioDescription*>( Sim::findObject( argv[1] ) );
|
||||
if (description == NULL)
|
||||
{
|
||||
Con::printf("Unable to locate audio profile/description '%s'", argv[1]);
|
||||
return NULL_AUDIOHANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
if (profile)
|
||||
{
|
||||
if (argc == 2)
|
||||
return alxCreateSource(profile);
|
||||
|
||||
MatrixF transform;
|
||||
transform.set(EulerF(0,0,0), Point3F( dAtof(argv[2]),dAtof(argv[3]),dAtof(argv[4]) ));
|
||||
return alxCreateSource(profile, &transform);
|
||||
}
|
||||
|
||||
if (description)
|
||||
{
|
||||
|
||||
if (argc == 3)
|
||||
return alxCreateSource(description, argv[2]);
|
||||
|
||||
MatrixF transform;
|
||||
transform.set(EulerF(0,0,0), Point3F( dAtof(argv[3]),dAtof(argv[4]),dAtof(argv[5]) ));
|
||||
return alxCreateSource(description, argv[2], &transform);
|
||||
}
|
||||
|
||||
return NULL_AUDIOHANDLE;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------
|
||||
ConsoleFunction(alxSourcef, void, 4, 4, "(handle, ALenum, value)")
|
||||
{
|
||||
ALenum e = getEnum(argv[2], (Source|Set|Float));
|
||||
if(e == AL_INVALID)
|
||||
{
|
||||
Con::errorf(ConsoleLogEntry::General, "cAudio_alxSourcef: invalid enum name '%s'", argv[2]);
|
||||
return;
|
||||
}
|
||||
|
||||
alxSourcef(dAtoi(argv[1]), e, dAtof(argv[3]));
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------
|
||||
ConsoleFunction(alxSource3f, void, 3, 6, "(handle, ALenum, x, y, z)\n\n"
|
||||
"@note You can replace the last three parameters with a string, "
|
||||
"\"x y z\"")
|
||||
{
|
||||
ALenum e = getEnum(argv[2], (Source|Set|Float3));
|
||||
if(e == AL_INVALID)
|
||||
{
|
||||
Con::errorf(ConsoleLogEntry::General, "cAudio_alxSource3f: invalid enum name '%s'", argv[2]);
|
||||
return;
|
||||
}
|
||||
|
||||
if((argc != 4 && argc != 6))
|
||||
{
|
||||
Con::errorf(ConsoleLogEntry::General, "cAudio_alxSource3f: wrong number of args");
|
||||
return;
|
||||
}
|
||||
|
||||
Point3F pos;
|
||||
if(argc == 4)
|
||||
dSscanf(argv[3], "%g %g %g", &pos.x, &pos.y, &pos.z);
|
||||
else
|
||||
{
|
||||
pos.x = dAtof(argv[3]);
|
||||
pos.y = dAtof(argv[4]);
|
||||
pos.z = dAtof(argv[5]);
|
||||
}
|
||||
|
||||
alxSource3f(dAtoi(argv[1]), e, pos.x, pos.y, pos.z);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------
|
||||
ConsoleFunction(alxSourcei, void, 4, 4, "(handle, ALenum, value)")
|
||||
{
|
||||
ALenum e = getEnum(argv[2], (Source|Set|Int));
|
||||
if(e == AL_INVALID)
|
||||
{
|
||||
Con::errorf(ConsoleLogEntry::General, "cAudio_alxSourcei: invalid enum name '%s'", argv[2]);
|
||||
return;
|
||||
}
|
||||
|
||||
alxSourcei(dAtoi(argv[1]), e, dAtoi(argv[3]));
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------
|
||||
ConsoleFunction(alxGetSourcef, F32, 3, 3, "(handle, ALenum)")
|
||||
{
|
||||
ALenum e = getEnum(argv[2], (Source|Get|Float));
|
||||
if(e == AL_INVALID)
|
||||
{
|
||||
Con::errorf(ConsoleLogEntry::General, "cAudio_alxGetSourcef: invalid enum name '%s'", argv[2]);
|
||||
return(0.f);
|
||||
}
|
||||
|
||||
F32 value;
|
||||
alxGetSourcef(dAtoi(argv[1]), e, &value);
|
||||
return(value);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------
|
||||
ConsoleFunction(alxGetSource3f, const char *, 3, 3, "(handle, ALenum)" )
|
||||
{
|
||||
ALenum e = getEnum(argv[2], (Source|Get|Float));
|
||||
if(e == AL_INVALID)
|
||||
{
|
||||
Con::errorf(ConsoleLogEntry::General, "cAudio_alxGetSource3f: invalid enum name '%s'", argv[2]);
|
||||
return("0 0 0");
|
||||
}
|
||||
|
||||
F32 value1, value2, value3;
|
||||
alxGetSource3f(dAtoi(argv[1]), e, &value1, &value2, &value3);
|
||||
|
||||
char * ret = Con::getReturnBuffer(64);
|
||||
dSprintf(ret, 64, "%7.3f %7.3 %7.3", value1, value2, value3);
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------
|
||||
ConsoleFunction(alxGetSourcei, S32, 3, 3, "(handle, ALenum)")
|
||||
{
|
||||
ALenum e = getEnum(argv[2], (Source|Get|Int));
|
||||
if(e == AL_INVALID)
|
||||
{
|
||||
Con::errorf(ConsoleLogEntry::General, "cAudio_alxGetSourcei: invalid enum name '%s'", argv[2]);
|
||||
return(0);
|
||||
}
|
||||
|
||||
S32 value;
|
||||
alxGetSourcei(dAtoi(argv[1]), e, &value);
|
||||
return(value);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------
|
||||
ConsoleFunction(alxPlay, S32, 2, 5, "alxPlay(handle) or "
|
||||
"alxPlay(profile) or "
|
||||
"alxPlay(profile, x,y,z)")
|
||||
{
|
||||
if (argc == 2)
|
||||
{
|
||||
AUDIOHANDLE handle = dAtoi(argv[1]);
|
||||
if (handle != 0)
|
||||
return alxPlay(handle);
|
||||
}
|
||||
|
||||
AudioProfile *profile = dynamic_cast<AudioProfile*>( Sim::findObject( argv[1] ) );
|
||||
if (profile == NULL)
|
||||
{
|
||||
Con::printf("Unable to locate audio profile '%s'", argv[1]);
|
||||
return NULL_AUDIOHANDLE;
|
||||
}
|
||||
|
||||
Point3F pos(0.f, 0.f, 0.f);
|
||||
if(argc == 3)
|
||||
dSscanf(argv[2], "%g %g %g", &pos.x, &pos.y, &pos.z);
|
||||
else if(argc == 5)
|
||||
pos.set(dAtof(argv[2]), dAtof(argv[3]), dAtof(argv[4]));
|
||||
|
||||
MatrixF transform;
|
||||
transform.set(EulerF(0,0,0), pos);
|
||||
|
||||
return alxPlay(profile, &transform, NULL);
|
||||
}
|
||||
|
||||
//-----------------------------------------------
|
||||
ConsoleFunction(alxStop, void, 2, 2, "(int handle)")
|
||||
{
|
||||
AUDIOHANDLE handle = dAtoi(argv[1]);
|
||||
if(handle == NULL_AUDIOHANDLE)
|
||||
return;
|
||||
alxStop(handle);
|
||||
}
|
||||
|
||||
//-----------------------------------------------
|
||||
ConsoleFunction(alxStopAll, void, 1, 1, "()")
|
||||
{
|
||||
alxStopAll();
|
||||
}
|
||||
|
||||
//-----------------------------------------------
|
||||
ConsoleFunction(alxIsPlaying, bool, 2, 5, "alxIsPlaying(handle)")
|
||||
{
|
||||
AUDIOHANDLE handle = dAtoi(argv[1]);
|
||||
if(handle == NULL_AUDIOHANDLE)
|
||||
return false;
|
||||
return alxIsPlaying(handle);
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Listener
|
||||
//--------------------------------------------------------------------------
|
||||
ConsoleFunction(alxListenerf, void, 3, 3, "alxListener(ALenum, value)")
|
||||
{
|
||||
ALenum e = getEnum(argv[1], (Listener|Set|Float));
|
||||
if(e == AL_INVALID)
|
||||
{
|
||||
Con::errorf(ConsoleLogEntry::General, "alxListenerf: invalid enum name '%s'", argv[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
alxListenerf(e, dAtof(argv[2]));
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------
|
||||
ConsoleFunction(alListener3f, void, 3, 5, "alListener3f(ALenum, \"x y z\") or "
|
||||
"alListener3f(ALenum, x, y, z)")
|
||||
{
|
||||
ALenum e = getEnum(argv[1], (Listener|Set|Float3));
|
||||
if(e == AL_INVALID)
|
||||
{
|
||||
Con::errorf(ConsoleLogEntry::General, "alListener3f: invalid enum name '%s'", argv[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
if(argc != 3 || argc != 5)
|
||||
{
|
||||
Con::errorf(ConsoleLogEntry::General, "alListener3f: wrong number of arguments");
|
||||
return;
|
||||
}
|
||||
|
||||
Point3F pos;
|
||||
if(argc == 3)
|
||||
dSscanf(argv[2], "%g %g %g", &pos.x, &pos.y, &pos.z);
|
||||
else
|
||||
{
|
||||
pos.x = dAtof(argv[2]);
|
||||
pos.y = dAtof(argv[3]);
|
||||
pos.z = dAtof(argv[4]);
|
||||
}
|
||||
|
||||
alListener3f(e, pos.x, pos.y, pos.z);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------
|
||||
ConsoleFunction(alxGetListenerf, F32, 2, 2, "alxGetListenerf(Alenum)")
|
||||
{
|
||||
ALenum e = getEnum(argv[1], (Source|Get|Float));
|
||||
if(e == AL_INVALID)
|
||||
{
|
||||
Con::errorf(ConsoleLogEntry::General, "alxGetListenerf: invalid enum name '%s'", argv[1]);
|
||||
return(0.f);
|
||||
}
|
||||
|
||||
F32 value;
|
||||
alxGetListenerf(e, &value);
|
||||
return(value);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------
|
||||
ConsoleFunction(alGetListener3f, const char *, 2, 2, "alGetListener3f(Alenum)")
|
||||
{
|
||||
ALenum e = getEnum(argv[2], (Source|Get|Float));
|
||||
if(e == AL_INVALID)
|
||||
{
|
||||
Con::errorf(ConsoleLogEntry::General, "alGetListener3f: invalid enum name '%s'", argv[1]);
|
||||
return("0 0 0");
|
||||
}
|
||||
|
||||
F32 value1, value2, value3;
|
||||
alGetListener3f(e, &value1, &value2, &value3);
|
||||
|
||||
char * ret = Con::getReturnBuffer(64);
|
||||
dSprintf(ret, 64, "%7.3f %7.3 %7.3", value1, value2, value3);
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------
|
||||
ConsoleFunction(alGetListeneri, S32, 2, 2, "alGetListeneri(Alenum)")
|
||||
{
|
||||
ALenum e = getEnum(argv[1], (Source|Get|Int));
|
||||
if(e == AL_INVALID)
|
||||
{
|
||||
Con::errorf(ConsoleLogEntry::General, "alGetListeneri: invalid enum name '%s'", argv[1]);
|
||||
return(0);
|
||||
}
|
||||
|
||||
S32 value;
|
||||
alGetListeneri(e, &value);
|
||||
return(value);
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Channel Volumes
|
||||
//--------------------------------------------------------------------------
|
||||
ConsoleFunction(alxGetChannelVolume, F32, 2, 2, "(int channel_id)\n\n"
|
||||
"@param channel_id ID of channel to fetch volume from.\n"
|
||||
"@return Volume of channel.")
|
||||
{
|
||||
U32 type = dAtoi(argv[1]);
|
||||
if(type >= Audio::NumAudioTypes)
|
||||
{
|
||||
Con::errorf(ConsoleLogEntry::General, "alxGetChannelVolume: invalid channel '%d'", dAtoi(argv[1]));
|
||||
return(0.f);
|
||||
}
|
||||
|
||||
return(mAudioTypeVolume[type]);
|
||||
}
|
||||
|
||||
//-----------------------------------------------
|
||||
ConsoleFunction(alxSetChannelVolume, bool, 3, 3, "(int channel_id, float volume)\n\n"
|
||||
"@param channel_id ID of channel to set volume on.\n"
|
||||
"@param volume New volume of channel, from 0.0f-1.0f"
|
||||
)
|
||||
{
|
||||
U32 type = dAtoi(argv[1]);
|
||||
F32 volume = mClampF(dAtof(argv[2]), 0.f, 1.f);
|
||||
|
||||
if(type >= Audio::NumAudioTypes)
|
||||
{
|
||||
Con::errorf(ConsoleLogEntry::General, "alxSetChannelVolume: channel '%d' out of range [0, %d]", dAtoi(argv[1]), Audio::NumAudioTypes);
|
||||
return false;
|
||||
}
|
||||
|
||||
mAudioTypeVolume[type] = volume;
|
||||
alxUpdateTypeGain(type);
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------
|
||||
ConsoleFunction(alxGetStreamPosition, F32, 2, 2, "alxGetStreamPosition(handle)" )
|
||||
{
|
||||
AUDIOHANDLE handle = dAtoi(argv[1]);
|
||||
|
||||
if(handle == NULL_AUDIOHANDLE)
|
||||
return -1;
|
||||
|
||||
return alxGetStreamPosition( handle );
|
||||
}
|
||||
|
||||
//-----------------------------------------------
|
||||
ConsoleFunction(alxGetStreamDuration, F32, 2, 2, "alxGetStreamDuration(handle)" )
|
||||
{
|
||||
AUDIOHANDLE handle = dAtoi(argv[1]);
|
||||
|
||||
if(handle == NULL_AUDIOHANDLE)
|
||||
return -1;
|
||||
|
||||
return alxGetStreamDuration( handle );
|
||||
}
|
||||
|
||||
|
||||
ConsoleFunctionGroupEnd(Audio);
|
64
engine/audio/audioStreamSource.h
Executable file
64
engine/audio/audioStreamSource.h
Executable file
@ -0,0 +1,64 @@
|
||||
//--------------------------------------------
|
||||
// audioStreamSource.h
|
||||
// header for streaming audio source
|
||||
//
|
||||
// Kurtis Seebaldt
|
||||
//--------------------------------------------
|
||||
|
||||
#ifndef _AUDIOSTREAMSOURCE_H_
|
||||
#define _AUDIOSTREAMSOURCE_H_
|
||||
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "platform/platform.h"
|
||||
#endif
|
||||
#ifndef _PLATFORMAUDIO_H_
|
||||
#include "platform/platformAudio.h"
|
||||
#endif
|
||||
#ifndef _PLATFORMAL_H_
|
||||
#include "platform/platformAL.h"
|
||||
#endif
|
||||
#ifndef _AUDIOBUFFER_H_
|
||||
#include "audio/audioBuffer.h"
|
||||
#endif
|
||||
#ifndef _RESMANAGER_H_
|
||||
#include "core/resManager.h"
|
||||
#endif
|
||||
|
||||
#define NUMBUFFERS 16
|
||||
|
||||
class AudioStreamSource
|
||||
{
|
||||
public:
|
||||
//need this because subclasses are deleted through base pointer
|
||||
virtual ~AudioStreamSource() {}
|
||||
virtual bool initStream() = 0;
|
||||
virtual bool updateBuffers() = 0;
|
||||
virtual void freeStream() = 0;
|
||||
virtual F32 getElapsedTime() = 0;
|
||||
virtual F32 getTotalTime() = 0;
|
||||
//void clear();
|
||||
|
||||
AUDIOHANDLE mHandle;
|
||||
ALuint mSource;
|
||||
|
||||
Audio::Description mDescription;
|
||||
AudioSampleEnvironment *mEnvironment;
|
||||
|
||||
Point3F mPosition;
|
||||
Point3F mDirection;
|
||||
F32 mPitch;
|
||||
F32 mScore;
|
||||
U32 mCullTime;
|
||||
|
||||
bool bFinishedPlaying;
|
||||
bool bIsValid;
|
||||
|
||||
#ifdef TORQUE_OS_LINUX
|
||||
void checkPosition();
|
||||
#endif
|
||||
|
||||
protected:
|
||||
const char* mFilename;
|
||||
};
|
||||
|
||||
#endif // _AUDIOSTREAMSOURCE_H_
|
33
engine/audio/audioStreamSourceFactory.cc
Executable file
33
engine/audio/audioStreamSourceFactory.cc
Executable file
@ -0,0 +1,33 @@
|
||||
//--------------------------------------
|
||||
// audioStreamSource.cc
|
||||
// implementation of streaming audio source
|
||||
//
|
||||
// Kurtis Seebaldt
|
||||
//--------------------------------------
|
||||
|
||||
#include "audio/audioStreamSourceFactory.h"
|
||||
|
||||
#include "audio/wavStreamSource.h"
|
||||
#ifndef TORQUE_NO_OGGVORBIS
|
||||
#include "audio/vorbisStreamSource.h"
|
||||
#include "audio/oggMixedStreamSource.h"
|
||||
#endif
|
||||
|
||||
AudioStreamSource* AudioStreamSourceFactory::getNewInstance(const char *filename)
|
||||
{
|
||||
#ifndef TORQUE_NO_OGGVORBIS
|
||||
|
||||
if(!dStricmp(filename, "oggMixedStream"))
|
||||
return new OggMixedStreamSource(filename);
|
||||
|
||||
S32 len = dStrlen(filename);
|
||||
|
||||
// Check filename extension and guess filetype from that
|
||||
|
||||
if(len > 3 && !dStricmp(filename + len - 4, ".wav"))
|
||||
return new WavStreamSource(filename);
|
||||
if(len > 3 && !dStricmp(filename + len - 4, ".ogg"))
|
||||
return new VorbisStreamSource(filename);
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
30
engine/audio/audioStreamSourceFactory.h
Executable file
30
engine/audio/audioStreamSourceFactory.h
Executable file
@ -0,0 +1,30 @@
|
||||
//--------------------------------------------
|
||||
// audioStreamSource.h
|
||||
// header for streaming audio source
|
||||
//
|
||||
// Kurtis Seebaldt
|
||||
//--------------------------------------------
|
||||
|
||||
#ifndef _AUDIOSTREAMSOURCEFACTORY_H_
|
||||
#define _AUDIOSTREAMSOURCEFACTORY_H_
|
||||
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "platform/platform.h"
|
||||
#endif
|
||||
#ifndef _PLATFORMAUDIO_H_
|
||||
#include "platform/platformAudio.h"
|
||||
#endif
|
||||
#ifndef _PLATFORMAL_H_
|
||||
#include "platform/platformAL.h"
|
||||
#endif
|
||||
#ifndef _AUDIOSTREAMSOURCE_H_
|
||||
#include "audio/audioStreamSource.h"
|
||||
#endif
|
||||
|
||||
class AudioStreamSourceFactory
|
||||
{
|
||||
public:
|
||||
static AudioStreamSource* getNewInstance(const char* filename);
|
||||
};
|
||||
|
||||
#endif // _AUDIOSTREAMSOURCEFACTORY_H_
|
133
engine/audio/oggMixedStreamSource.cc
Executable file
133
engine/audio/oggMixedStreamSource.cc
Executable file
@ -0,0 +1,133 @@
|
||||
//--------------------------------------
|
||||
//
|
||||
// This class is basically a buffer that is filled from
|
||||
// the ogg stream the theoraplayer has open
|
||||
//
|
||||
//--------------------------------------
|
||||
|
||||
#include "audio/oggMixedStreamSource.h"
|
||||
|
||||
OggMixedStreamSource::OggMixedStreamSource(const char *filename)
|
||||
{
|
||||
bIsValid = false;
|
||||
bBuffersAllocated = false;
|
||||
for(int i = 0; i < BUFFERCNT; i++)
|
||||
{
|
||||
mBufferList[i] = 0;
|
||||
m_fBufferInUse[i] = false;
|
||||
}
|
||||
|
||||
|
||||
mHandle = NULL_AUDIOHANDLE;
|
||||
mSource = NULL;
|
||||
|
||||
mFilename = filename;
|
||||
mPosition = Point3F(0.f,0.f,0.f);
|
||||
|
||||
dMemset(&mDescription, 0, sizeof(Audio::Description));
|
||||
mEnvironment = 0;
|
||||
mPosition.set(0.f,0.f,0.f);
|
||||
mDirection.set(0.f,1.f,0.f);
|
||||
mPitch = 1.f;
|
||||
mScore = 0.f;
|
||||
mCullTime = 0;
|
||||
|
||||
bFinishedPlaying = false;
|
||||
bIsValid = false;
|
||||
bBuffersAllocated = false;
|
||||
}
|
||||
|
||||
OggMixedStreamSource::~OggMixedStreamSource()
|
||||
{
|
||||
if(bIsValid)
|
||||
freeStream();
|
||||
}
|
||||
|
||||
bool OggMixedStreamSource::initStream()
|
||||
{
|
||||
alSourceStop(mSource);
|
||||
alSourcei(mSource, AL_BUFFER, 0);
|
||||
|
||||
// Clear Error Code
|
||||
alGetError();
|
||||
|
||||
alGenBuffers(BUFFERCNT, mBufferList);
|
||||
if (alGetError() != AL_NO_ERROR)
|
||||
return false;
|
||||
|
||||
bBuffersAllocated = true;
|
||||
|
||||
alSourcei(mSource, AL_LOOPING, AL_FALSE);
|
||||
|
||||
bIsValid = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OggMixedStreamSource::updateBuffers()
|
||||
{
|
||||
// buffers are updated from theora player
|
||||
return true;
|
||||
}
|
||||
|
||||
void OggMixedStreamSource::freeStream()
|
||||
{
|
||||
// free the al buffers
|
||||
if(bBuffersAllocated)
|
||||
{
|
||||
alSourceStop(mSource);
|
||||
alSourcei(mSource, AL_BUFFER, 0);
|
||||
alDeleteBuffers(BUFFERCNT, mBufferList);
|
||||
|
||||
alGetError();
|
||||
|
||||
for(int i = 0; i < BUFFERCNT; i++)
|
||||
{
|
||||
mBufferList[i] = 0;
|
||||
m_fBufferInUse[i] = false;
|
||||
}
|
||||
|
||||
bBuffersAllocated = false;
|
||||
}
|
||||
}
|
||||
|
||||
ALuint OggMixedStreamSource::GetAvailableBuffer()
|
||||
{
|
||||
if(!bBuffersAllocated)
|
||||
return 0;
|
||||
|
||||
// test for unused buffers
|
||||
for(int i = 0; i < BUFFERCNT; i++)
|
||||
{
|
||||
if(!m_fBufferInUse[i])
|
||||
{
|
||||
m_fBufferInUse[i] = true;
|
||||
return mBufferList[i];
|
||||
}
|
||||
}
|
||||
|
||||
alGetError();
|
||||
|
||||
// test for processed buffers
|
||||
ALint processed;
|
||||
alGetSourcei(mSource, AL_BUFFERS_PROCESSED, &processed);
|
||||
|
||||
if(!processed)
|
||||
return 0; // no available buffers
|
||||
|
||||
ALuint BufferID;
|
||||
alSourceUnqueueBuffers(mSource, 1, &BufferID);
|
||||
|
||||
if (alGetError() != AL_NO_ERROR)
|
||||
return 0; // something went wrong..
|
||||
|
||||
return BufferID;
|
||||
}
|
||||
|
||||
bool OggMixedStreamSource::QueueBuffer(ALuint BufferID)
|
||||
{
|
||||
alSourceQueueBuffers(mSource, 1, &BufferID);
|
||||
if (alGetError() != AL_NO_ERROR)
|
||||
return false;
|
||||
return true;
|
||||
}
|
50
engine/audio/oggMixedStreamSource.h
Executable file
50
engine/audio/oggMixedStreamSource.h
Executable file
@ -0,0 +1,50 @@
|
||||
//--------------------------------------------
|
||||
// oggMixedStreamSource.h
|
||||
// header for audio stream dummy class, basicly
|
||||
// an audio buffer filled remotely
|
||||
//
|
||||
//--------------------------------------------
|
||||
|
||||
#ifndef _OGGMIXEDSTREAMSOURCE_H_
|
||||
#define _OGGMIXEDSTREAMSOURCE_H_
|
||||
|
||||
#ifndef _AUDIOSTREAMSOURCE_H_
|
||||
#include "audio/audioStreamSource.h"
|
||||
#endif
|
||||
|
||||
#define BUFFERCNT 128
|
||||
|
||||
class OggMixedStreamSource: public AudioStreamSource
|
||||
{
|
||||
public:
|
||||
OggMixedStreamSource(const char *filename);
|
||||
virtual ~OggMixedStreamSource();
|
||||
|
||||
virtual bool initStream();
|
||||
virtual bool updateBuffers();
|
||||
virtual void freeStream();
|
||||
|
||||
ALuint GetAvailableBuffer();
|
||||
bool QueueBuffer(ALuint BufferID);
|
||||
void PlayStream();
|
||||
|
||||
virtual F32 getElapsedTime()
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
virtual F32 getTotalTime()
|
||||
{
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
private:
|
||||
ALuint mBufferList[BUFFERCNT];
|
||||
bool m_fBufferInUse[BUFFERCNT];
|
||||
|
||||
bool bBuffersAllocated;
|
||||
|
||||
void clear();
|
||||
};
|
||||
|
||||
#endif // _OGGMIXEDSTREAMSOURCE_H_
|
1535
engine/audio/vorbisStream.cc
Executable file
1535
engine/audio/vorbisStream.cc
Executable file
File diff suppressed because it is too large
Load Diff
156
engine/audio/vorbisStream.h
Executable file
156
engine/audio/vorbisStream.h
Executable file
@ -0,0 +1,156 @@
|
||||
/********************************************************************
|
||||
* *
|
||||
* THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. *
|
||||
* USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
|
||||
* GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
|
||||
* IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
|
||||
* *
|
||||
* THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2001 *
|
||||
* by the XIPHOPHORUS Company http://www.xiph.org/ *
|
||||
* *
|
||||
********************************************************************
|
||||
|
||||
function: stdio-based convenience library for opening/seeking/decoding
|
||||
|
||||
********************************************************************/
|
||||
|
||||
// modified vorbisfile to use Torque Streams
|
||||
// Kurtis Seebaldt
|
||||
|
||||
|
||||
#ifndef _OV_FILE_H_
|
||||
#define _OV_FILE_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#include "vorbis/codec.h"
|
||||
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "platform/platform.h"
|
||||
#endif
|
||||
#ifndef _RESMANAGER_H_
|
||||
#include "core/resManager.h"
|
||||
#endif
|
||||
|
||||
/* The function prototypes for the callbacks are basically the same as for
|
||||
* the stdio functions fread, fseek, fclose, ftell.
|
||||
* The one difference is that the FILE * arguments have been replaced with
|
||||
* a void * - this is to be used as a pointer to whatever internal data these
|
||||
* functions might need. In the stdio case, it's just a FILE * cast to a void *
|
||||
*
|
||||
* If you use other functions, check the docs for these functions and return
|
||||
* the right values. For seek_func(), you *MUST* return -1 if the stream is
|
||||
* unseekable
|
||||
*/
|
||||
//typedef struct {
|
||||
// size_t (*read_func) (Stream *ptr, size_t size, size_t nmemb, void *datasource);
|
||||
// int (*seek_func) (Stream *datasource, ogg_int64_t offset, int whence);
|
||||
// int (*close_func) (void *datasource);
|
||||
// long (*tell_func) (void *datasource);
|
||||
//} ov_callbacks;
|
||||
|
||||
#define NOTOPEN 0
|
||||
#define PARTOPEN 1
|
||||
#define OPENED 2
|
||||
#define STREAMSET 3
|
||||
#define INITSET 4
|
||||
|
||||
typedef struct OggVorbis_File {
|
||||
Stream *datasource; /* Pointer to a FILE *, etc. */
|
||||
int seekable;
|
||||
ogg_int64_t offset;
|
||||
ogg_int64_t end;
|
||||
ogg_sync_state oy;
|
||||
|
||||
/* If the FILE handle isn't seekable (eg, a pipe), only the current
|
||||
stream appears */
|
||||
int links;
|
||||
ogg_int64_t *offsets;
|
||||
ogg_int64_t *dataoffsets;
|
||||
long *serialnos;
|
||||
ogg_int64_t *pcmlengths;
|
||||
vorbis_info *vi;
|
||||
vorbis_comment *vc;
|
||||
|
||||
/* Decoding working state local storage */
|
||||
ogg_int64_t pcm_offset;
|
||||
int ready_state;
|
||||
long current_serialno;
|
||||
int current_link;
|
||||
|
||||
double bittrack;
|
||||
double samptrack;
|
||||
|
||||
ogg_stream_state os; /* take physical pages, weld into a logical
|
||||
stream of packets */
|
||||
vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */
|
||||
vorbis_block vb; /* local working space for packet->PCM decode */
|
||||
|
||||
// ov_callbacks callbacks;
|
||||
|
||||
} OggVorbis_File;
|
||||
|
||||
|
||||
class OggVorbisFile {
|
||||
|
||||
public:
|
||||
OggVorbisFile();
|
||||
~OggVorbisFile();
|
||||
int ov_clear();
|
||||
int ov_open(Stream *stream,char *initial,long ibytes);
|
||||
int ov_open_callbacks(Stream *datasource,
|
||||
char *initial, long ibytes);
|
||||
|
||||
int ov_test(Stream *stream,char *initial,long ibytes);
|
||||
int ov_test_callbacks(Stream *datasource,
|
||||
char *initial, long ibytes);
|
||||
int ov_test_open();
|
||||
|
||||
long ov_bitrate(int i);
|
||||
long ov_bitrate_instant();
|
||||
long ov_streams();
|
||||
long ov_seekable();
|
||||
long ov_serialnumber(int i);
|
||||
|
||||
ogg_int64_t ov_raw_total(int i);
|
||||
ogg_int64_t ov_pcm_total(int i);
|
||||
double ov_time_total(int i);
|
||||
|
||||
int ov_raw_seek(long pos);
|
||||
int ov_pcm_seek(ogg_int64_t pos);
|
||||
int ov_pcm_seek_page(ogg_int64_t pos);
|
||||
int ov_time_seek(double pos);
|
||||
int ov_time_seek_page(double pos);
|
||||
|
||||
ogg_int64_t ov_raw_tell();
|
||||
ogg_int64_t ov_pcm_tell();
|
||||
double ov_time_tell();
|
||||
|
||||
vorbis_info *ov_info(int link);
|
||||
vorbis_comment *ov_comment(int link);
|
||||
|
||||
long ov_read_float(float ***pcm_channels,
|
||||
int *bitstream);
|
||||
long ov_read(char *buffer,int length,
|
||||
int bigendianp,int *bitstream);
|
||||
|
||||
private:
|
||||
OggVorbis_File *vf;
|
||||
|
||||
long _get_data();
|
||||
void _seek_helper(long offset);
|
||||
long _get_next_page(ogg_page *og,int boundary);
|
||||
long _get_prev_page(ogg_page *og);
|
||||
int _bisect_forward_serialno(long begin,long searched,long end,long currentno,long m);
|
||||
int _fetch_headers(vorbis_info *vi,vorbis_comment *vc,long *serialno,ogg_page *og_ptr);
|
||||
void _prefetch_all_headers(long dataoffset);
|
||||
void _make_decode_ready();
|
||||
int _open_seekable2();
|
||||
void _decode_clear();
|
||||
int _process_packet(int readp);
|
||||
int _fseek64_wrap(Stream *stream,ogg_int64_t off,int whence);
|
||||
int _ov_open1(Stream *stream,char *initial,long ibytes);
|
||||
int _ov_open2();
|
||||
|
||||
};
|
||||
|
||||
#endif
|
442
engine/audio/vorbisStreamSource.cc
Executable file
442
engine/audio/vorbisStreamSource.cc
Executable file
@ -0,0 +1,442 @@
|
||||
//--------------------------------------
|
||||
// vorbisStreamSource.cc
|
||||
// implementation of streaming audio source
|
||||
//
|
||||
// Kurtis Seebaldt
|
||||
//--------------------------------------
|
||||
|
||||
#include "audio/vorbisStreamSource.h"
|
||||
#include "vorbis/codec.h"
|
||||
|
||||
#define BUFFERSIZE 32768
|
||||
#define CHUNKSIZE 4096
|
||||
|
||||
#if defined(TORQUE_BIG_ENDIAN)
|
||||
#define ENDIAN 1
|
||||
#else
|
||||
#define ENDIAN 0
|
||||
#endif
|
||||
|
||||
extern const char * MusicPlayerStreamingHook(const AUDIOHANDLE & handle);
|
||||
|
||||
VorbisStreamSource::VorbisStreamSource(const char *filename)
|
||||
{
|
||||
stream = NULL;
|
||||
bIsValid = false;
|
||||
bBuffersAllocated = false;
|
||||
bVorbisFileInitialized = false;
|
||||
mBufferList[0] = 0;
|
||||
clear();
|
||||
|
||||
mFilename = filename;
|
||||
mPosition = Point3F(0.f,0.f,0.f);
|
||||
}
|
||||
|
||||
VorbisStreamSource::~VorbisStreamSource()
|
||||
{
|
||||
if(bReady && bIsValid)
|
||||
freeStream();
|
||||
}
|
||||
|
||||
void VorbisStreamSource::clear()
|
||||
{
|
||||
if(stream)
|
||||
freeStream();
|
||||
|
||||
mHandle = NULL_AUDIOHANDLE;
|
||||
mSource = NULL;
|
||||
|
||||
if(mBufferList[0] != 0)
|
||||
alDeleteBuffers(NUMBUFFERS, mBufferList);
|
||||
for(int i = 0; i < NUMBUFFERS; i++)
|
||||
mBufferList[i] = 0;
|
||||
|
||||
dMemset(&mDescription, 0, sizeof(Audio::Description));
|
||||
mEnvironment = 0;
|
||||
mPosition.set(0.f,0.f,0.f);
|
||||
mDirection.set(0.f,1.f,0.f);
|
||||
mPitch = 1.f;
|
||||
mScore = 0.f;
|
||||
mCullTime = 0;
|
||||
|
||||
bReady = false;
|
||||
bFinishedPlaying = false;
|
||||
bIsValid = false;
|
||||
bBuffersAllocated = false;
|
||||
bVorbisFileInitialized = false;
|
||||
}
|
||||
|
||||
bool VorbisStreamSource::initStream()
|
||||
{
|
||||
vorbis_info *vi;
|
||||
|
||||
ALint error;
|
||||
|
||||
bFinished = false;
|
||||
|
||||
// JMQ: changed buffer to static and doubled size. workaround for
|
||||
// https://206.163.64.242/mantis/view_bug_page.php?f_id=0000242
|
||||
static char data[BUFFERSIZE*2];
|
||||
|
||||
alSourceStop(mSource);
|
||||
alSourcei(mSource, AL_BUFFER, 0);
|
||||
|
||||
stream = ResourceManager->openStream(mFilename);
|
||||
if(stream != NULL)
|
||||
{
|
||||
if(vf.ov_open(stream, NULL, 0) < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bVorbisFileInitialized = true;
|
||||
|
||||
//Read Vorbis File Info
|
||||
vi = vf.ov_info(-1);
|
||||
freq = vi->rate;
|
||||
|
||||
long samples = (long)vf.ov_pcm_total(-1);
|
||||
|
||||
if(vi->channels == 1)
|
||||
{
|
||||
format = AL_FORMAT_MONO16;
|
||||
DataSize = 2 * samples;
|
||||
}
|
||||
else
|
||||
{
|
||||
format = AL_FORMAT_STEREO16;
|
||||
DataSize = 4 * samples;
|
||||
}
|
||||
DataLeft = DataSize;
|
||||
|
||||
// Clear Error Code
|
||||
alGetError();
|
||||
|
||||
alGenBuffers(NUMBUFFERS, mBufferList);
|
||||
if ((error = alGetError()) != AL_NO_ERROR)
|
||||
return false;
|
||||
|
||||
bBuffersAllocated = true;
|
||||
|
||||
int numBuffers = 0;
|
||||
for(int loop = 0; loop < NUMBUFFERS; loop++)
|
||||
{
|
||||
ALuint DataToRead = (DataLeft > BUFFERSIZE) ? BUFFERSIZE : DataLeft;
|
||||
if (DataToRead == DataLeft)
|
||||
bFinished = AL_TRUE;
|
||||
|
||||
long ret = oggRead(data, BUFFERSIZE, ENDIAN, ¤t_section);
|
||||
if(ret <= 0)
|
||||
{
|
||||
bFinished = AL_TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
DataLeft -= ret;
|
||||
alBufferData(mBufferList[loop], format, data, ret, freq);
|
||||
++numBuffers;
|
||||
|
||||
if ((error = alGetError()) != AL_NO_ERROR)
|
||||
return false;
|
||||
if(bFinished)
|
||||
break;
|
||||
}
|
||||
|
||||
// Queue the buffers on the source
|
||||
alSourceQueueBuffers(mSource, NUMBUFFERS, mBufferList);
|
||||
if ((error = alGetError()) != AL_NO_ERROR)
|
||||
return false;
|
||||
|
||||
alSourcei(mSource, AL_LOOPING, AL_FALSE);
|
||||
bReady = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bIsValid = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VorbisStreamSource::updateBuffers()
|
||||
{
|
||||
ALint processed;
|
||||
ALuint BufferID;
|
||||
ALint error;
|
||||
// JMQ: changed buffer to static and doubled size. workaround for
|
||||
// https://206.163.64.242/mantis/view_bug_page.php?f_id=0000242
|
||||
static char data[BUFFERSIZE*2];
|
||||
|
||||
// don't do anything if stream not loaded properly
|
||||
if(!bIsValid)
|
||||
return false;
|
||||
|
||||
if(bFinished && mDescription.mIsLooping)
|
||||
resetStream();
|
||||
|
||||
// reset AL error code
|
||||
alGetError();
|
||||
|
||||
#if 1 //def TORQUE_OS_LINUX
|
||||
// JMQ: this doesn't really help on linux. it may make things worse.
|
||||
// if it doesn't help on mac/win either, could disable it.
|
||||
ALint state;
|
||||
alGetSourcei(mSource, AL_SOURCE_STATE, &state);
|
||||
if (state == AL_STOPPED)
|
||||
{
|
||||
// um, yeah. you should be playing
|
||||
// restart
|
||||
alSourcePlay(mSource);
|
||||
//#ifdef TORQUE_DEBUG
|
||||
//Con::errorf(">><<>><< THE MUSIC STOPPED >><<>><<");
|
||||
//#endif
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef TORQUE_OS_LINUX
|
||||
checkPosition();
|
||||
#endif
|
||||
|
||||
// Get status
|
||||
alGetSourcei(mSource, AL_BUFFERS_PROCESSED, &processed);
|
||||
|
||||
// If some buffers have been played, unqueue them and load new audio into them, then add them to the queue
|
||||
if (processed > 0)
|
||||
{
|
||||
while (processed)
|
||||
{
|
||||
alSourceUnqueueBuffers(mSource, 1, &BufferID);
|
||||
if ((error = alGetError()) != AL_NO_ERROR)
|
||||
return false;
|
||||
|
||||
if (!bFinished)
|
||||
{
|
||||
ALuint DataToRead = (DataLeft > BUFFERSIZE) ? BUFFERSIZE : DataLeft;
|
||||
|
||||
if (DataToRead == DataLeft)
|
||||
bFinished = AL_TRUE;
|
||||
|
||||
long ret = oggRead(data, BUFFERSIZE, ENDIAN, ¤t_section);
|
||||
if(ret > 0)
|
||||
{
|
||||
DataLeft -= ret;
|
||||
|
||||
alBufferData(BufferID, format, data, ret, freq);
|
||||
if ((error = alGetError()) != AL_NO_ERROR)
|
||||
return false;
|
||||
|
||||
// Queue buffer
|
||||
alSourceQueueBuffers(mSource, 1, &BufferID);
|
||||
if ((error = alGetError()) != AL_NO_ERROR)
|
||||
return false;
|
||||
}
|
||||
|
||||
processed--;
|
||||
|
||||
if(bFinished && mDescription.mIsLooping)
|
||||
{
|
||||
resetStream();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
buffersinqueue--;
|
||||
processed--;
|
||||
|
||||
if (buffersinqueue == 0)
|
||||
{
|
||||
bFinishedPlaying = AL_TRUE;
|
||||
return AL_FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void VorbisStreamSource::freeStream()
|
||||
{
|
||||
bReady = false;
|
||||
|
||||
if(stream != NULL)
|
||||
ResourceManager->closeStream(stream);
|
||||
|
||||
stream = NULL;
|
||||
|
||||
if(bBuffersAllocated)
|
||||
{
|
||||
if(mBufferList[0] != 0)
|
||||
alDeleteBuffers(NUMBUFFERS, mBufferList);
|
||||
for(int i = 0; i < NUMBUFFERS; i++)
|
||||
mBufferList[i] = 0;
|
||||
|
||||
bBuffersAllocated = false;
|
||||
}
|
||||
|
||||
if(bVorbisFileInitialized)
|
||||
{
|
||||
vf.ov_clear();
|
||||
bVorbisFileInitialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
void VorbisStreamSource::resetStream()
|
||||
{
|
||||
// MusicPlayerStreamingHook allow you to create a handler
|
||||
// where you can rotate through streaming files
|
||||
// Comment in and provide hook if desired.
|
||||
//
|
||||
//const char * newFile = MusicPlayerStreamingHook(mHandle);
|
||||
//if (newFile)
|
||||
//{
|
||||
// setNewFile(newFile);
|
||||
// return;
|
||||
//}
|
||||
//else
|
||||
{
|
||||
vf.ov_pcm_seek(0);
|
||||
DataLeft = DataSize;
|
||||
bFinished = AL_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
void VorbisStreamSource::setNewFile(const char * file)
|
||||
{
|
||||
//---------------------
|
||||
// close down old file...
|
||||
//---------------------
|
||||
|
||||
if(stream != NULL)
|
||||
{
|
||||
ResourceManager->closeStream(stream);
|
||||
stream = NULL;
|
||||
}
|
||||
|
||||
if(bVorbisFileInitialized)
|
||||
{
|
||||
vf.ov_clear();
|
||||
bVorbisFileInitialized = false;
|
||||
}
|
||||
|
||||
//---------------------
|
||||
// open up new file...
|
||||
//---------------------
|
||||
|
||||
mFilename = file;
|
||||
stream = ResourceManager->openStream(mFilename);
|
||||
if(stream != NULL)
|
||||
{
|
||||
if(vf.ov_open(stream, NULL, 0) < 0)
|
||||
{
|
||||
bFinished = AL_TRUE;
|
||||
bVorbisFileInitialized = false;
|
||||
bIsValid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
//Read Vorbis File Info
|
||||
vorbis_info * vi = vf.ov_info(-1);
|
||||
freq = vi->rate;
|
||||
|
||||
long samples = (long)vf.ov_pcm_total(-1);
|
||||
|
||||
if(vi->channels == 1)
|
||||
{
|
||||
format = AL_FORMAT_MONO16;
|
||||
DataSize = 2 * samples;
|
||||
}
|
||||
else
|
||||
{
|
||||
format = AL_FORMAT_STEREO16;
|
||||
DataSize = 4 * samples;
|
||||
}
|
||||
DataLeft = DataSize;
|
||||
|
||||
// Clear Error Code
|
||||
alGetError();
|
||||
|
||||
bFinished = AL_FALSE;
|
||||
bVorbisFileInitialized = true;
|
||||
bIsValid = true;
|
||||
}
|
||||
}
|
||||
|
||||
// ov_read() only returns a maximum of one page worth of data
|
||||
// this helper function will repeat the read until buffer is full
|
||||
long VorbisStreamSource::oggRead(char *buffer,int length,
|
||||
int bigendianp,int *bitstream)
|
||||
{
|
||||
long bytesRead = 0;
|
||||
long totalBytes = 0;
|
||||
long offset = 0;
|
||||
long bytesToRead = 0;
|
||||
|
||||
while((offset) < length)
|
||||
{
|
||||
if((length - offset) < CHUNKSIZE)
|
||||
bytesToRead = length - offset;
|
||||
else
|
||||
bytesToRead = CHUNKSIZE;
|
||||
|
||||
bytesRead = vf.ov_read(buffer, bytesToRead, bigendianp, bitstream);
|
||||
//#ifdef TORQUE_OS_LINUX
|
||||
#if 1 // Might fix mac audio issue and possibly others...based on references, this looks like correct action
|
||||
// linux ver will hang on exit after a stream loop if we don't
|
||||
// do this
|
||||
if (bytesRead == OV_HOLE)
|
||||
// retry, see:
|
||||
// http://www.xiph.org/archives/vorbis-dev/200102/0163.html
|
||||
// http://www.mit.edu/afs/sipb/user/xiphmont/ogg-sandbox/vorbis/doc/vorbis-errors.txt
|
||||
continue;
|
||||
#endif
|
||||
|
||||
if(bytesRead <= 0)
|
||||
break;
|
||||
offset += bytesRead;
|
||||
buffer += bytesRead;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
#ifdef TORQUE_OS_LINUX
|
||||
|
||||
// JMQ: OpenAL sometimes replaces the stream source's position with its own
|
||||
// nifty value, causing the music to pan around the listener. how nice.
|
||||
// this function checks to see if the current source position in openal
|
||||
// is near the initial position, and slams it to the correct value if it
|
||||
// is wrong.
|
||||
|
||||
// This is a bad place to put this, but I don't feel like adding a new
|
||||
// .cc file. And since this is an incredibly lame hack to
|
||||
// workaround a stupid OpenAL bug, I see no point in overengineering it.
|
||||
|
||||
void AudioStreamSource::checkPosition()
|
||||
{
|
||||
ALfloat pos[3];
|
||||
alGetSourcefv(mSource, AL_POSITION, pos);
|
||||
|
||||
// just compute the difference between the openal pos and the
|
||||
// correct pos. it better be pretty friggin small.
|
||||
Point3F openalPos(pos[0], pos[1], pos[2]);
|
||||
F32 slopDist = 0.0001f;
|
||||
|
||||
F32 dist = mFabs((openalPos - mPosition).len());
|
||||
if (dist > slopDist)
|
||||
// slam!
|
||||
alSource3f(mSource, AL_POSITION, mPosition.x, mPosition.y, mPosition.z);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
F32 VorbisStreamSource::getElapsedTime()
|
||||
{
|
||||
return vf.ov_time_tell();
|
||||
}
|
||||
|
||||
F32 VorbisStreamSource::getTotalTime()
|
||||
{
|
||||
return vf.ov_time_total(-1);
|
||||
}
|
58
engine/audio/vorbisStreamSource.h
Executable file
58
engine/audio/vorbisStreamSource.h
Executable file
@ -0,0 +1,58 @@
|
||||
//--------------------------------------------
|
||||
// vorbisStreamSource.h
|
||||
// header for streaming audio source for Ogg Vorbis
|
||||
//
|
||||
// Kurtis Seebaldt
|
||||
//--------------------------------------------
|
||||
|
||||
#ifndef _VORBISSTREAMSOURCE_H_
|
||||
#define _VORBISSTREAMSOURCE_H_
|
||||
|
||||
#ifndef _AUDIOSTREAMSOURCE_H_
|
||||
#include "audio/audioStreamSource.h"
|
||||
#endif
|
||||
|
||||
#include "audio/vorbisStream.h"
|
||||
|
||||
class VorbisStreamSource: public AudioStreamSource
|
||||
{
|
||||
public:
|
||||
VorbisStreamSource(const char *filename);
|
||||
virtual ~VorbisStreamSource();
|
||||
|
||||
virtual bool initStream();
|
||||
virtual bool updateBuffers();
|
||||
virtual void freeStream();
|
||||
virtual F32 getElapsedTime();
|
||||
virtual F32 getTotalTime();
|
||||
|
||||
private:
|
||||
ALuint mBufferList[NUMBUFFERS];
|
||||
S32 mNumBuffers;
|
||||
S32 mBufferSize;
|
||||
Stream *stream;
|
||||
|
||||
bool bReady;
|
||||
bool bFinished;
|
||||
|
||||
ALenum format;
|
||||
ALsizei size;
|
||||
ALsizei freq;
|
||||
|
||||
ALuint DataSize;
|
||||
ALuint DataLeft;
|
||||
ALuint buffersinqueue;
|
||||
|
||||
bool bBuffersAllocated;
|
||||
bool bVorbisFileInitialized;
|
||||
|
||||
int current_section;
|
||||
OggVorbisFile vf;
|
||||
|
||||
void clear();
|
||||
long oggRead(char *buffer,int length, int bigendianp,int *bitstream);
|
||||
void resetStream();
|
||||
void setNewFile(const char * file);
|
||||
};
|
||||
|
||||
#endif // _VORBISSTREAMSOURCE_H_
|
334
engine/audio/wavStreamSource.cc
Executable file
334
engine/audio/wavStreamSource.cc
Executable file
@ -0,0 +1,334 @@
|
||||
//--------------------------------------
|
||||
// audioStreamSource.cc
|
||||
// implementation of streaming audio source
|
||||
//
|
||||
// Kurtis Seebaldt
|
||||
//--------------------------------------
|
||||
|
||||
#include "audio/wavStreamSource.h"
|
||||
|
||||
#define BUFFERSIZE 32768
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ALubyte riff[4]; // 'RIFF'
|
||||
ALsizei riffSize;
|
||||
ALubyte wave[4]; // 'WAVE'
|
||||
ALubyte fmt[4]; // 'fmt '
|
||||
ALuint fmtSize;
|
||||
ALushort Format;
|
||||
ALushort Channels;
|
||||
ALuint SamplesPerSec;
|
||||
ALuint BytesPerSec;
|
||||
ALushort BlockAlign;
|
||||
ALushort BitsPerSample;
|
||||
ALubyte data[4]; // 'data'
|
||||
ALuint dataSize;
|
||||
} WAVE_Struct;
|
||||
|
||||
/// WAV File-header
|
||||
struct WAVFileHdr
|
||||
{
|
||||
ALubyte id[4];
|
||||
ALsizei size;
|
||||
ALubyte type[4];
|
||||
};
|
||||
|
||||
//// WAV Fmt-header
|
||||
struct WAVFmtHdr
|
||||
{
|
||||
ALushort format;
|
||||
ALushort channels;
|
||||
ALuint samplesPerSec;
|
||||
ALuint bytesPerSec;
|
||||
ALushort blockAlign;
|
||||
ALushort bitsPerSample;
|
||||
};
|
||||
|
||||
/// WAV FmtEx-header
|
||||
struct WAVFmtExHdr
|
||||
{
|
||||
ALushort size;
|
||||
ALushort samplesPerBlock;
|
||||
};
|
||||
|
||||
/// WAV Smpl-header
|
||||
struct WAVSmplHdr
|
||||
{
|
||||
ALuint manufacturer;
|
||||
ALuint product;
|
||||
ALuint samplePeriod;
|
||||
ALuint note;
|
||||
ALuint fineTune;
|
||||
ALuint SMPTEFormat;
|
||||
ALuint SMPTEOffest;
|
||||
ALuint loops;
|
||||
ALuint samplerData;
|
||||
struct
|
||||
{
|
||||
ALuint identifier;
|
||||
ALuint type;
|
||||
ALuint start;
|
||||
ALuint end;
|
||||
ALuint fraction;
|
||||
ALuint count;
|
||||
} loop[1];
|
||||
};
|
||||
|
||||
/// WAV Chunk-header
|
||||
struct WAVChunkHdr
|
||||
{
|
||||
ALubyte id[4];
|
||||
ALuint size;
|
||||
};
|
||||
|
||||
|
||||
|
||||
WavStreamSource::WavStreamSource(const char *filename) {
|
||||
stream = NULL;
|
||||
bIsValid = false;
|
||||
bBuffersAllocated = false;
|
||||
mBufferList[0] = 0;
|
||||
clear();
|
||||
|
||||
mFilename = filename;
|
||||
mPosition = Point3F(0.f,0.f,0.f);
|
||||
}
|
||||
|
||||
WavStreamSource::~WavStreamSource() {
|
||||
if(bReady && bIsValid)
|
||||
freeStream();
|
||||
}
|
||||
|
||||
void WavStreamSource::clear()
|
||||
{
|
||||
if(stream)
|
||||
freeStream();
|
||||
|
||||
mHandle = NULL_AUDIOHANDLE;
|
||||
mSource = NULL;
|
||||
|
||||
if(mBufferList[0] != 0)
|
||||
alDeleteBuffers(NUMBUFFERS, mBufferList);
|
||||
for(int i = 0; i < NUMBUFFERS; i++)
|
||||
mBufferList[i] = 0;
|
||||
|
||||
dMemset(&mDescription, 0, sizeof(Audio::Description));
|
||||
mEnvironment = 0;
|
||||
mPosition.set(0.f,0.f,0.f);
|
||||
mDirection.set(0.f,1.f,0.f);
|
||||
mPitch = 1.f;
|
||||
mScore = 0.f;
|
||||
mCullTime = 0;
|
||||
|
||||
bReady = false;
|
||||
bFinishedPlaying = false;
|
||||
bIsValid = false;
|
||||
bBuffersAllocated = false;
|
||||
}
|
||||
|
||||
bool WavStreamSource::initStream() {
|
||||
WAVChunkHdr chunkHdr;
|
||||
WAVFmtExHdr fmtExHdr;
|
||||
WAVFileHdr fileHdr;
|
||||
WAVSmplHdr smplHdr;
|
||||
WAVFmtHdr fmtHdr;
|
||||
|
||||
WAVE_Struct wave;
|
||||
|
||||
ALint error;
|
||||
|
||||
bFinished = false;
|
||||
|
||||
char data[BUFFERSIZE];
|
||||
|
||||
alSourceStop(mSource);
|
||||
alSourcei(mSource, AL_BUFFER, 0);
|
||||
|
||||
stream = ResourceManager->openStream(mFilename);
|
||||
if(stream != NULL) {
|
||||
stream->read(4, &fileHdr.id[0]);
|
||||
stream->read(&fileHdr.size);
|
||||
stream->read(4, &fileHdr.type[0]);
|
||||
|
||||
stream->read(4, &chunkHdr.id[0]);
|
||||
stream->read(&chunkHdr.size);
|
||||
|
||||
// WAV Format header
|
||||
stream->read(&fmtHdr.format);
|
||||
stream->read(&fmtHdr.channels);
|
||||
stream->read(&fmtHdr.samplesPerSec);
|
||||
stream->read(&fmtHdr.bytesPerSec);
|
||||
stream->read(&fmtHdr.blockAlign);
|
||||
stream->read(&fmtHdr.bitsPerSample);
|
||||
|
||||
format=(fmtHdr.channels==1?
|
||||
(fmtHdr.bitsPerSample==8?AL_FORMAT_MONO8:AL_FORMAT_MONO16):
|
||||
(fmtHdr.bitsPerSample==8?AL_FORMAT_STEREO8:AL_FORMAT_STEREO16));
|
||||
freq=fmtHdr.samplesPerSec;
|
||||
|
||||
stream->read(4, &chunkHdr.id[0]);
|
||||
stream->read(&chunkHdr.size);
|
||||
|
||||
DataSize = chunkHdr.size;
|
||||
DataLeft = DataSize;
|
||||
dataStart = stream->getPosition();
|
||||
|
||||
// Clear Error Code
|
||||
alGetError();
|
||||
|
||||
alGenBuffers(NUMBUFFERS, mBufferList);
|
||||
if ((error = alGetError()) != AL_NO_ERROR) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bBuffersAllocated = true;
|
||||
|
||||
int numBuffers = 0;
|
||||
for(int loop = 0; loop < NUMBUFFERS; loop++)
|
||||
{
|
||||
ALuint DataToRead = (DataLeft > BUFFERSIZE) ? BUFFERSIZE : DataLeft;
|
||||
|
||||
if (DataToRead == DataLeft)
|
||||
bFinished = AL_TRUE;
|
||||
stream->read(DataToRead, data);
|
||||
DataLeft -= DataToRead;
|
||||
alBufferData(mBufferList[loop], format, data, DataToRead, freq);
|
||||
if ((error = alGetError()) != AL_NO_ERROR) {
|
||||
return false;
|
||||
}
|
||||
++numBuffers;
|
||||
if(bFinished)
|
||||
break;
|
||||
}
|
||||
|
||||
// Queue the buffers on the source
|
||||
alSourceQueueBuffers(mSource, numBuffers, mBufferList);
|
||||
if ((error = alGetError()) != AL_NO_ERROR) {
|
||||
return false;
|
||||
}
|
||||
|
||||
buffersinqueue = numBuffers;
|
||||
alSourcei(mSource, AL_LOOPING, AL_FALSE);
|
||||
bReady = true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
|
||||
bIsValid = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WavStreamSource::updateBuffers() {
|
||||
|
||||
ALint processed;
|
||||
ALuint BufferID;
|
||||
ALint error;
|
||||
char data[BUFFERSIZE];
|
||||
|
||||
// don't do anything if buffer isn't initialized
|
||||
if(!bIsValid)
|
||||
return false;
|
||||
|
||||
if(bFinished && mDescription.mIsLooping) {
|
||||
resetStream();
|
||||
}
|
||||
|
||||
// reset AL error code
|
||||
alGetError();
|
||||
|
||||
// Get status
|
||||
alGetSourcei(mSource, AL_BUFFERS_PROCESSED, &processed);
|
||||
|
||||
// If some buffers have been played, unqueue them and load new audio into them, then add them to the queue
|
||||
if (processed > 0)
|
||||
{
|
||||
|
||||
while (processed)
|
||||
{
|
||||
alSourceUnqueueBuffers(mSource, 1, &BufferID);
|
||||
if ((error = alGetError()) != AL_NO_ERROR)
|
||||
return false;
|
||||
|
||||
if (!bFinished)
|
||||
{
|
||||
ALuint DataToRead = (DataLeft > BUFFERSIZE) ? BUFFERSIZE : DataLeft;
|
||||
|
||||
if (DataToRead == DataLeft) {
|
||||
bFinished = AL_TRUE;
|
||||
}
|
||||
|
||||
stream->read(DataToRead, data);
|
||||
DataLeft -= DataToRead;
|
||||
|
||||
alBufferData(BufferID, format, data, DataToRead, freq);
|
||||
if ((error = alGetError()) != AL_NO_ERROR)
|
||||
return false;
|
||||
|
||||
// Queue buffer
|
||||
alSourceQueueBuffers(mSource, 1, &BufferID);
|
||||
if ((error = alGetError()) != AL_NO_ERROR)
|
||||
return false;
|
||||
|
||||
processed--;
|
||||
|
||||
if(bFinished && mDescription.mIsLooping) {
|
||||
resetStream();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
buffersinqueue--;
|
||||
processed--;
|
||||
|
||||
if (buffersinqueue == 0)
|
||||
{
|
||||
bFinishedPlaying = AL_TRUE;
|
||||
return AL_FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return AL_TRUE;
|
||||
}
|
||||
|
||||
void WavStreamSource::freeStream() {
|
||||
bReady = false;
|
||||
|
||||
|
||||
if(stream != NULL)
|
||||
ResourceManager->closeStream(stream);
|
||||
stream = NULL;
|
||||
|
||||
if(bBuffersAllocated) {
|
||||
if(mBufferList[0] != 0)
|
||||
alDeleteBuffers(NUMBUFFERS, mBufferList);
|
||||
for(int i = 0; i < NUMBUFFERS; i++)
|
||||
mBufferList[i] = 0;
|
||||
|
||||
bBuffersAllocated = false;
|
||||
}
|
||||
}
|
||||
|
||||
void WavStreamSource::resetStream() {
|
||||
stream->setPosition(dataStart);
|
||||
DataLeft = DataSize;
|
||||
bFinished = AL_FALSE;
|
||||
}
|
||||
|
||||
#include "console/console.h"
|
||||
|
||||
F32 WavStreamSource::getElapsedTime()
|
||||
{
|
||||
Con::warnf( "GetElapsedTime not implemented in WaveStreams yet" );
|
||||
return -1.f;
|
||||
}
|
||||
|
||||
F32 WavStreamSource::getTotalTime()
|
||||
{
|
||||
Con::warnf( "GetTotalTime not implemented in WaveStreams yet" );
|
||||
return -1.f;
|
||||
}
|
49
engine/audio/wavStreamSource.h
Executable file
49
engine/audio/wavStreamSource.h
Executable file
@ -0,0 +1,49 @@
|
||||
//--------------------------------------------
|
||||
// wavStreamSource.h
|
||||
// header for streaming audio source for WAV
|
||||
//--------------------------------------------
|
||||
|
||||
#ifndef _WAVSTREAMSOURCE_H_
|
||||
#define _WAVSTREAMSOURCE_H_
|
||||
|
||||
#ifndef _AUDIOSTREAMSOURCE_H_
|
||||
#include "audio/audioStreamSource.h"
|
||||
#endif
|
||||
|
||||
class WavStreamSource: public AudioStreamSource
|
||||
{
|
||||
public:
|
||||
WavStreamSource(const char *filename);
|
||||
virtual ~WavStreamSource();
|
||||
|
||||
virtual bool initStream();
|
||||
virtual bool updateBuffers();
|
||||
virtual void freeStream();
|
||||
virtual F32 getElapsedTime();
|
||||
virtual F32 getTotalTime();
|
||||
|
||||
private:
|
||||
ALuint mBufferList[NUMBUFFERS];
|
||||
S32 mNumBuffers;
|
||||
S32 mBufferSize;
|
||||
Stream *stream;
|
||||
|
||||
bool bReady;
|
||||
bool bFinished;
|
||||
|
||||
ALenum format;
|
||||
ALsizei size;
|
||||
ALsizei freq;
|
||||
|
||||
ALuint DataSize;
|
||||
ALuint DataLeft;
|
||||
ALuint dataStart;
|
||||
ALuint buffersinqueue;
|
||||
|
||||
bool bBuffersAllocated;
|
||||
|
||||
void clear();
|
||||
void resetStream();
|
||||
};
|
||||
|
||||
#endif // _AUDIOSTREAMSOURCE_H_
|
97
engine/collision/abstractPolyList.cc
Executable file
97
engine/collision/abstractPolyList.cc
Executable file
@ -0,0 +1,97 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "collision/abstractPolyList.h"
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
AbstractPolyList::~AbstractPolyList()
|
||||
{
|
||||
mInterestNormalRegistered = false;
|
||||
}
|
||||
|
||||
static U32 PolyFace[6][4] = {
|
||||
{ 3, 2, 1, 0 },
|
||||
{ 7, 4, 5, 6 },
|
||||
{ 0, 5, 4, 3 },
|
||||
{ 6, 5, 0, 1 },
|
||||
{ 7, 6, 1, 2 },
|
||||
{ 4, 7, 2, 3 },
|
||||
};
|
||||
|
||||
void AbstractPolyList::addBox(const Box3F &box)
|
||||
{
|
||||
Point3F pos = box.min;
|
||||
F32 dx = box.max.x - box.min.x;
|
||||
F32 dy = box.max.y - box.min.y;
|
||||
F32 dz = box.max.z - box.min.z;
|
||||
|
||||
U32 base = addPoint(pos);
|
||||
pos.y += dy; addPoint(pos);
|
||||
pos.x += dx; addPoint(pos);
|
||||
pos.y -= dy; addPoint(pos);
|
||||
pos.z += dz; addPoint(pos);
|
||||
pos.x -= dx; addPoint(pos);
|
||||
pos.y += dy; addPoint(pos);
|
||||
pos.x += dx; addPoint(pos);
|
||||
|
||||
for (S32 i = 0; i < 6; i++) {
|
||||
begin(0,i);
|
||||
S32 v1 = base + PolyFace[i][0];
|
||||
S32 v2 = base + PolyFace[i][1];
|
||||
S32 v3 = base + PolyFace[i][2];
|
||||
S32 v4 = base + PolyFace[i][3];
|
||||
vertex(v1);
|
||||
vertex(v2);
|
||||
vertex(v3);
|
||||
vertex(v4);
|
||||
plane(v1,v2,v3);
|
||||
end();
|
||||
}
|
||||
}
|
||||
|
||||
bool AbstractPolyList::getMapping(MatrixF *, Box3F *)
|
||||
{
|
||||
// return list transform and bounds in list space...optional
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool AbstractPolyList::isInterestedInPlane(const PlaneF& plane)
|
||||
{
|
||||
if (mInterestNormalRegistered == false) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
PlaneF xformed;
|
||||
mPlaneTransformer.transform(plane, xformed);
|
||||
if (mDot(xformed, mInterestNormal) >= 0.0f)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool AbstractPolyList::isInterestedInPlane(const U32 index)
|
||||
{
|
||||
if (mInterestNormalRegistered == false) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
const PlaneF& rPlane = getIndexedPlane(index);
|
||||
if (mDot(rPlane, mInterestNormal) >= 0.0f)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void AbstractPolyList::setInterestNormal(const Point3F& normal)
|
||||
{
|
||||
mInterestNormalRegistered = true;
|
||||
mInterestNormal = normal;
|
||||
}
|
||||
|
241
engine/collision/abstractPolyList.h
Executable file
241
engine/collision/abstractPolyList.h
Executable file
@ -0,0 +1,241 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _ABSTRACTPOLYLIST_H_
|
||||
#define _ABSTRACTPOLYLIST_H_
|
||||
|
||||
#ifndef _MMATH_H_
|
||||
#include "math/mMath.h"
|
||||
#endif
|
||||
#ifndef _MPLANETRANSFORMER_H_
|
||||
#include "math/mPlaneTransformer.h"
|
||||
#endif
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/tVector.h"
|
||||
#endif
|
||||
|
||||
class SceneObject;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
/// A polygon filtering interface.
|
||||
///
|
||||
/// The various AbstractPolyList subclasses are used in Torque as an interface to
|
||||
/// handle spatial queries. SceneObject::buildPolyList() takes an implementor of
|
||||
/// AbstractPolyList (such as ConcretePolyList, ClippedPolyList, etc.) and a
|
||||
/// bounding volume. The function runs geometry data from all the objects in the
|
||||
/// bounding volume through the passed PolyList.
|
||||
///
|
||||
/// This interface only provides a method to get data INTO your implementation. Different
|
||||
/// subclasses provide different interfaces to get data back out, depending on their
|
||||
/// specific quirks.
|
||||
///
|
||||
/// The physics engine now uses convex hulls for collision detection.
|
||||
///
|
||||
/// @see Convex
|
||||
class AbstractPolyList
|
||||
{
|
||||
protected:
|
||||
// User set state
|
||||
SceneObject* mCurrObject;
|
||||
|
||||
MatrixF mBaseMatrix; // Base transform
|
||||
MatrixF mTransformMatrix; // Current object transform
|
||||
MatrixF mMatrix; // Base * current transform
|
||||
Point3F mScale;
|
||||
|
||||
PlaneTransformer mPlaneTransformer;
|
||||
|
||||
bool mInterestNormalRegistered;
|
||||
Point3F mInterestNormal;
|
||||
|
||||
public:
|
||||
AbstractPolyList();
|
||||
virtual ~AbstractPolyList();
|
||||
|
||||
/// @name Common Interface
|
||||
/// @{
|
||||
void setBaseTransform(const MatrixF& mat);
|
||||
|
||||
/// Sets the transform applying to the current stream of
|
||||
/// vertices.
|
||||
///
|
||||
/// @param mat Transformation of the object. (in)
|
||||
/// @param scale Scaling of the object. (in)
|
||||
void setTransform(const MatrixF* mat, const Point3F& scale);
|
||||
|
||||
/// Gets the transform applying to the current stream of
|
||||
/// vertices.
|
||||
///
|
||||
/// @param mat Transformation of the object. (out)
|
||||
/// @param scale Scaling of the object. (out)
|
||||
void getTransform(MatrixF* mat, Point3F * scale);
|
||||
|
||||
/// This is called by the object which is currently feeding us
|
||||
/// vertices, to tell us who it is.
|
||||
void setObject(SceneObject*);
|
||||
|
||||
/// Add a box via the query interface (below). This wraps some calls
|
||||
/// to addPoint and addPlane.
|
||||
void addBox(const Box3F &box);
|
||||
|
||||
void doConstruct();
|
||||
/// @}
|
||||
|
||||
/// @name Query Interface
|
||||
///
|
||||
/// It is through this interface that geometry data is fed to the
|
||||
/// PolyList. The order of calls are:
|
||||
/// - begin()
|
||||
/// - One or more calls to vertex() and one call to plane(), in any order.
|
||||
/// - end()
|
||||
///
|
||||
/// @code
|
||||
/// // Example code that adds data to a PolyList.
|
||||
/// // See AbstractPolyList::addBox() for the function this was taken from.
|
||||
///
|
||||
/// // First, we add points... (note that we use base to track the start of adding.)
|
||||
/// U32 base = addPoint(pos);
|
||||
/// pos.y += dy; addPoint(pos);
|
||||
/// pos.x += dx; addPoint(pos);
|
||||
/// pos.y -= dy; addPoint(pos);
|
||||
/// pos.z += dz; addPoint(pos);
|
||||
/// pos.x -= dx; addPoint(pos);
|
||||
/// pos.y += dy; addPoint(pos);
|
||||
/// pos.x += dx; addPoint(pos);
|
||||
///
|
||||
/// // Now we add our surfaces. (there are six, as we are adding a cube here)
|
||||
/// for (S32 i = 0; i < 6; i++) {
|
||||
/// // Begin a surface
|
||||
/// begin(0,i);
|
||||
///
|
||||
/// // Calculate the vertex ids; we have a lookup table to tell us offset from base.
|
||||
/// // In your own code, you might use a different method.
|
||||
/// S32 v1 = base + PolyFace[i][0];
|
||||
/// S32 v2 = base + PolyFace[i][1];
|
||||
/// S32 v3 = base + PolyFace[i][2];
|
||||
/// S32 v4 = base + PolyFace[i][3];
|
||||
///
|
||||
/// // Reference the four vertices that make up this surface.
|
||||
/// vertex(v1);
|
||||
/// vertex(v2);
|
||||
/// vertex(v3);
|
||||
/// vertex(v4);
|
||||
///
|
||||
/// // Indicate the plane of this surface.
|
||||
/// plane(v1,v2,v3);
|
||||
///
|
||||
/// // End the surface.
|
||||
/// end();
|
||||
/// }
|
||||
/// @endcode
|
||||
/// @{
|
||||
|
||||
/// Are we empty of data?
|
||||
virtual bool isEmpty() const = 0;
|
||||
|
||||
/// Adds a point to the poly list, and returns
|
||||
/// an ID number for that point.
|
||||
virtual U32 addPoint(const Point3F& p) = 0;
|
||||
|
||||
/// Adds a plane to the poly list, and returns
|
||||
/// an ID number for that point.
|
||||
virtual U32 addPlane(const PlaneF& plane) = 0;
|
||||
|
||||
/// Start a surface.
|
||||
///
|
||||
/// @param material A material ID for this surface.
|
||||
/// @param surfaceKey A key value to associate with this surface.
|
||||
virtual void begin(U32 material,U32 surfaceKey) = 0;
|
||||
|
||||
/// Indicate the plane of the surface.
|
||||
virtual void plane(U32 v1,U32 v2,U32 v3) = 0;
|
||||
/// Indicate the plane of the surface.
|
||||
virtual void plane(const PlaneF& p) = 0;
|
||||
/// Indicate the plane of the surface.
|
||||
virtual void plane(const U32 index) = 0;
|
||||
|
||||
/// Reference a vertex which is in this surface.
|
||||
virtual void vertex(U32 vi) = 0;
|
||||
|
||||
/// Mark the end of a surface.
|
||||
virtual void end() = 0;
|
||||
|
||||
/// Return list transform and bounds in list space.
|
||||
///
|
||||
/// @returns False if no data is available.
|
||||
virtual bool getMapping(MatrixF *, Box3F *);
|
||||
/// @}
|
||||
|
||||
/// @name Interest
|
||||
///
|
||||
/// This is a mechanism to let you specify interest in a specific normal.
|
||||
/// If you set a normal you're interested in, then any planes facing "away"
|
||||
/// from that normal are culled from the results.
|
||||
///
|
||||
/// This is handy if you're using this to do a physics check, as you're not
|
||||
/// interested in polygons facing away from you (since you don't collide with
|
||||
/// the backsides/insides of things).
|
||||
/// @{
|
||||
|
||||
void setInterestNormal(const Point3F& normal);
|
||||
void clearInterestNormal() { mInterestNormalRegistered = false; }
|
||||
|
||||
|
||||
virtual bool isInterestedInPlane(const PlaneF& plane);
|
||||
virtual bool isInterestedInPlane(const U32 index);
|
||||
|
||||
/// @}
|
||||
|
||||
protected:
|
||||
/// A helper function to convert a plane index to a PlaneF structure.
|
||||
virtual const PlaneF& getIndexedPlane(const U32 index) = 0;
|
||||
};
|
||||
|
||||
inline AbstractPolyList::AbstractPolyList()
|
||||
{
|
||||
doConstruct();
|
||||
}
|
||||
|
||||
inline void AbstractPolyList::doConstruct()
|
||||
{
|
||||
mCurrObject = NULL;
|
||||
mBaseMatrix.identity();
|
||||
mMatrix.identity();
|
||||
mScale.set(1, 1, 1);
|
||||
|
||||
mPlaneTransformer.setIdentity();
|
||||
|
||||
mInterestNormalRegistered = false;
|
||||
}
|
||||
|
||||
inline void AbstractPolyList::setBaseTransform(const MatrixF& mat)
|
||||
{
|
||||
mBaseMatrix = mat;
|
||||
}
|
||||
|
||||
inline void AbstractPolyList::setTransform(const MatrixF* mat, const Point3F& scale)
|
||||
{
|
||||
mMatrix = mBaseMatrix;
|
||||
mTransformMatrix = *mat;
|
||||
mMatrix.mul(*mat);
|
||||
mScale = scale;
|
||||
|
||||
mPlaneTransformer.set(mMatrix, mScale);
|
||||
}
|
||||
|
||||
inline void AbstractPolyList::getTransform(MatrixF* mat, Point3F * scale)
|
||||
{
|
||||
*mat = mTransformMatrix;
|
||||
*scale = mScale;
|
||||
}
|
||||
|
||||
inline void AbstractPolyList::setObject(SceneObject* obj)
|
||||
{
|
||||
mCurrObject = obj;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
199
engine/collision/boxConvex.cc
Executable file
199
engine/collision/boxConvex.cc
Executable file
@ -0,0 +1,199 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "math/mMath.h"
|
||||
#include "game/gameBase.h"
|
||||
#include "collision/boxConvex.h"
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
struct Corner {
|
||||
S32 a,b,c;
|
||||
S32 ab,ac,bc;
|
||||
} sCorner[] =
|
||||
{
|
||||
{ 1,2,4, 4,0,1 },
|
||||
{ 0,3,5, 4,0,3 },
|
||||
{ 0,3,6, 4,1,2 },
|
||||
{ 1,2,7, 4,3,2 },
|
||||
{ 0,5,6, 0,1,5 },
|
||||
{ 1,4,7, 0,3,5 },
|
||||
{ 2,4,7, 1,2,5 },
|
||||
{ 3,5,6, 3,2,5 },
|
||||
};
|
||||
|
||||
struct Face {
|
||||
S32 vertex[4];
|
||||
S32 axis;
|
||||
bool flip;
|
||||
} sFace[] =
|
||||
{
|
||||
{ 0,4,5,1, 1,true },
|
||||
{ 0,2,6,4, 0,true },
|
||||
{ 3,7,6,2, 1,false },
|
||||
{ 3,1,5,7, 0,false },
|
||||
{ 0,1,3,2, 2,true },
|
||||
{ 4,6,7,5, 2,false },
|
||||
};
|
||||
|
||||
Point3F BoxConvex::support(const VectorF& v) const
|
||||
{
|
||||
Point3F p = mCenter;
|
||||
p.x += (v.x >= 0)? mSize.x: -mSize.x;
|
||||
p.y += (v.y >= 0)? mSize.y: -mSize.y;
|
||||
p.z += (v.z >= 0)? mSize.z: -mSize.z;
|
||||
return p;
|
||||
}
|
||||
|
||||
Point3F BoxConvex::getVertex(S32 v)
|
||||
{
|
||||
Point3F p = mCenter;
|
||||
p.x += (v & 1)? mSize.x: -mSize.x;
|
||||
p.y += (v & 2)? mSize.y: -mSize.y;
|
||||
p.z += (v & 4)? mSize.z: -mSize.z;
|
||||
return p;
|
||||
}
|
||||
|
||||
inline bool isOnPlane(Point3F p,PlaneF& plane)
|
||||
{
|
||||
F32 dist = mDot(plane,p) + plane.d;
|
||||
return dist < 0.1 && dist > -0.1;
|
||||
}
|
||||
|
||||
void BoxConvex::getFeatures(const MatrixF& mat,const VectorF& n, ConvexFeature* cf)
|
||||
{
|
||||
cf->material = 0;
|
||||
cf->object = mObject;
|
||||
|
||||
S32 v = 0;
|
||||
v += (n.x >= 0)? 1: 0;
|
||||
v += (n.y >= 0)? 2: 0;
|
||||
v += (n.z >= 0)? 4: 0;
|
||||
|
||||
PlaneF plane;
|
||||
plane.set(getVertex(v),n);
|
||||
|
||||
// Emit vertex and edge
|
||||
S32 mask = 0;
|
||||
Corner& corner = sCorner[v];
|
||||
mask |= isOnPlane(getVertex(corner.a),plane)? 1: 0;
|
||||
mask |= isOnPlane(getVertex(corner.b),plane)? 2: 0;
|
||||
mask |= isOnPlane(getVertex(corner.c),plane)? 4: 0;
|
||||
|
||||
switch(mask) {
|
||||
case 0: {
|
||||
cf->mVertexList.increment();
|
||||
mat.mulP(getVertex(v),&cf->mVertexList.last());
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
emitEdge(v,corner.a,mat,cf);
|
||||
break;
|
||||
case 2:
|
||||
emitEdge(v,corner.b,mat,cf);
|
||||
break;
|
||||
case 4:
|
||||
emitEdge(v,corner.c,mat,cf);
|
||||
break;
|
||||
case 1 | 2:
|
||||
emitFace(corner.ab,mat,cf);
|
||||
break;
|
||||
case 2 | 4:
|
||||
emitFace(corner.bc,mat,cf);
|
||||
break;
|
||||
case 1 | 4:
|
||||
emitFace(corner.ac,mat,cf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void BoxConvex::getPolyList(AbstractPolyList* list)
|
||||
{
|
||||
list->setTransform(&getTransform(), getScale());
|
||||
list->setObject(getObject());
|
||||
|
||||
U32 base = list->addPoint(mCenter + Point3F(-mSize.x, -mSize.y, -mSize.z));
|
||||
list->addPoint(mCenter + Point3F( mSize.x, -mSize.y, -mSize.z));
|
||||
list->addPoint(mCenter + Point3F(-mSize.x, mSize.y, -mSize.z));
|
||||
list->addPoint(mCenter + Point3F( mSize.x, mSize.y, -mSize.z));
|
||||
list->addPoint(mCenter + Point3F(-mSize.x, -mSize.y, mSize.z));
|
||||
list->addPoint(mCenter + Point3F( mSize.x, -mSize.y, mSize.z));
|
||||
list->addPoint(mCenter + Point3F(-mSize.x, mSize.y, mSize.z));
|
||||
list->addPoint(mCenter + Point3F( mSize.x, mSize.y, mSize.z));
|
||||
|
||||
for (U32 i = 0; i < 6; i++) {
|
||||
list->begin(0, i);
|
||||
|
||||
list->vertex(base + sFace[i].vertex[0]);
|
||||
list->vertex(base + sFace[i].vertex[1]);
|
||||
list->vertex(base + sFace[i].vertex[2]);
|
||||
list->vertex(base + sFace[i].vertex[3]);
|
||||
|
||||
list->plane(base + sFace[i].vertex[0],
|
||||
base + sFace[i].vertex[1],
|
||||
base + sFace[i].vertex[2]);
|
||||
list->end();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void BoxConvex::emitEdge(S32 v1,S32 v2,const MatrixF& mat,ConvexFeature* cf)
|
||||
{
|
||||
S32 vc = cf->mVertexList.size();
|
||||
cf->mVertexList.increment(2);
|
||||
Point3F *vp = cf->mVertexList.begin();
|
||||
mat.mulP(getVertex(v1),&vp[vc]);
|
||||
mat.mulP(getVertex(v2),&vp[vc + 1]);
|
||||
|
||||
cf->mEdgeList.increment();
|
||||
ConvexFeature::Edge& edge = cf->mEdgeList.last();
|
||||
edge.vertex[0] = vc;
|
||||
edge.vertex[1] = vc + 1;
|
||||
}
|
||||
|
||||
void BoxConvex::emitFace(S32 fi,const MatrixF& mat,ConvexFeature* cf)
|
||||
{
|
||||
Face& face = sFace[fi];
|
||||
|
||||
// Emit vertices
|
||||
S32 vc = cf->mVertexList.size();
|
||||
cf->mVertexList.increment(4);
|
||||
Point3F *vp = cf->mVertexList.begin();
|
||||
for (S32 v = 0; v < 4; v++)
|
||||
mat.mulP(getVertex(face.vertex[v]),&vp[vc + v]);
|
||||
|
||||
// Emit edges
|
||||
cf->mEdgeList.increment(4);
|
||||
ConvexFeature::Edge* edge = cf->mEdgeList.end() - 4;
|
||||
for (S32 e = 0; e < 4; e++) {
|
||||
edge[e].vertex[0] = vc + e;
|
||||
edge[e].vertex[1] = vc + ((e + 1) & 3);
|
||||
}
|
||||
|
||||
// Emit 2 triangle faces
|
||||
cf->mFaceList.increment(2);
|
||||
ConvexFeature::Face* ef = cf->mFaceList.end() - 2;
|
||||
mat.getColumn(face.axis,&ef->normal);
|
||||
if (face.flip)
|
||||
ef[0].normal.neg();
|
||||
ef[1].normal = ef[0].normal;
|
||||
ef[1].vertex[0] = ef[0].vertex[0] = vc;
|
||||
ef[1].vertex[1] = ef[0].vertex[2] = vc + 2;
|
||||
ef[0].vertex[1] = vc + 1;
|
||||
ef[1].vertex[2] = vc + 3;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const MatrixF& OrthoBoxConvex::getTransform() const
|
||||
{
|
||||
Point3F translation;
|
||||
Parent::getTransform().getColumn(3, &translation);
|
||||
mOrthoMatrixCache.setColumn(3, translation);
|
||||
return mOrthoMatrixCache;
|
||||
}
|
||||
|
46
engine/collision/boxConvex.h
Executable file
46
engine/collision/boxConvex.h
Executable file
@ -0,0 +1,46 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _BOXCONVEX_H_
|
||||
#define _BOXCONVEX_H_
|
||||
|
||||
#ifndef _CONVEX_H_
|
||||
#include "collision/convex.h"
|
||||
#endif
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
class BoxConvex: public Convex
|
||||
{
|
||||
Point3F getVertex(S32 v);
|
||||
void emitEdge(S32 v1,S32 v2,const MatrixF& mat,ConvexFeature* cf);
|
||||
void emitFace(S32 fi,const MatrixF& mat,ConvexFeature* cf);
|
||||
public:
|
||||
//
|
||||
Point3F mCenter;
|
||||
VectorF mSize;
|
||||
|
||||
BoxConvex() { mType = BoxConvexType; }
|
||||
void init(SceneObject* obj) { mObject = obj; }
|
||||
|
||||
Point3F support(const VectorF& v) const;
|
||||
void getFeatures(const MatrixF& mat,const VectorF& n, ConvexFeature* cf);
|
||||
void getPolyList(AbstractPolyList* list);
|
||||
};
|
||||
|
||||
|
||||
class OrthoBoxConvex: public BoxConvex
|
||||
{
|
||||
typedef BoxConvex Parent;
|
||||
mutable MatrixF mOrthoMatrixCache;
|
||||
|
||||
public:
|
||||
OrthoBoxConvex() { mOrthoMatrixCache.identity(); }
|
||||
|
||||
virtual const MatrixF& getTransform() const;
|
||||
};
|
||||
|
||||
#endif
|
268
engine/collision/clippedPolyList.cc
Executable file
268
engine/collision/clippedPolyList.cc
Executable file
@ -0,0 +1,268 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "dgl/dgl.h"
|
||||
#include "math/mMath.h"
|
||||
#include "console/console.h"
|
||||
#include "collision/clippedPolyList.h"
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
ClippedPolyList::ClippedPolyList()
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION(mPolyList);
|
||||
VECTOR_SET_ASSOCIATION(mVertexList);
|
||||
VECTOR_SET_ASSOCIATION(mIndexList);
|
||||
VECTOR_SET_ASSOCIATION(mPolyPlaneList);
|
||||
VECTOR_SET_ASSOCIATION(mPlaneList);
|
||||
|
||||
mNormal.set(0,0,0);
|
||||
mIndexList.reserve(100);
|
||||
}
|
||||
|
||||
ClippedPolyList::~ClippedPolyList()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void ClippedPolyList::clear()
|
||||
{
|
||||
// Only clears internal data
|
||||
mPolyList.clear();
|
||||
mVertexList.clear();
|
||||
mIndexList.clear();
|
||||
mPolyPlaneList.clear();
|
||||
}
|
||||
|
||||
bool ClippedPolyList::isEmpty() const
|
||||
{
|
||||
return mPolyList.size() == 0;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
U32 ClippedPolyList::addPoint(const Point3F& p)
|
||||
{
|
||||
mVertexList.increment();
|
||||
Vertex& v = mVertexList.last();
|
||||
v.point.x = p.x * mScale.x;
|
||||
v.point.y = p.y * mScale.y;
|
||||
v.point.z = p.z * mScale.z;
|
||||
mMatrix.mulP(v.point);
|
||||
|
||||
// Build the plane mask
|
||||
register U32 mask = 1;
|
||||
register S32 count = mPlaneList.size();
|
||||
register PlaneF * plane = mPlaneList.address();
|
||||
|
||||
v.mask = 0;
|
||||
while(--count >= 0) {
|
||||
if (plane++->distToPlane(v.point) > 0)
|
||||
v.mask |= mask;
|
||||
mask <<= 1;
|
||||
}
|
||||
|
||||
return mVertexList.size() - 1;
|
||||
}
|
||||
|
||||
|
||||
U32 ClippedPolyList::addPlane(const PlaneF& plane)
|
||||
{
|
||||
mPolyPlaneList.increment();
|
||||
mPlaneTransformer.transform(plane, mPolyPlaneList.last());
|
||||
|
||||
return mPolyPlaneList.size() - 1;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void ClippedPolyList::begin(U32 material,U32 surfaceKey)
|
||||
{
|
||||
mPolyList.increment();
|
||||
Poly& poly = mPolyList.last();
|
||||
poly.object = mCurrObject;
|
||||
poly.material = material;
|
||||
poly.vertexStart = mIndexList.size();
|
||||
poly.surfaceKey = surfaceKey;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void ClippedPolyList::plane(U32 v1,U32 v2,U32 v3)
|
||||
{
|
||||
mPolyList.last().plane.set(mVertexList[v1].point,
|
||||
mVertexList[v2].point,mVertexList[v3].point);
|
||||
}
|
||||
|
||||
void ClippedPolyList::plane(const PlaneF& p)
|
||||
{
|
||||
mPlaneTransformer.transform(p, mPolyList.last().plane);
|
||||
}
|
||||
|
||||
void ClippedPolyList::plane(const U32 index)
|
||||
{
|
||||
AssertFatal(index < mPolyPlaneList.size(), "Out of bounds index!");
|
||||
mPolyList.last().plane = mPolyPlaneList[index];
|
||||
}
|
||||
|
||||
const PlaneF& ClippedPolyList::getIndexedPlane(const U32 index)
|
||||
{
|
||||
AssertFatal(index < mPolyPlaneList.size(), "Out of bounds index!");
|
||||
return mPolyPlaneList[index];
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void ClippedPolyList::vertex(U32 vi)
|
||||
{
|
||||
mIndexList.push_back(vi);
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void ClippedPolyList::end()
|
||||
{
|
||||
Poly& poly = mPolyList.last();
|
||||
|
||||
// Anything facing away from the mNormal is rejected
|
||||
if (mDot(poly.plane,mNormal) > 0) {
|
||||
mIndexList.setSize(poly.vertexStart);
|
||||
mPolyList.decrement();
|
||||
return;
|
||||
}
|
||||
|
||||
// Build initial inside/outside plane masks
|
||||
U32 indexStart = poly.vertexStart;
|
||||
U32 vertexCount = mIndexList.size() - indexStart;
|
||||
|
||||
U32 frontMask = 0,backMask = 0;
|
||||
U32 i;
|
||||
for (i = indexStart; i < mIndexList.size(); i++) {
|
||||
U32 mask = mVertexList[mIndexList[i]].mask;
|
||||
frontMask |= mask;
|
||||
backMask |= ~mask;
|
||||
}
|
||||
|
||||
// Trivial accept if all the vertices are on the backsides of
|
||||
// all the planes.
|
||||
if (!frontMask) {
|
||||
poly.vertexCount = vertexCount;
|
||||
return;
|
||||
}
|
||||
|
||||
// Trivial reject if any plane not crossed has all it's points
|
||||
// on the front.
|
||||
U32 crossMask = frontMask & backMask;
|
||||
if (~crossMask & frontMask) {
|
||||
mIndexList.setSize(poly.vertexStart);
|
||||
mPolyList.decrement();
|
||||
return;
|
||||
}
|
||||
|
||||
// Need to do some clipping
|
||||
for (U32 p = 0; p < mPlaneList.size(); p++) {
|
||||
U32 pmask = 1 << p;
|
||||
// Only test against this plane if we have something
|
||||
// on both sides
|
||||
if (crossMask & pmask) {
|
||||
U32 indexEnd = mIndexList.size();
|
||||
U32 i1 = indexEnd - 1;
|
||||
U32 mask1 = mVertexList[mIndexList[i1]].mask;
|
||||
|
||||
for (U32 i2 = indexStart; i2 < indexEnd; i2++) {
|
||||
U32 mask2 = mVertexList[mIndexList[i2]].mask;
|
||||
if ((mask1 ^ mask2) & pmask) {
|
||||
//
|
||||
mVertexList.increment();
|
||||
VectorF& v1 = mVertexList[mIndexList[i1]].point;
|
||||
VectorF& v2 = mVertexList[mIndexList[i2]].point;
|
||||
VectorF vv = v2 - v1;
|
||||
F32 t = -mPlaneList[p].distToPlane(v1) / mDot(mPlaneList[p],vv);
|
||||
|
||||
mIndexList.push_back(mVertexList.size() - 1);
|
||||
Vertex& iv = mVertexList.last();
|
||||
iv.point.x = v1.x + vv.x * t;
|
||||
iv.point.y = v1.y + vv.y * t;
|
||||
iv.point.z = v1.z + vv.z * t;
|
||||
iv.mask = 0;
|
||||
|
||||
// Test against the remaining planes
|
||||
for (U32 i = p + 1; i < mPlaneList.size(); i++)
|
||||
if (mPlaneList[i].distToPlane(iv.point) > 0) {
|
||||
iv.mask = 1 << i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!(mask2 & pmask)) {
|
||||
U32 index = mIndexList[i2];
|
||||
mIndexList.push_back(index);
|
||||
}
|
||||
mask1 = mask2;
|
||||
i1 = i2;
|
||||
}
|
||||
|
||||
// Check for degenerate
|
||||
indexStart = indexEnd;
|
||||
if (mIndexList.size() - indexStart < 3) {
|
||||
mIndexList.setSize(poly.vertexStart);
|
||||
mPolyList.decrement();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Emit what's left and compress the index list.
|
||||
poly.vertexCount = mIndexList.size() - indexStart;
|
||||
memcpy(&mIndexList[poly.vertexStart],
|
||||
&mIndexList[indexStart],poly.vertexCount);
|
||||
mIndexList.setSize(poly.vertexStart + poly.vertexCount);
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void ClippedPolyList::memcpy(U32* dst, U32* src,U32 size)
|
||||
{
|
||||
U32* end = src + size;
|
||||
while (src != end)
|
||||
*dst++ = *src++;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void ClippedPolyList::render()
|
||||
{
|
||||
glVertexPointer(3,GL_FLOAT,sizeof(Vertex),mVertexList.address());
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glColor4f(1,0,0,0.25);
|
||||
glEnable(GL_BLEND);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
Poly * p;
|
||||
for (p = mPolyList.begin(); p < mPolyList.end(); p++) {
|
||||
glDrawElements(GL_POLYGON,p->vertexCount,
|
||||
GL_UNSIGNED_INT,&mIndexList[p->vertexStart]);
|
||||
}
|
||||
|
||||
glColor3f(0.6,0.6,0.6);
|
||||
glDisable(GL_BLEND);
|
||||
for (p = mPolyList.begin(); p < mPolyList.end(); p++) {
|
||||
glDrawElements(GL_LINE_LOOP,p->vertexCount,
|
||||
GL_UNSIGNED_INT,&mIndexList[p->vertexStart]);
|
||||
}
|
||||
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
}
|
84
engine/collision/clippedPolyList.h
Executable file
84
engine/collision/clippedPolyList.h
Executable file
@ -0,0 +1,84 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _CLIPPEDPOLYLIST_H_
|
||||
#define _CLIPPEDPOLYLIST_H_
|
||||
|
||||
#ifndef _MMATH_H_
|
||||
#include "math/mMath.h"
|
||||
#endif
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/tVector.h"
|
||||
#endif
|
||||
#ifndef _ABSTRACTPOLYLIST_H_
|
||||
#include "collision/abstractPolyList.h"
|
||||
#endif
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// Clipped PolyList
|
||||
///
|
||||
/// This takes the geometry passed to it and clips against the PlaneList set
|
||||
/// by the caller.
|
||||
//
|
||||
/// @see AbstractPolyList
|
||||
class ClippedPolyList: public AbstractPolyList
|
||||
{
|
||||
void memcpy(U32* d, U32* s,U32 size);
|
||||
|
||||
public:
|
||||
struct Vertex {
|
||||
Point3F point;
|
||||
U32 mask;
|
||||
};
|
||||
|
||||
struct Poly {
|
||||
PlaneF plane;
|
||||
SceneObject* object;
|
||||
U32 material;
|
||||
U32 vertexStart;
|
||||
U32 vertexCount;
|
||||
U32 surfaceKey;
|
||||
};
|
||||
|
||||
typedef Vector<PlaneF> PlaneList;
|
||||
typedef Vector<Vertex> VertexList;
|
||||
typedef Vector<Poly> PolyList;
|
||||
typedef Vector<U32> IndexList;
|
||||
|
||||
// Internal data
|
||||
PolyList mPolyList;
|
||||
VertexList mVertexList;
|
||||
IndexList mIndexList;
|
||||
|
||||
PlaneList mPolyPlaneList;
|
||||
|
||||
// Data set by caller
|
||||
PlaneList mPlaneList;
|
||||
VectorF mNormal;
|
||||
|
||||
//
|
||||
ClippedPolyList();
|
||||
~ClippedPolyList();
|
||||
void clear();
|
||||
void render();
|
||||
|
||||
// Virtual methods
|
||||
bool isEmpty() const;
|
||||
U32 addPoint(const Point3F& p);
|
||||
U32 addPlane(const PlaneF& plane);
|
||||
void begin(U32 material,U32 surfaceKey);
|
||||
void plane(U32 v1,U32 v2,U32 v3);
|
||||
void plane(const PlaneF& p);
|
||||
void plane(const U32 index);
|
||||
void vertex(U32 vi);
|
||||
void end();
|
||||
|
||||
protected:
|
||||
const PlaneF& getIndexedPlane(const U32 index);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
65
engine/collision/collision.h
Executable file
65
engine/collision/collision.h
Executable file
@ -0,0 +1,65 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _COLLISION_H_
|
||||
#define _COLLISION_H_
|
||||
|
||||
#ifndef _DATACHUNKER_H_
|
||||
#include "core/dataChunker.h"
|
||||
#endif
|
||||
#ifndef _MPLANE_H_
|
||||
#include "math/mPlane.h"
|
||||
#endif
|
||||
|
||||
class SceneObject;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
struct Collision
|
||||
{
|
||||
SceneObject* object;
|
||||
Point3F point;
|
||||
VectorF normal;
|
||||
U32 material;
|
||||
|
||||
// Face and Face dot are currently only set by the extrudedPolyList
|
||||
// clipper. Values are otherwise undefined.
|
||||
U32 face; // Which face was hit
|
||||
F32 faceDot; // -Dot of face with poly normal
|
||||
F32 distance;
|
||||
};
|
||||
|
||||
struct CollisionList
|
||||
{
|
||||
enum {
|
||||
MaxCollisions = 64
|
||||
};
|
||||
int count;
|
||||
Collision collision[MaxCollisions];
|
||||
F32 t;
|
||||
// MaxHeight is currently only set by the extrudedPolyList
|
||||
// clipper. It represents the maximum vertex z value of
|
||||
// the returned collision surfaces.
|
||||
F32 maxHeight;
|
||||
};
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// BSP Collision tree
|
||||
// Solid nodes are represented by structures with NULL frontNode and
|
||||
// backNode pointers. The material field is only valid on a solid node.
|
||||
// There is no structure for empty nodes, frontNode or backNode
|
||||
// should be set to NULL to represent empty half-spaces.
|
||||
|
||||
struct BSPNode
|
||||
{
|
||||
U32 material;
|
||||
PlaneF plane;
|
||||
BSPNode *frontNode, *backNode;
|
||||
};
|
||||
|
||||
typedef Chunker<BSPNode> BSPTree;
|
||||
|
||||
#endif
|
149
engine/collision/concretePolyList.cc
Executable file
149
engine/collision/concretePolyList.cc
Executable file
@ -0,0 +1,149 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "dgl/dgl.h"
|
||||
#include "math/mMath.h"
|
||||
#include "console/console.h"
|
||||
#include "collision/concretePolyList.h"
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
ConcretePolyList::ConcretePolyList()
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION(mPolyList);
|
||||
VECTOR_SET_ASSOCIATION(mVertexList);
|
||||
VECTOR_SET_ASSOCIATION(mIndexList);
|
||||
VECTOR_SET_ASSOCIATION(mPolyPlaneList);
|
||||
|
||||
mIndexList.reserve(100);
|
||||
}
|
||||
|
||||
ConcretePolyList::~ConcretePolyList()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void ConcretePolyList::clear()
|
||||
{
|
||||
// Only clears internal data
|
||||
mPolyList.clear();
|
||||
mVertexList.clear();
|
||||
mIndexList.clear();
|
||||
mPolyPlaneList.clear();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
U32 ConcretePolyList::addPoint(const Point3F& p)
|
||||
{
|
||||
mVertexList.increment();
|
||||
Point3F& v = mVertexList.last();
|
||||
v.x = p.x * mScale.x;
|
||||
v.y = p.y * mScale.y;
|
||||
v.z = p.z * mScale.z;
|
||||
mMatrix.mulP(v);
|
||||
|
||||
return mVertexList.size() - 1;
|
||||
}
|
||||
|
||||
|
||||
U32 ConcretePolyList::addPlane(const PlaneF& plane)
|
||||
{
|
||||
mPolyPlaneList.increment();
|
||||
mPlaneTransformer.transform(plane, mPolyPlaneList.last());
|
||||
|
||||
return mPolyPlaneList.size() - 1;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void ConcretePolyList::begin(U32 material,U32 surfaceKey)
|
||||
{
|
||||
mPolyList.increment();
|
||||
Poly& poly = mPolyList.last();
|
||||
poly.object = mCurrObject;
|
||||
poly.material = material;
|
||||
poly.vertexStart = mIndexList.size();
|
||||
poly.surfaceKey = surfaceKey;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void ConcretePolyList::plane(U32 v1,U32 v2,U32 v3)
|
||||
{
|
||||
mPolyList.last().plane.set(mVertexList[v1],
|
||||
mVertexList[v2],mVertexList[v3]);
|
||||
}
|
||||
|
||||
void ConcretePolyList::plane(const PlaneF& p)
|
||||
{
|
||||
mPlaneTransformer.transform(p, mPolyList.last().plane);
|
||||
}
|
||||
|
||||
void ConcretePolyList::plane(const U32 index)
|
||||
{
|
||||
AssertFatal(index < mPolyPlaneList.size(), "Out of bounds index!");
|
||||
mPolyList.last().plane = mPolyPlaneList[index];
|
||||
}
|
||||
|
||||
const PlaneF& ConcretePolyList::getIndexedPlane(const U32 index)
|
||||
{
|
||||
AssertFatal(index < mPolyPlaneList.size(), "Out of bounds index!");
|
||||
return mPolyPlaneList[index];
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void ConcretePolyList::vertex(U32 vi)
|
||||
{
|
||||
mIndexList.push_back(vi);
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
bool ConcretePolyList::isEmpty() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void ConcretePolyList::end()
|
||||
{
|
||||
Poly& poly = mPolyList.last();
|
||||
poly.vertexCount = mIndexList.size() - poly.vertexStart;
|
||||
}
|
||||
|
||||
|
||||
void ConcretePolyList::render()
|
||||
{
|
||||
glVertexPointer(3,GL_FLOAT,sizeof(Point3F),mVertexList.address());
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glColor4f(1,0,0,0.25);
|
||||
glEnable(GL_BLEND);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
Poly * p;
|
||||
for (p = mPolyList.begin(); p < mPolyList.end(); p++) {
|
||||
if(p->material != 0xFFFFFFFF)
|
||||
glDrawElements(GL_POLYGON,p->vertexCount,
|
||||
GL_UNSIGNED_INT,&mIndexList[p->vertexStart]);
|
||||
}
|
||||
|
||||
glColor3f(0.6,0.6,0.6);
|
||||
glDisable(GL_BLEND);
|
||||
for (p = mPolyList.begin(); p < mPolyList.end(); p++) {
|
||||
glDrawElements(GL_LINE_LOOP,p->vertexCount,
|
||||
GL_UNSIGNED_INT,&mIndexList[p->vertexStart]);
|
||||
}
|
||||
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
}
|
66
engine/collision/concretePolyList.h
Executable file
66
engine/collision/concretePolyList.h
Executable file
@ -0,0 +1,66 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _CONCRETEPOLYLIST_H_
|
||||
#define _CONCRETEPOLYLIST_H_
|
||||
|
||||
#ifndef _ABSTRACTPOLYLIST_H_
|
||||
#include "collision/abstractPolyList.h"
|
||||
#endif
|
||||
|
||||
/// A concrete, renderable PolyList
|
||||
///
|
||||
/// This class is used to store geometry from a PolyList query.
|
||||
///
|
||||
/// It allows you to render this data, as well.
|
||||
///
|
||||
/// @see AbstractPolyList
|
||||
class ConcretePolyList : public AbstractPolyList
|
||||
{
|
||||
public:
|
||||
|
||||
struct Poly {
|
||||
PlaneF plane;
|
||||
SceneObject* object;
|
||||
U32 material;
|
||||
U32 vertexStart;
|
||||
U32 vertexCount;
|
||||
U32 surfaceKey;
|
||||
};
|
||||
|
||||
typedef Vector<PlaneF> PlaneList;
|
||||
typedef Vector<Point3F> VertexList;
|
||||
typedef Vector<Poly> PolyList;
|
||||
typedef Vector<U32> IndexList;
|
||||
|
||||
PolyList mPolyList;
|
||||
VertexList mVertexList;
|
||||
IndexList mIndexList;
|
||||
|
||||
PlaneList mPolyPlaneList;
|
||||
|
||||
public:
|
||||
ConcretePolyList();
|
||||
~ConcretePolyList();
|
||||
void clear();
|
||||
|
||||
// Virtual methods
|
||||
U32 addPoint(const Point3F& p);
|
||||
U32 addPlane(const PlaneF& plane);
|
||||
void begin(U32 material,U32 surfaceKey);
|
||||
void plane(U32 v1,U32 v2,U32 v3);
|
||||
void plane(const PlaneF& p);
|
||||
void plane(const U32 index);
|
||||
void vertex(U32 vi);
|
||||
void end();
|
||||
|
||||
void render();
|
||||
bool isEmpty() const;
|
||||
|
||||
protected:
|
||||
const PlaneF& getIndexedPlane(const U32 index);
|
||||
};
|
||||
|
||||
#endif // _H_EARLYOUTPOLYLIST_
|
641
engine/collision/convex.cc
Executable file
641
engine/collision/convex.cc
Executable file
@ -0,0 +1,641 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "dgl/dgl.h"
|
||||
#include "core/dataChunker.h"
|
||||
#include "collision/collision.h"
|
||||
#include "sceneGraph/sceneGraph.h"
|
||||
#include "sim/sceneObject.h"
|
||||
#include "terrain/terrData.h"
|
||||
#include "collision/convex.h"
|
||||
#include "collision/gjk.h"
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
static DataChunker sChunker;
|
||||
|
||||
CollisionStateList CollisionStateList::sFreeList;
|
||||
CollisionWorkingList CollisionWorkingList::sFreeList;
|
||||
F32 sqrDistanceEdges(const Point3F& start0,
|
||||
const Point3F& end0,
|
||||
const Point3F& start1,
|
||||
const Point3F& end1,
|
||||
Point3F* is,
|
||||
Point3F* it);
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Collision State
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
CollisionState::CollisionState()
|
||||
{
|
||||
mLista = mListb = 0;
|
||||
}
|
||||
|
||||
CollisionState::~CollisionState()
|
||||
{
|
||||
if (mLista)
|
||||
mLista->free();
|
||||
if (mListb)
|
||||
mListb->free();
|
||||
}
|
||||
|
||||
void CollisionState::swap()
|
||||
{
|
||||
}
|
||||
|
||||
void CollisionState::set(Convex* a,Convex* b,const MatrixF& a2w, const MatrixF& b2w)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
F32 CollisionState::distance(const MatrixF& a2w, const MatrixF& b2w, const F32 dontCareDist,
|
||||
const MatrixF* w2a, const MatrixF* _w2b)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CollisionState::render()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Feature Collision
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
bool ConvexFeature::collide(ConvexFeature& cf,CollisionList* cList, F32 tol)
|
||||
{
|
||||
// Our vertices vs. other faces
|
||||
const Point3F* vert = mVertexList.begin();
|
||||
const Point3F* vend = mVertexList.end();
|
||||
while (vert != vend) {
|
||||
cf.testVertex(*vert,cList,false, tol);
|
||||
vert++;
|
||||
}
|
||||
|
||||
// Other vertices vs. our faces
|
||||
vert = cf.mVertexList.begin();
|
||||
vend = cf.mVertexList.end();
|
||||
while (vert != vend) {
|
||||
U32 storeCount = cList->count;
|
||||
testVertex(*vert,cList,true, tol);
|
||||
|
||||
// Fix up last reference. material and object are copied from this rather
|
||||
// than the object we're colliding against.
|
||||
if (storeCount != cList->count) {
|
||||
cList->collision[cList->count - 1].material = cf.material;
|
||||
cList->collision[cList->count - 1].object = cf.object;
|
||||
}
|
||||
vert++;
|
||||
}
|
||||
|
||||
// Edge vs. Edge
|
||||
const Edge* edge = mEdgeList.begin();
|
||||
const Edge* eend = mEdgeList.end();
|
||||
while (edge != eend) {
|
||||
cf.testEdge(this,mVertexList[edge->vertex[0]],
|
||||
mVertexList[edge->vertex[1]],cList, tol);
|
||||
edge++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool isInside(const Point3F& p, const Point3F& a, const Point3F& b, const VectorF& n)
|
||||
{
|
||||
VectorF v;
|
||||
mCross(n,b - a,&v);
|
||||
return mDot(v,p - a) < 0.0f;
|
||||
}
|
||||
|
||||
void ConvexFeature::testVertex(const Point3F& v,CollisionList* cList,bool flip, F32 tol)
|
||||
{
|
||||
// Test vertex against all faces
|
||||
const Face* face = mFaceList.begin();
|
||||
const Face* end = mFaceList.end();
|
||||
for (; face != end; face++) {
|
||||
if (cList->count >= CollisionList::MaxCollisions)
|
||||
return;
|
||||
|
||||
const Point3F& p0 = mVertexList[face->vertex[0]];
|
||||
const Point3F& p1 = mVertexList[face->vertex[1]];
|
||||
const Point3F& p2 = mVertexList[face->vertex[2]];
|
||||
|
||||
// Point near the plane?
|
||||
F32 distance = mDot(face->normal,v - p0);
|
||||
if (distance > tol || distance < -tol)
|
||||
continue;
|
||||
|
||||
// Make sure it's within the bounding edges
|
||||
if (isInside(v,p0,p1,face->normal) && isInside(v,p1,p2,face->normal) &&
|
||||
isInside(v,p2,p0,face->normal)) {
|
||||
|
||||
// Add collision to this face
|
||||
Collision& info = cList->collision[cList->count++];
|
||||
info.point = v;
|
||||
info.normal = face->normal;
|
||||
if (flip)
|
||||
info.normal.neg();
|
||||
info.material = material;
|
||||
info.object = object;
|
||||
info.distance = distance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConvexFeature::testEdge(ConvexFeature* cf,const Point3F& s1, const Point3F& e1, CollisionList* cList, F32 tol)
|
||||
{
|
||||
F32 tolSquared = tol*tol;
|
||||
|
||||
// Test edges against edges
|
||||
const Edge* edge = mEdgeList.begin();
|
||||
const Edge* end = mEdgeList.end();
|
||||
for (; edge != end; edge++) {
|
||||
if (cList->count >= CollisionList::MaxCollisions)
|
||||
return;
|
||||
|
||||
const Point3F& s2 = mVertexList[edge->vertex[0]];
|
||||
const Point3F& e2 = mVertexList[edge->vertex[1]];
|
||||
|
||||
// Get the distance and closest points
|
||||
Point3F i1,i2;
|
||||
F32 distance = sqrDistanceEdges(s1, e1, s2, e2, &i1, &i2);
|
||||
if (distance > tolSquared)
|
||||
continue;
|
||||
distance = mSqrt(distance);
|
||||
|
||||
// Need to figure out how to orient the collision normal.
|
||||
// The current test involves checking to see whether the collision
|
||||
// points are contained within the convex volumes, which is slow.
|
||||
if (inVolume(i1) || cf->inVolume(i2))
|
||||
distance = -distance;
|
||||
|
||||
// Contact normal
|
||||
VectorF normal = i1 - i2;
|
||||
normal *= 1 / distance;
|
||||
|
||||
// Return a collision
|
||||
Collision& info = cList->collision[cList->count++];
|
||||
info.point = i1;
|
||||
info.normal = normal;
|
||||
info.distance = distance;
|
||||
info.material = material;
|
||||
info.object = object;
|
||||
}
|
||||
}
|
||||
|
||||
bool ConvexFeature::inVolume(const Point3F& v)
|
||||
{
|
||||
// Test the point to see if it's inside the volume
|
||||
const Face* face = mFaceList.begin();
|
||||
const Face* end = mFaceList.end();
|
||||
for (; face != end; face++) {
|
||||
const Point3F& p0 = mVertexList[face->vertex[0]];
|
||||
if (mDot(face->normal,v - p0) > 0)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Collision State management
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
CollisionStateList::CollisionStateList()
|
||||
{
|
||||
mPrev = mNext = this;
|
||||
mState = NULL;
|
||||
}
|
||||
|
||||
void CollisionStateList::linkAfter(CollisionStateList* ptr)
|
||||
{
|
||||
mPrev = ptr;
|
||||
mNext = ptr->mNext;
|
||||
ptr->mNext = this;
|
||||
mNext->mPrev = this;
|
||||
}
|
||||
|
||||
void CollisionStateList::unlink()
|
||||
{
|
||||
mPrev->mNext = mNext;
|
||||
mNext->mPrev = mPrev;
|
||||
mPrev = mNext = this;
|
||||
}
|
||||
|
||||
CollisionStateList* CollisionStateList::alloc()
|
||||
{
|
||||
if (!sFreeList.isEmpty()) {
|
||||
CollisionStateList* nxt = sFreeList.mNext;
|
||||
nxt->unlink();
|
||||
nxt->mState = NULL;
|
||||
return nxt;
|
||||
}
|
||||
return constructInPlace((CollisionStateList*)sChunker.alloc(sizeof(CollisionStateList)));
|
||||
}
|
||||
|
||||
void CollisionStateList::free()
|
||||
{
|
||||
unlink();
|
||||
linkAfter(&sFreeList);
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
CollisionWorkingList::CollisionWorkingList()
|
||||
{
|
||||
wLink.mPrev = wLink.mNext = this;
|
||||
rLink.mPrev = rLink.mNext = this;
|
||||
}
|
||||
|
||||
void CollisionWorkingList::wLinkAfter(CollisionWorkingList* ptr)
|
||||
{
|
||||
wLink.mPrev = ptr;
|
||||
wLink.mNext = ptr->wLink.mNext;
|
||||
ptr->wLink.mNext = this;
|
||||
wLink.mNext->wLink.mPrev = this;
|
||||
}
|
||||
|
||||
void CollisionWorkingList::rLinkAfter(CollisionWorkingList* ptr)
|
||||
{
|
||||
rLink.mPrev = ptr;
|
||||
rLink.mNext = ptr->rLink.mNext;
|
||||
ptr->rLink.mNext = this;
|
||||
rLink.mNext->rLink.mPrev = this;
|
||||
}
|
||||
|
||||
void CollisionWorkingList::unlink()
|
||||
{
|
||||
wLink.mPrev->wLink.mNext = wLink.mNext;
|
||||
wLink.mNext->wLink.mPrev = wLink.mPrev;
|
||||
wLink.mPrev = wLink.mNext = this;
|
||||
|
||||
rLink.mPrev->rLink.mNext = rLink.mNext;
|
||||
rLink.mNext->rLink.mPrev = rLink.mPrev;
|
||||
rLink.mPrev = rLink.mNext = this;
|
||||
}
|
||||
|
||||
CollisionWorkingList* CollisionWorkingList::alloc()
|
||||
{
|
||||
if (sFreeList.wLink.mNext != &sFreeList) {
|
||||
CollisionWorkingList* nxt = sFreeList.wLink.mNext;
|
||||
nxt->unlink();
|
||||
return nxt;
|
||||
}
|
||||
return constructInPlace((CollisionWorkingList*)sChunker.alloc(sizeof(CollisionWorkingList)));
|
||||
}
|
||||
|
||||
void CollisionWorkingList::free()
|
||||
{
|
||||
unlink();
|
||||
wLinkAfter(&sFreeList);
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Convex Base Class
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
U32 Convex::sTag = (U32)-1;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
Convex::Convex()
|
||||
{
|
||||
mNext = mPrev = this;
|
||||
mTag = 0;
|
||||
}
|
||||
|
||||
Convex::~Convex()
|
||||
{
|
||||
// Unlink from Convex Database
|
||||
unlink();
|
||||
|
||||
// Delete collision states
|
||||
while (mList.mNext != &mList)
|
||||
delete mList.mNext->mState;
|
||||
|
||||
// Free up working list
|
||||
while (mWorking.wLink.mNext != &mWorking)
|
||||
mWorking.wLink.mNext->free();
|
||||
|
||||
// Free up references
|
||||
while (mReference.rLink.mNext != &mReference)
|
||||
mReference.rLink.mNext->free();
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void Convex::collectGarbage()
|
||||
{
|
||||
// Delete unreferenced Convex Objects
|
||||
for (Convex* itr = mNext; itr != this; itr = itr->mNext) {
|
||||
if (itr->mReference.rLink.mNext == &itr->mReference) {
|
||||
Convex* ptr = itr;
|
||||
itr = itr->mPrev;
|
||||
delete ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Convex::nukeList()
|
||||
{
|
||||
// Delete all Convex Objects
|
||||
for (Convex* itr = mNext; itr != this; itr = itr->mNext) {
|
||||
Convex* ptr = itr;
|
||||
itr = itr->mPrev;
|
||||
delete ptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Convex::registerObject(Convex *convex)
|
||||
{
|
||||
convex->linkAfter(this);
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void Convex::linkAfter(Convex* ptr)
|
||||
{
|
||||
mPrev = ptr;
|
||||
mNext = ptr->mNext;
|
||||
ptr->mNext = this;
|
||||
mNext->mPrev = this;
|
||||
}
|
||||
|
||||
void Convex::unlink()
|
||||
{
|
||||
mPrev->mNext = mNext;
|
||||
mNext->mPrev = mPrev;
|
||||
mPrev = mNext = this;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
Point3F Convex::support(const VectorF&) const
|
||||
{
|
||||
return Point3F(0,0,0);
|
||||
}
|
||||
|
||||
void Convex::getFeatures(const MatrixF&,const VectorF&,ConvexFeature* f)
|
||||
{
|
||||
f->object = NULL;
|
||||
}
|
||||
|
||||
const MatrixF& Convex::getTransform() const
|
||||
{
|
||||
return mObject->getTransform();
|
||||
}
|
||||
|
||||
const Point3F& Convex::getScale() const
|
||||
{
|
||||
return mObject->getScale();
|
||||
}
|
||||
|
||||
Box3F Convex::getBoundingBox() const
|
||||
{
|
||||
return mObject->getWorldBox();
|
||||
}
|
||||
|
||||
Box3F Convex::getBoundingBox(const MatrixF& mat, const Point3F& scale) const
|
||||
{
|
||||
Box3F wBox = mObject->getObjBox();
|
||||
wBox.min.convolve(scale);
|
||||
wBox.max.convolve(scale);
|
||||
mat.mul(wBox);
|
||||
return wBox;
|
||||
}
|
||||
|
||||
void Convex::render()
|
||||
{
|
||||
for (CollisionStateList* itr = mList.mNext; itr != &mList; itr = itr->mNext)
|
||||
if (itr->mState->mLista == itr)
|
||||
itr->mState->render();
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void Convex::addToWorkingList(Convex* ptr)
|
||||
{
|
||||
CollisionWorkingList* cl = CollisionWorkingList::alloc();
|
||||
cl->wLinkAfter(&mWorking);
|
||||
cl->rLinkAfter(&ptr->mReference);
|
||||
cl->mConvex = ptr;
|
||||
};
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void Convex::updateWorkingList(const Box3F& box, const U32 colMask)
|
||||
{
|
||||
sTag++;
|
||||
|
||||
// Clear objects off the working list that are no longer intersecting
|
||||
for (CollisionWorkingList* itr = mWorking.wLink.mNext; itr != &mWorking; itr = itr->wLink.mNext) {
|
||||
itr->mConvex->mTag = sTag;
|
||||
if ((!box.isOverlapped(itr->mConvex->getBoundingBox())) || (!itr->mConvex->getObject()->isCollisionEnabled())) {
|
||||
CollisionWorkingList* cl = itr;
|
||||
itr = itr->wLink.mPrev;
|
||||
cl->free();
|
||||
}
|
||||
}
|
||||
|
||||
// Special processing for the terrain and interiors...
|
||||
AssertFatal(mObject->getContainer(), "Must be in a container!");
|
||||
|
||||
SimpleQueryList sql;
|
||||
mObject->getContainer()->findObjects(box, colMask,SimpleQueryList::insertionCallback, &sql);
|
||||
for (U32 i = 0; i < sql.mList.size(); i++)
|
||||
sql.mList[i]->buildConvex(box, this);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void Convex::updateStateList(const MatrixF& mat, const Point3F& scale, const Point3F* displacement)
|
||||
{
|
||||
Box3F box1 = getBoundingBox(mat, scale);
|
||||
box1.min -= Point3F(1, 1, 1);
|
||||
box1.max += Point3F(1, 1, 1);
|
||||
if (displacement) {
|
||||
Point3F oldMin = box1.min;
|
||||
Point3F oldMax = box1.max;
|
||||
|
||||
box1.min.setMin(oldMin + *displacement);
|
||||
box1.min.setMin(oldMax + *displacement);
|
||||
box1.max.setMax(oldMin + *displacement);
|
||||
box1.max.setMax(oldMax + *displacement);
|
||||
}
|
||||
sTag++;
|
||||
|
||||
// Destroy states which are no longer intersecting
|
||||
for (CollisionStateList* itr = mList.mNext; itr != &mList; itr = itr->mNext) {
|
||||
Convex* cv = (itr->mState->a == this)? itr->mState->b: itr->mState->a;
|
||||
cv->mTag = sTag;
|
||||
if (!box1.isOverlapped(cv->getBoundingBox())) {
|
||||
CollisionState* cs = itr->mState;
|
||||
itr = itr->mPrev;
|
||||
delete cs;
|
||||
}
|
||||
}
|
||||
|
||||
// Add collision states for new overlapping objects
|
||||
for (CollisionWorkingList* itr0 = mWorking.wLink.mNext; itr0 != &mWorking; itr0 = itr0->wLink.mNext) {
|
||||
register Convex* cv = itr0->mConvex;
|
||||
if (cv->mTag != sTag && box1.isOverlapped(cv->getBoundingBox())) {
|
||||
CollisionState* state = new GjkCollisionState;
|
||||
state->set(this,cv,mat,cv->getTransform());
|
||||
state->mLista->linkAfter(&mList);
|
||||
state->mListb->linkAfter(&cv->mList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
CollisionState* Convex::findClosestState(const MatrixF& mat, const Point3F& scale, const F32 dontCareDist)
|
||||
{
|
||||
updateStateList(mat, scale);
|
||||
F32 dist = +1E30;
|
||||
CollisionState *st = 0;
|
||||
|
||||
// Prepare scaled version of transform
|
||||
MatrixF axform = mat;
|
||||
axform.scale(scale);
|
||||
MatrixF axforminv(true);
|
||||
MatrixF temp(mat);
|
||||
axforminv.scale(Point3F(1.0f/scale.x,1.0f/scale.y,1.0f/scale.z));
|
||||
temp.affineInverse();
|
||||
axforminv.mul(temp);
|
||||
|
||||
for (CollisionStateList* itr = mList.mNext; itr != &mList; itr = itr->mNext) {
|
||||
CollisionState* state = itr->mState;
|
||||
if (state->mLista != itr)
|
||||
state->swap();
|
||||
|
||||
// Prepare scaled version of transform
|
||||
MatrixF bxform = state->b->getTransform();
|
||||
temp = bxform;
|
||||
Point3F bscale = state->b->getScale();
|
||||
bxform.scale(bscale);
|
||||
MatrixF bxforminv(true);
|
||||
bxforminv.scale(Point3F(1.0f/bscale.x,1.0f/bscale.y,1.0f/bscale.z));
|
||||
temp.affineInverse();
|
||||
bxforminv.mul(temp);
|
||||
|
||||
//
|
||||
F32 dd = state->distance(axform, bxform, dontCareDist, &axforminv, &bxforminv);
|
||||
if (dd < dist) {
|
||||
dist = dd;
|
||||
st = state;
|
||||
}
|
||||
}
|
||||
if (dist < dontCareDist)
|
||||
return st;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
bool Convex::getCollisionInfo(const MatrixF& mat, const Point3F& scale, CollisionList* cList,F32 tol)
|
||||
{
|
||||
for (CollisionStateList* itr = mList.mNext; itr != &mList; itr = itr->mNext) {
|
||||
CollisionState* state = itr->mState;
|
||||
if (state->mLista != itr)
|
||||
state->swap();
|
||||
if (state->dist <= tol) {
|
||||
ConvexFeature fa,fb;
|
||||
VectorF v;
|
||||
|
||||
// The idea is that we need to scale the matrix, so we need to
|
||||
// make a copy of it, before we can pass it in to getFeatures.
|
||||
// This is used to scale us for comparison against the other
|
||||
// convex, which is correctly scaled.
|
||||
MatrixF omat = mat;
|
||||
omat.scale(scale);
|
||||
MatrixF imat = omat;
|
||||
imat.inverse();
|
||||
imat.mulV(-state->v,&v);
|
||||
getFeatures(omat,v,&fa);
|
||||
|
||||
imat = state->b->getTransform();
|
||||
imat.scale(state->b->getScale());
|
||||
MatrixF bxform = imat;
|
||||
imat.inverse();
|
||||
imat.mulV(state->v,&v);
|
||||
state->b->getFeatures(bxform,v,&fb);
|
||||
|
||||
fa.collide(fb,cList,tol);
|
||||
}
|
||||
}
|
||||
return (cList->count != 0);
|
||||
}
|
||||
|
||||
void Convex::getPolyList(AbstractPolyList*)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// This function based on code orignally written for the book:
|
||||
// 3D Game Engine Design, by David H. Eberly
|
||||
//
|
||||
F32 sqrDistanceEdges(const Point3F& start0, const Point3F& end0,
|
||||
const Point3F& start1, const Point3F& end1,
|
||||
Point3F* is, Point3F* it)
|
||||
{
|
||||
Point3F direction0 = end0 - start0;
|
||||
F32 fA00 = direction0.lenSquared();
|
||||
|
||||
Point3F direction1 = end1 - start1;
|
||||
F32 fA11 = direction1.lenSquared();
|
||||
F32 fA01 = -mDot(direction0, direction1);
|
||||
|
||||
Point3F kDiff = start0 - start1;
|
||||
F32 fC = kDiff.lenSquared();
|
||||
F32 fB0 = mDot(kDiff, direction0);
|
||||
F32 fDet = mAbs((S32)(fA00*fA11 - fA01*fA01));
|
||||
|
||||
// Since the endpoints are tested as vertices, we're not interested
|
||||
// in parallel lines, and intersections that don't involve end-points.
|
||||
if (fDet >= 0.00001) {
|
||||
|
||||
// Calculate time of intersection for each line
|
||||
F32 fB1 = -mDot(kDiff, direction1);
|
||||
F32 fS = fA01*fB1-fA11*fB0;
|
||||
F32 fT = fA01*fB0-fA00*fB1;
|
||||
|
||||
// Only interested in collisions that don't involve the end points
|
||||
if (fS >= 0.0 && fS <= fDet && fT >= 0.0 && fT <= fDet) {
|
||||
F32 fInvDet = 1.0 / fDet;
|
||||
fS *= fInvDet;
|
||||
fT *= fInvDet;
|
||||
F32 fSqrDist = (fS*(fA00*fS + fA01*fT + 2.0*fB0) +
|
||||
fT*(fA01*fS + fA11*fT + 2.0*fB1) + fC);
|
||||
|
||||
// Intersection points.
|
||||
*is = start0 + direction0 * fS;
|
||||
*it = start1 + direction1 * fT;
|
||||
return mFabs(fSqrDist);
|
||||
}
|
||||
}
|
||||
|
||||
// Return a large number in the cases where endpoints are involved.
|
||||
return 1e10f;
|
||||
}
|
249
engine/collision/convex.h
Executable file
249
engine/collision/convex.h
Executable file
@ -0,0 +1,249 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _CONVEX_H_
|
||||
#define _CONVEX_H_
|
||||
|
||||
#ifndef _MMATH_H_
|
||||
#include "math/mMath.h"
|
||||
#endif
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/tVector.h"
|
||||
#endif
|
||||
|
||||
struct Collision;
|
||||
struct CollisionList;
|
||||
struct CollisionStateList;
|
||||
class AbstractPolyList;
|
||||
class SceneObject;
|
||||
class Convex;
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
class ConvexFeature
|
||||
{
|
||||
public:
|
||||
struct Edge {
|
||||
S32 vertex[2];
|
||||
};
|
||||
struct Face {
|
||||
VectorF normal;
|
||||
S32 vertex[3];
|
||||
};
|
||||
|
||||
Vector<Point3F> mVertexList;
|
||||
Vector<Edge> mEdgeList;
|
||||
Vector<Face> mFaceList;
|
||||
S32 material;
|
||||
SceneObject* object;
|
||||
|
||||
ConvexFeature() : mVertexList(64), mEdgeList(128), mFaceList(64) {
|
||||
VECTOR_SET_ASSOCIATION(mVertexList);
|
||||
VECTOR_SET_ASSOCIATION(mEdgeList);
|
||||
VECTOR_SET_ASSOCIATION(mFaceList);
|
||||
}
|
||||
|
||||
bool collide(ConvexFeature& cf,CollisionList* cList, F32 tol = 0.1);
|
||||
void testVertex(const Point3F& v,CollisionList* cList,bool,F32 tol);
|
||||
void testEdge(ConvexFeature* cf,const Point3F& s1, const Point3F& e1, CollisionList* cList, F32 tol);
|
||||
bool inVolume(const Point3F& v);
|
||||
};
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
enum ConvexType {
|
||||
TSConvexType,
|
||||
BoxConvexType,
|
||||
TerrainConvexType,
|
||||
InteriorConvexType,
|
||||
ShapeBaseConvexType,
|
||||
TSStaticConvexType,
|
||||
InteriorMapConvexType
|
||||
};
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
struct CollisionState
|
||||
{
|
||||
CollisionStateList* mLista;
|
||||
CollisionStateList* mListb;
|
||||
Convex* a;
|
||||
Convex* b;
|
||||
|
||||
F32 dist; // Current estimated distance
|
||||
VectorF v; // Vector between closest points
|
||||
|
||||
//
|
||||
CollisionState();
|
||||
virtual ~CollisionState();
|
||||
virtual void swap();
|
||||
virtual void set(Convex* a,Convex* b,const MatrixF& a2w, const MatrixF& b2w);
|
||||
virtual F32 distance(const MatrixF& a2w, const MatrixF& b2w, const F32 dontCareDist,
|
||||
const MatrixF* w2a = NULL, const MatrixF* _w2b = NULL);
|
||||
void render();
|
||||
};
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
struct CollisionStateList
|
||||
{
|
||||
static CollisionStateList sFreeList;
|
||||
CollisionStateList* mNext;
|
||||
CollisionStateList* mPrev;
|
||||
CollisionState* mState;
|
||||
|
||||
CollisionStateList();
|
||||
|
||||
void linkAfter(CollisionStateList* next);
|
||||
void unlink();
|
||||
bool isEmpty() { return mNext == this; }
|
||||
|
||||
static CollisionStateList* alloc();
|
||||
void free();
|
||||
};
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
struct CollisionWorkingList
|
||||
{
|
||||
static CollisionWorkingList sFreeList;
|
||||
struct WLink {
|
||||
CollisionWorkingList* mNext;
|
||||
CollisionWorkingList* mPrev;
|
||||
} wLink;
|
||||
struct RLink {
|
||||
CollisionWorkingList* mNext;
|
||||
CollisionWorkingList* mPrev;
|
||||
} rLink;
|
||||
Convex* mConvex;
|
||||
|
||||
void wLinkAfter(CollisionWorkingList* next);
|
||||
void rLinkAfter(CollisionWorkingList* next);
|
||||
void unlink();
|
||||
CollisionWorkingList();
|
||||
|
||||
static CollisionWorkingList* alloc();
|
||||
void free();
|
||||
};
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
class Convex {
|
||||
|
||||
/// @name Linked list managent
|
||||
/// @{
|
||||
Convex* mNext; ///< Next item in linked list of Convex's
|
||||
Convex* mPrev; ///< Previous item in linked list of Convex's
|
||||
|
||||
/// Insert this Convex after the provided convex
|
||||
/// @param next
|
||||
void linkAfter(Convex* next);
|
||||
|
||||
/// Remove this Convex from the linked list
|
||||
void unlink();
|
||||
/// @}
|
||||
|
||||
U32 mTag;
|
||||
static U32 sTag;
|
||||
|
||||
protected:
|
||||
CollisionStateList mList; ///< Objects we're testing against
|
||||
CollisionWorkingList mWorking; ///< Objects within our bounds
|
||||
CollisionWorkingList mReference; ///< Other convex testing against us
|
||||
SceneObject* mObject; ///< Object this Convex is built around
|
||||
ConvexType mType; ///< Type of Convex this is @see ConvexType
|
||||
|
||||
//
|
||||
public:
|
||||
|
||||
/// Constructor
|
||||
Convex();
|
||||
|
||||
/// Destructor
|
||||
virtual ~Convex();
|
||||
|
||||
/// Registers another Convex by linking it after this one
|
||||
void registerObject(Convex *convex);
|
||||
|
||||
/// Runs through the linked list of Convex objects and removes the ones
|
||||
/// with no references
|
||||
void collectGarbage();
|
||||
|
||||
/// Deletes all convex objects in the list
|
||||
void nukeList();
|
||||
|
||||
/// Returns the type of this Convex
|
||||
ConvexType getType() { return mType; }
|
||||
|
||||
/// Returns the object this Convex is built from
|
||||
SceneObject* getObject() { return mObject; }
|
||||
|
||||
/// Traverses mList and renders all collision states
|
||||
void render();
|
||||
|
||||
/// Adds the provided Convex to the list of objects within the bounds of this Convex
|
||||
/// @param ptr Convex to add to the working list of this object
|
||||
void addToWorkingList(Convex* ptr);
|
||||
|
||||
/// Returns the list of objects currently inside the bounds of this Convex
|
||||
CollisionWorkingList& getWorkingList() { return mWorking; }
|
||||
|
||||
/// Finds the closest
|
||||
CollisionState* findClosestState(const MatrixF& mat, const Point3F& scale, const F32 dontCareDist = 1);
|
||||
|
||||
/// Returns the list of objects this object is testing against
|
||||
CollisionStateList* getStateList() { return mList.mNext; }
|
||||
|
||||
/// Updates the CollisionStateList (mList) with new collision states and removing
|
||||
/// ones no longer under consideration
|
||||
/// @param mat Used as the matrix to create a bounding box for updating the list
|
||||
/// @param scale Used to scale the bounding box
|
||||
/// @param displacement Bounding box displacement (optional)
|
||||
void updateStateList(const MatrixF& mat, const Point3F& scale, const Point3F* displacement = NULL);
|
||||
|
||||
/// Updates the working collision list of objects which are currently colliding with
|
||||
/// (inside the bounds of) this Convex.
|
||||
///
|
||||
/// @param box Used as the bounding box.
|
||||
/// @param colMask Mask of objects to check against.
|
||||
void updateWorkingList(const Box3F& box, const U32 colMask);
|
||||
|
||||
/// Returns the transform of the object this is built around
|
||||
virtual const MatrixF& getTransform() const;
|
||||
|
||||
/// Returns the scale of the object this is built around
|
||||
virtual const Point3F& getScale() const;
|
||||
|
||||
/// Returns the bounding box for the object this is built around in world space
|
||||
virtual Box3F getBoundingBox() const;
|
||||
|
||||
/// Returns the object space bounding box for the object this is built around
|
||||
/// transformed and scaled
|
||||
/// @param mat Matrix to transform the object-space box by
|
||||
/// @param scale Scaling factor to scale the bounding box by
|
||||
virtual Box3F getBoundingBox(const MatrixF& mat, const Point3F& scale) const;
|
||||
|
||||
/// Returns the farthest point, along a vector, still bound by the convex
|
||||
/// @param v Vector
|
||||
virtual Point3F support(const VectorF& v) const;
|
||||
|
||||
///
|
||||
virtual void getFeatures(const MatrixF& mat,const VectorF& n, ConvexFeature* cf);
|
||||
|
||||
/// Builds a collision poly list out of this convex
|
||||
/// @param list (Out) Poly list built
|
||||
virtual void getPolyList(AbstractPolyList* list);
|
||||
|
||||
///
|
||||
bool getCollisionInfo(const MatrixF& mat, const Point3F& scale, CollisionList* cList,F32 tol);
|
||||
};
|
||||
|
||||
#endif
|
1290
engine/collision/convexBrush.cc
Executable file
1290
engine/collision/convexBrush.cc
Executable file
File diff suppressed because it is too large
Load Diff
127
engine/collision/convexBrush.h
Executable file
127
engine/collision/convexBrush.h
Executable file
@ -0,0 +1,127 @@
|
||||
#ifndef _CONVEXBRUSH_H_
|
||||
#define _CONVEXBRUSH_H_
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "core/tVector.h"
|
||||
#include "math/mPoint.h"
|
||||
#include "math/mBox.h"
|
||||
#include "math/mPlane.h"
|
||||
#include "collision/abstractPolyList.h"
|
||||
#include "collision/optimizedPolyList.h"
|
||||
#include "sim/sceneObject.h"
|
||||
#include "interior/interiorMapRes.h"
|
||||
|
||||
#define WINDINGSCALE 10000
|
||||
|
||||
class ConvexBrush
|
||||
{
|
||||
public:
|
||||
// Constructor
|
||||
ConvexBrush();
|
||||
// Destructor
|
||||
~ConvexBrush();
|
||||
|
||||
enum
|
||||
{
|
||||
Front = 0,
|
||||
Back = 1,
|
||||
On = 2,
|
||||
Split = 3,
|
||||
Unknown = 4
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
Unprocessed = 0,
|
||||
Good,
|
||||
BadWinding,
|
||||
ShortPlanes,
|
||||
BadFaces,
|
||||
Malformed,
|
||||
Concave,
|
||||
Deleted
|
||||
};
|
||||
|
||||
// Supporting structures
|
||||
struct TexInfo
|
||||
{
|
||||
StringTableEntry texture;
|
||||
PlaneF texGens[2];
|
||||
F32 scale[2];
|
||||
F32 rot;
|
||||
F32 texDiv[2];
|
||||
};
|
||||
|
||||
// The brushes owner entity
|
||||
InteriorMapResource::Entity* mOwner;
|
||||
|
||||
// Bounding box
|
||||
Box3F mBounds;
|
||||
Point3F mCentroid;
|
||||
MatrixF mTransform;
|
||||
Point3F mScale;
|
||||
QuatF mRotation;
|
||||
|
||||
// lighting info...
|
||||
MatrixF mLightingTransform;
|
||||
|
||||
// Some useful values
|
||||
F32 mBrushScale;
|
||||
S32 mID;
|
||||
U32 mType;
|
||||
U32 mStatus;
|
||||
U32 mPrevStatus;
|
||||
bool mSelected;
|
||||
StringTableEntry mDebugInfo;
|
||||
|
||||
// Scratch buffer storage
|
||||
MatrixF mTransformScratch;
|
||||
QuatF mRotationScratch;
|
||||
|
||||
|
||||
// Data
|
||||
OptimizedPolyList mFaces;
|
||||
Vector<TexInfo> mTexInfos;
|
||||
|
||||
// Setup functions
|
||||
bool addPlane(PlaneF pln);
|
||||
bool addFace(PlaneF pln, PlaneF texGen[2], F32 scale[2], char* texture);
|
||||
bool addFace(PlaneF pln, PlaneF texGen[2], F32 scale[2], char* texture, U32 matIdx);
|
||||
bool addFace(PlaneF pln, PlaneF texGen[2], F32 scale[2], U32 matIdx);
|
||||
bool processBrush();
|
||||
bool setScale(F32 scale);
|
||||
|
||||
// Utility functions
|
||||
bool selfClip();
|
||||
bool intersectPlanes(const PlaneF& plane1, const PlaneF& plane2, const PlaneF& plane3, Point3D* pOutput);
|
||||
bool createWinding(const Vector<U32>& rPoints, const Point3F& normal, Vector<U32>& pWinding);
|
||||
void addEdge(U16 zero, U16 one, U32 face);
|
||||
bool generateEdgelist();
|
||||
void validateWindings();
|
||||
bool validateEdges();
|
||||
void addIndex(Vector<U32>& indices, U32 index);
|
||||
void calcCentroid();
|
||||
void setupTransform();
|
||||
void resetBrush();
|
||||
|
||||
U32 getPolySide(S32 side);
|
||||
U32 whichSide(PlaneF pln);
|
||||
U32 whichSide(PlaneF pln, U32 faceIndex);
|
||||
bool isInsideBox(PlaneF left, PlaneF right, PlaneF top, PlaneF bottom);
|
||||
bool splitBrush(PlaneF pln, ConvexBrush* fbrush, ConvexBrush* bbrush);
|
||||
bool calcBounds();
|
||||
bool castRay(const Point3F& s, const Point3F& e, RayInfo* info);
|
||||
|
||||
OptimizedPolyList getIntersectingPolys(OptimizedPolyList* list);
|
||||
OptimizedPolyList getNonIntersectingPolys(OptimizedPolyList* list);
|
||||
bool isPolyInside(OptimizedPolyList* list, U32 pdx);
|
||||
|
||||
bool getPolyList(AbstractPolyList* list);
|
||||
|
||||
// Debug render functions
|
||||
bool render(bool genColors);
|
||||
bool renderFace(U32 face, bool renderLighting);
|
||||
bool renderEdges(ColorF color);
|
||||
};
|
||||
|
||||
#endif
|
903
engine/collision/depthSortList.cc
Executable file
903
engine/collision/depthSortList.cc
Executable file
@ -0,0 +1,903 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "dgl/dgl.h"
|
||||
#include "math/mMath.h"
|
||||
#include "console/console.h"
|
||||
#include "collision/depthSortList.h"
|
||||
#include "core/color.h"
|
||||
#include "core/fileStream.h" // TODO, remove this
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
// some defines and global parameters that affect poly split routine
|
||||
F32 SPLIT_TOL = 0.0005f;
|
||||
bool ALWAYS_RETURN_FRONT_AND_BACK = false; // if false, split routine will return polys only if a split occurs
|
||||
|
||||
// more global parameters
|
||||
F32 XZ_TOL = 0.0f;
|
||||
F32 DEPTH_TOL = 0.01f;
|
||||
#define MIN_Y_DOT 0.05f
|
||||
DepthSortList * gCurrentSort = NULL;
|
||||
S32 gForceOverlap = -1; // force an overlap test to result in an overlap
|
||||
S32 gNoOverlapCount;
|
||||
S32 gBadSpots = 0;
|
||||
|
||||
// if polys are correctly sorted then writing depth values should result in no
|
||||
// overlap of polys when looking down from camera...otoh, if polys are out of
|
||||
// order, we should see overlap
|
||||
bool DepthSortList::renderWithDepth = false;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// following copied from shapeBase.cc because I didn't want to reference
|
||||
// something from the test program in the core. This should really be a
|
||||
// stand-alone function, but...
|
||||
|
||||
static ColorF cubeColors[8] = {
|
||||
ColorF(0, 0, 0), ColorF(1, 0, 0), ColorF(0, 1, 0), ColorF(0, 0, 1),
|
||||
ColorF(1, 1, 0), ColorF(1, 0, 1), ColorF(0, 1, 1), ColorF(1, 1, 1)
|
||||
};
|
||||
|
||||
static Point3F cubePoints[8] = {
|
||||
Point3F(-1, -1, -1), Point3F(-1, -1, 1), Point3F(-1, 1, -1), Point3F(-1, 1, 1),
|
||||
Point3F( 1, -1, -1), Point3F( 1, -1, 1), Point3F( 1, 1, -1), Point3F( 1, 1, 1)
|
||||
};
|
||||
|
||||
static U32 cubeFaces[6][4] = {
|
||||
{ 0, 2, 6, 4 }, { 0, 2, 3, 1 }, { 0, 1, 5, 4 },
|
||||
{ 3, 2, 6, 7 }, { 7, 6, 4, 5 }, { 3, 7, 5, 1 }
|
||||
};
|
||||
|
||||
void DepthSortList::wireCube(const Point3F& size, const Point3F& pos)
|
||||
{
|
||||
glDisable(GL_CULL_FACE);
|
||||
|
||||
for(int i = 0; i < 6; i++) {
|
||||
glBegin(GL_LINE_LOOP);
|
||||
for(int vert = 0; vert < 4; vert++) {
|
||||
int idx = cubeFaces[i][vert];
|
||||
glColor3f(cubeColors[idx].red, cubeColors[idx].green, cubeColors[idx].blue);
|
||||
glVertex3f(cubePoints[idx].x * size.x + pos.x, cubePoints[idx].y * size.y + pos.y, cubePoints[idx].z * size.z + pos.z);
|
||||
}
|
||||
glEnd();
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
DepthSortList::DepthSortList()
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION(mPolyExtentsList);
|
||||
VECTOR_SET_ASSOCIATION(mPolyIndexList);
|
||||
}
|
||||
|
||||
DepthSortList::~DepthSortList()
|
||||
{
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void DepthSortList::clear()
|
||||
{
|
||||
Parent::clear();
|
||||
|
||||
mPolyExtentsList.clear();
|
||||
mPolyIndexList.clear();
|
||||
|
||||
clearSort();
|
||||
}
|
||||
|
||||
void DepthSortList::clearSort()
|
||||
{
|
||||
mBase = -1;
|
||||
mMaxTouched = 0;
|
||||
gNoOverlapCount = 0;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void DepthSortList::end()
|
||||
{
|
||||
S32 numPoly = mPolyList.size();
|
||||
|
||||
if (mPolyList.last().plane.y >= -MIN_Y_DOT)
|
||||
{
|
||||
mIndexList.setSize(mPolyList.last().vertexStart);
|
||||
mPolyList.decrement();
|
||||
return;
|
||||
}
|
||||
|
||||
Parent::end();
|
||||
|
||||
// we deleted this poly, be sure not to add anything more...
|
||||
if (mPolyList.size()!=numPoly)
|
||||
return;
|
||||
|
||||
AssertFatal(mPolyList.last().vertexCount>2,"DepthSortList::end: only two vertices in poly");
|
||||
|
||||
mPolyExtentsList.increment();
|
||||
setExtents(mPolyList.last(),mPolyExtentsList.last());
|
||||
mPolyIndexList.push_back(numPoly-1);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
bool DepthSortList::getMapping(MatrixF * mat, Box3F * box)
|
||||
{
|
||||
// return list transform and bounds in list space...optional
|
||||
*mat = mMatrix;
|
||||
mat->inverse();
|
||||
box->min.set(-mExtent.x, 0.0f, -mExtent.z);
|
||||
box->max.set( mExtent.x, 2.0f * mExtent.y, mExtent.z);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void DepthSortList::render()
|
||||
{
|
||||
glPushMatrix();
|
||||
glPushAttrib(GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
MatrixF mat = mBaseMatrix;
|
||||
mat.inverse();
|
||||
dglMultMatrix(&mat);
|
||||
|
||||
glVertexPointer(3,GL_FLOAT,sizeof(Vertex),mVertexList.address());
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glColor4f(1,0,0,0.25);
|
||||
glEnable(GL_BLEND);
|
||||
glEnable(GL_CULL_FACE);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
if (renderWithDepth)
|
||||
glDepthMask(GL_TRUE);
|
||||
else
|
||||
glDepthMask(GL_FALSE);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
Poly * p;
|
||||
S32 only = Con::getIntVariable("$only",-1);
|
||||
for (S32 i=0; i<mPolyIndexList.size(); i++)
|
||||
{
|
||||
if (only>=0 && only!=i)
|
||||
continue;
|
||||
p = &mPolyList[mPolyIndexList[i]];
|
||||
glDrawElements(GL_POLYGON,p->vertexCount,
|
||||
GL_UNSIGNED_INT,&mIndexList[p->vertexStart]);
|
||||
}
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
|
||||
// draw outline around clip zone...
|
||||
wireCube(mExtent,Point3F(0,mExtent.y*0.5f,0));
|
||||
|
||||
glPopMatrix();
|
||||
glPopAttrib();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void DepthSortList::setExtents(Poly & poly, PolyExtents & polyExtents)
|
||||
{
|
||||
Point3F p = mVertexList[mIndexList[poly.vertexStart]].point;
|
||||
polyExtents.xMin = polyExtents.xMax = p.x;
|
||||
polyExtents.yMin = polyExtents.yMax = p.y;
|
||||
polyExtents.zMin = polyExtents.zMax = p.z;
|
||||
for (S32 i=poly.vertexStart+1; i<poly.vertexStart+poly.vertexCount; i++)
|
||||
{
|
||||
Point3F p = mVertexList[mIndexList[i]].point;
|
||||
|
||||
// x
|
||||
if (p.x < polyExtents.xMin)
|
||||
polyExtents.xMin = p.x;
|
||||
else if (p.x > polyExtents.xMax)
|
||||
polyExtents.xMax = p.x;
|
||||
|
||||
// y
|
||||
if (p.y < polyExtents.yMin)
|
||||
polyExtents.yMin = p.y;
|
||||
else if (p.y > polyExtents.yMax)
|
||||
polyExtents.yMax = p.y;
|
||||
|
||||
// z
|
||||
if (p.z < polyExtents.zMin)
|
||||
polyExtents.zMin = p.z;
|
||||
else if (p.z > polyExtents.zMax)
|
||||
polyExtents.zMax = p.z;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
// function for comparing two poly indices
|
||||
S32 FN_CDECL compareYExtents( const void* e1, const void* e2)
|
||||
{
|
||||
DepthSortList::PolyExtents & poly1 = gCurrentSort->getExtents(*(U32*)e1);
|
||||
DepthSortList::PolyExtents & poly2 = gCurrentSort->getExtents(*(U32*)e2);
|
||||
|
||||
if (poly1.yMin < poly2.yMin)
|
||||
return -1;
|
||||
if (poly2.yMin < poly1.yMin)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void DepthSortList::sortByYExtents()
|
||||
{
|
||||
gCurrentSort = this;
|
||||
dQsort(mPolyIndexList.address(),mPolyIndexList.size(),sizeof(U32),compareYExtents);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void DepthSortList::set(const MatrixF & mat, Point3F & extents)
|
||||
{
|
||||
setBaseTransform(mat);
|
||||
mNormal.set(0,1,0); // ignore polys not facing up...
|
||||
mExtent = extents;
|
||||
mExtent *= 0.5f;
|
||||
|
||||
// set clip planes
|
||||
mPlaneList.clear();
|
||||
|
||||
mPlaneList.increment();
|
||||
mPlaneList.last().set(-1.0f, 0.0f, 0.0f);
|
||||
mPlaneList.last().d = -mExtent.x;
|
||||
mPlaneList.increment();
|
||||
mPlaneList.last().set( 1.0f, 0.0f, 0.0f);
|
||||
mPlaneList.last().d = -mExtent.x;
|
||||
|
||||
mPlaneList.increment();
|
||||
mPlaneList.last().set( 0.0f,-1.0f, 0.0f);
|
||||
mPlaneList.last().d = 0;
|
||||
mPlaneList.increment();
|
||||
mPlaneList.last().set( 0.0f, 1.0f, 0.0f);
|
||||
mPlaneList.last().d = -2.0f * mExtent.y;
|
||||
|
||||
mPlaneList.increment();
|
||||
mPlaneList.last().set( 0.0f, 0.0f,-1.0f);
|
||||
mPlaneList.last().d = -mExtent.z;
|
||||
mPlaneList.increment();
|
||||
mPlaneList.last().set( 0.0f, 0.0f, 1.0f);
|
||||
mPlaneList.last().d = -mExtent.z;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void DepthSortList::setBase(S32 base)
|
||||
{
|
||||
mBase = base;
|
||||
getOrderedPoly(mBase, &mBasePoly, &mBaseExtents);
|
||||
mBaseNormal = &mBasePoly->plane;
|
||||
mBaseDot = -mBasePoly->plane.d;
|
||||
mBaseYMax = mBaseExtents->yMax;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
bool DepthSortList::sortNext()
|
||||
{
|
||||
// find the next poly in the depth order
|
||||
// NOTE: a closer poly may occur before a farther away poly so long as
|
||||
// they don't overlap in the x-z plane...
|
||||
if (++mBase>=mPolyIndexList.size())
|
||||
return false;
|
||||
|
||||
setBase(mBase);
|
||||
|
||||
gBadSpots = 0;
|
||||
ALWAYS_RETURN_FRONT_AND_BACK = false; // split routine will return polys only if a split occurs
|
||||
|
||||
bool switched = false; // haven't switched base poly yet
|
||||
S32 i = 0; // currently comparing base to base+i
|
||||
Poly * testPoly;
|
||||
PolyExtents * testExtents;
|
||||
|
||||
while (mBase+i+1<mPolyIndexList.size())
|
||||
{
|
||||
i++;
|
||||
|
||||
// get test poly...
|
||||
getOrderedPoly(mBase+i,&testPoly,&testExtents);
|
||||
Point3F & testNormal = testPoly->plane;
|
||||
F32 testDot = -testPoly->plane.d;
|
||||
|
||||
// if base poly's y extents don't overlap test poly's, base poly can stay where it is...
|
||||
if (mBase+i>mMaxTouched && mBaseYMax<=testExtents->yMin+DEPTH_TOL)
|
||||
break;
|
||||
|
||||
// if base poly and test poly don't have overlapping x & z extents, then order doesn't matter...stay the same
|
||||
if (mBaseExtents->xMin>=testExtents->xMax-XZ_TOL || mBaseExtents->xMax<=testExtents->xMin+XZ_TOL ||
|
||||
mBaseExtents->zMin>=testExtents->zMax-XZ_TOL || mBaseExtents->zMax<=testExtents->zMin+XZ_TOL)
|
||||
continue;
|
||||
|
||||
// is test poly completely behind base poly? if so, order is fine as it is
|
||||
S32 v;
|
||||
for (v=0; v<testPoly->vertexCount; v++)
|
||||
if (mDot(mVertexList[mIndexList[testPoly->vertexStart+v]].point,*mBaseNormal)>mBaseDot+DEPTH_TOL)
|
||||
break;
|
||||
if (v==testPoly->vertexCount)
|
||||
// test behind base
|
||||
continue;
|
||||
|
||||
// is base poly completely in front of test poly? if so, order is fine as it is
|
||||
for (v=0; v<mBasePoly->vertexCount; v++)
|
||||
if (mDot(mVertexList[mIndexList[mBasePoly->vertexStart+v]].point,testNormal)<testDot-DEPTH_TOL)
|
||||
break;
|
||||
if (v==mBasePoly->vertexCount)
|
||||
// base in front of test
|
||||
continue;
|
||||
|
||||
// if the polys don't overlap in the x-z plane, then order doesn't matter, stay as we are
|
||||
if (!overlap(mBasePoly,testPoly))
|
||||
{
|
||||
gNoOverlapCount++;
|
||||
if (gNoOverlapCount!=gForceOverlap)
|
||||
continue;
|
||||
}
|
||||
|
||||
// handle switching/splitting of polys due to overlap
|
||||
handleOverlap(testPoly,testNormal,testDot,i,switched);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void DepthSortList::sort()
|
||||
{
|
||||
// depth sort mPolyIndexList -- entries are indices into mPolyList (where poly is found) & mPolyExtentsList
|
||||
|
||||
// sort by min y extent
|
||||
sortByYExtents();
|
||||
|
||||
mBase = -1;
|
||||
|
||||
while (sortNext())
|
||||
;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void DepthSortList::handleOverlap(Poly * testPoly, Point3F & testNormal, F32 testDot, S32 & testOffset, bool & switched)
|
||||
{
|
||||
// first reverse the plane tests (i.e., test to see if basePoly behind testPoly or testPoly in front of basePoly...
|
||||
// if either succeeds, switch poly
|
||||
// if they both fail, split base poly
|
||||
// But split anyway if basePoly has already been switched...
|
||||
bool doSwitch = false;
|
||||
|
||||
if (!switched)
|
||||
{
|
||||
S32 v;
|
||||
for (v=0; v<mBasePoly->vertexCount; v++)
|
||||
if (mDot(mVertexList[mIndexList[mBasePoly->vertexStart+v]].point,testNormal)>testDot+DEPTH_TOL)
|
||||
break;
|
||||
if (v==mBasePoly->vertexCount)
|
||||
doSwitch = true;
|
||||
else
|
||||
{
|
||||
for (v=0; v<testPoly->vertexCount; v++)
|
||||
if (mDot(mVertexList[mIndexList[testPoly->vertexStart+v]].point,*mBaseNormal)<mBaseDot-DEPTH_TOL)
|
||||
break;
|
||||
if (v==testPoly->vertexCount)
|
||||
doSwitch = true;
|
||||
}
|
||||
}
|
||||
|
||||
// try to split base poly along plane of test poly
|
||||
Poly frontPoly, backPoly;
|
||||
bool splitBase = false, splitTest = false;
|
||||
if (!doSwitch)
|
||||
{
|
||||
splitBase = splitPoly(*mBasePoly,testNormal,testDot,frontPoly,backPoly);
|
||||
if (!splitBase)
|
||||
// didn't take...no splitting happened...try splitting test poly by base poly
|
||||
splitTest = splitPoly(*testPoly,*mBaseNormal,mBaseDot,frontPoly,backPoly);
|
||||
}
|
||||
|
||||
U32 testIdx = mPolyIndexList[mBase+testOffset];
|
||||
|
||||
// should we switch order of test and base poly? Might have to even if we
|
||||
// don't want to if there's no splitting to do...
|
||||
// Note: possibility that infinite loop can be introduced here...if that happens,
|
||||
// then we need to split along edges of polys
|
||||
if (doSwitch || (!splitTest && !splitBase))
|
||||
{
|
||||
if (!doSwitch && gBadSpots++ > (mPolyIndexList.size()-mBase)<<1)
|
||||
// got here one too many times...just leave and don't touch poly -- avoid infinite loop
|
||||
return;
|
||||
|
||||
// move test poly to the front of the order
|
||||
dMemmove(&mPolyIndexList[mBase+1],&mPolyIndexList[mBase],testOffset*sizeof(U32));
|
||||
mPolyIndexList[mBase] = testIdx;
|
||||
|
||||
// base poly changed...
|
||||
setBase(mBase);
|
||||
|
||||
if (mBase+testOffset>mMaxTouched)
|
||||
mMaxTouched=mBase+testOffset;
|
||||
|
||||
testOffset=1; // don't need to compare against old base
|
||||
switched=true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (splitBase)
|
||||
{
|
||||
// need one more spot...frontPoly and backPoly replace basePoly
|
||||
setExtents(frontPoly,mPolyExtentsList[mPolyIndexList[mBase]]);
|
||||
mPolyExtentsList.increment();
|
||||
setExtents(backPoly,mPolyExtentsList.last());
|
||||
|
||||
mPolyList[mPolyIndexList[mBase]] = frontPoly;
|
||||
mPolyIndexList.insert(mBase+1);
|
||||
mPolyIndexList[mBase+1] = mPolyList.size();
|
||||
mPolyList.push_back(backPoly);
|
||||
|
||||
// new base poly...
|
||||
setBase(mBase);
|
||||
|
||||
// increase tsetOffset & mMaxTouched because of insertion of back poly
|
||||
testOffset++;
|
||||
mMaxTouched++;
|
||||
|
||||
//
|
||||
switched=false;
|
||||
return;
|
||||
}
|
||||
|
||||
// splitTest -- no other way to get here
|
||||
AssertFatal(splitTest,"DepthSortList::handleOverlap: how did we get here like this?");
|
||||
|
||||
// put frontPoly in front of basePoly, leave backPoly where testPoly was
|
||||
|
||||
// we need one more spot (testPoly broken into front and back)
|
||||
// and we need to shift everything from base up to test down one spot
|
||||
mPolyIndexList.insert(mBase);
|
||||
|
||||
// need one more poly for front poly
|
||||
mPolyIndexList[mBase] = mPolyList.size();
|
||||
mPolyList.push_back(frontPoly);
|
||||
mPolyExtentsList.increment();
|
||||
setExtents(mPolyList.last(),mPolyExtentsList.last());
|
||||
|
||||
// set up back poly
|
||||
mPolyList[testIdx] = backPoly;
|
||||
setExtents(mPolyList[testIdx],mPolyExtentsList[testIdx]);
|
||||
|
||||
// new base poly...
|
||||
setBase(mBase);
|
||||
|
||||
// we inserted an element, increase mMaxTouched...
|
||||
mMaxTouched++;
|
||||
|
||||
testOffset=0;
|
||||
switched = false;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
bool DepthSortList::overlap(Poly * poly1, Poly * poly2)
|
||||
{
|
||||
// check for overlap without any shortcuts
|
||||
S32 sz1 = poly1->vertexCount;
|
||||
S32 sz2 = poly2->vertexCount;
|
||||
|
||||
Point3F * a1, * b1;
|
||||
Point3F * a2, * b2;
|
||||
Point2F norm;
|
||||
F32 dot;
|
||||
b1 = &mVertexList[mIndexList[poly1->vertexStart+sz1-1]].point;
|
||||
S32 i;
|
||||
for (i=0; i<sz1; i++)
|
||||
{
|
||||
a1 = b1;
|
||||
b1 = &mVertexList[mIndexList[poly1->vertexStart+i]].point;
|
||||
|
||||
// get the mid-point of this edge
|
||||
Point3F mid1 = *a1+*b1;
|
||||
mid1 *= 0.5f;
|
||||
bool midOutside = false;
|
||||
|
||||
b2 = &mVertexList[mIndexList[poly2->vertexStart+sz2-1]].point;
|
||||
for (S32 j=0; j<sz2; j++)
|
||||
{
|
||||
a2 = b2;
|
||||
b2 = &mVertexList[mIndexList[poly2->vertexStart+j]].point;
|
||||
|
||||
// test for intersection of a1-b1 and a2-b2 (on x-z plane)
|
||||
|
||||
// they intersect if a1 & b1 are on opp. sides of line a2-b2
|
||||
// and a2 & b2 are on opp. sides of line a1-b1
|
||||
|
||||
norm.set(a2->z - b2->z, b2->x - a2->x); // normal to line a2-b2
|
||||
dot = norm.x * a2->x + norm.y * a2->z; // dot of a2 and norm
|
||||
if (norm.x * mid1.x + norm.y * mid1.z - dot >= 0) // special check for midpoint of line
|
||||
midOutside = true;
|
||||
if ( ((norm.x * a1->x + norm.y * a1->z) - dot) * ((norm.x * b1->x + norm.y * b1->z) - dot) >= 0 )
|
||||
// a1 & b1 are on the same side of the line a2-b2...edges don't overlap
|
||||
continue;
|
||||
|
||||
norm.set(a1->z - b1->z, b1->x - a1->x); // normal to line a1-b1
|
||||
dot = norm.x * a1->x + norm.y * a1->z; // dot of a1 and norm
|
||||
if ( ((norm.x * a2->x + norm.y * a2->z) - dot) * ((norm.x * b2->x + norm.y * b2->z) - dot) >= 0 )
|
||||
// a2 & b2 are on the same side of the line a1-b1...edges don't overlap
|
||||
continue;
|
||||
|
||||
return true; // edges overlap, so polys overlap
|
||||
}
|
||||
if (!midOutside)
|
||||
return true; // midpoint of a1-b1 is inside the poly
|
||||
}
|
||||
|
||||
// edges don't overlap...but one poly might be contained inside the other
|
||||
Point3F center = mVertexList[mIndexList[poly2->vertexStart]].point;
|
||||
for (i=1; i<sz2; i++)
|
||||
center += mVertexList[mIndexList[poly2->vertexStart+i]].point;
|
||||
center *= 1.0f / (F32)poly2->vertexCount;
|
||||
b1 = &mVertexList[mIndexList[poly1->vertexStart+sz1-1]].point;
|
||||
for (i=0; i<sz1; i++)
|
||||
{
|
||||
a1 = b1;
|
||||
b1 = &mVertexList[mIndexList[poly1->vertexStart+i]].point;
|
||||
|
||||
norm.set(a1->z - b1->z, b1->x - a1->x); // normal to line a1-b1
|
||||
dot = norm.x * a1->x + norm.y * a1->z; // dot of a1 and norm
|
||||
if (center.x * norm.x + center.z * norm.y > dot)
|
||||
// center is outside this edge, poly2 is not inside poly1
|
||||
break;
|
||||
}
|
||||
if (i==sz1)
|
||||
return true; // first vert of poly2 inside poly1 (so all of poly2 inside poly1)
|
||||
|
||||
center = mVertexList[mIndexList[poly1->vertexStart]].point;
|
||||
for (i=1; i<sz1; i++)
|
||||
center += mVertexList[mIndexList[poly1->vertexStart+i]].point;
|
||||
center *= 1.0f / (F32)poly1->vertexCount;
|
||||
b2 = &mVertexList[mIndexList[poly2->vertexStart+sz2-1]].point;
|
||||
for (i=0; i<sz2; i++)
|
||||
{
|
||||
a2 = b2;
|
||||
b2 = &mVertexList[mIndexList[poly2->vertexStart+i]].point;
|
||||
|
||||
norm.set(a2->z - b2->z, b2->x - a2->x); // normal to line a2-b2
|
||||
dot = norm.x * a2->x + norm.y * a2->z; // dot of a1 and norm
|
||||
if (center.x * norm.x + center.z * norm.y > dot)
|
||||
// v is outside this edge, poly1 is not inside poly2
|
||||
break;
|
||||
}
|
||||
if (i==sz2)
|
||||
return true; // first vert of poly1 inside poly2 (so all of poly1 inside poly2)
|
||||
|
||||
return false; // we survived, no overlap
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
Vector<U32> frontVerts(__FILE__, __LINE__);
|
||||
Vector<U32> backVerts(__FILE__, __LINE__);
|
||||
|
||||
// Split source poly into front and back. If either front or back is degenerate, don't do anything.
|
||||
// If we have a front and a back, then add the verts to our vertex list and fill out the poly structures.
|
||||
bool DepthSortList::splitPoly(const Poly & src, Point3F & normal, F32 k, Poly & frontPoly, Poly & backPoly)
|
||||
{
|
||||
frontVerts.clear();
|
||||
backVerts.clear();
|
||||
|
||||
// already degenerate...
|
||||
AssertFatal(src.vertexCount>=3,"DepthSortList::splitPoly - Don't need to split a triangle!");
|
||||
|
||||
S32 startSize = mVertexList.size();
|
||||
|
||||
// Assume back and front are degenerate polygons until proven otherwise.
|
||||
bool backDegen = true, frontDegen = true;
|
||||
|
||||
U32 bIdx;
|
||||
Point3F * a, * b;
|
||||
F32 dota, dotb;
|
||||
S32 signA, signB;
|
||||
|
||||
F32 splitTolSq = SPLIT_TOL * SPLIT_TOL * mDot(normal,normal);
|
||||
|
||||
bIdx = mIndexList[src.vertexStart+src.vertexCount-1];
|
||||
b = &mVertexList[bIdx].point;
|
||||
dotb = mDot(normal,*b)-k;
|
||||
|
||||
// Sign variable coded as follows: 1 for outside, 0 on the plane and -1 for inside.
|
||||
if (dotb*dotb > splitTolSq)
|
||||
signB = dotb > 0.0f ? 1 : -1;
|
||||
else
|
||||
signB = 0;
|
||||
|
||||
S32 i;
|
||||
for (i = 0; i<src.vertexCount; i++)
|
||||
{
|
||||
a = b;
|
||||
bIdx = mIndexList[src.vertexStart+i];
|
||||
b = &mVertexList[bIdx].point;
|
||||
dota = dotb;
|
||||
dotb = mDot(normal,*b)-k;
|
||||
signA = signB;
|
||||
if (dotb*dotb > splitTolSq)
|
||||
signB = dotb > 0.0f ? 1 : -1;
|
||||
else
|
||||
signB = 0;
|
||||
|
||||
switch(signA*3 + signB + 4) // +4 is to make values go from 0 up...hopefully enticing compiler to make a jump-table
|
||||
{
|
||||
case 0: // A-, B-
|
||||
case 3: // A., B-
|
||||
backVerts.push_back(bIdx);
|
||||
backDegen = false;
|
||||
break;
|
||||
case 8: // A+, B+
|
||||
case 5: // A., B+
|
||||
frontVerts.push_back(bIdx);
|
||||
frontDegen = false;
|
||||
break;
|
||||
|
||||
case 1: // A-, B.
|
||||
case 4: // A., B.
|
||||
case 7: // A+, B.
|
||||
backVerts.push_back(bIdx);
|
||||
frontVerts.push_back(bIdx);
|
||||
break;
|
||||
|
||||
case 2: // A-, B+
|
||||
{
|
||||
// intersect line A-B with plane
|
||||
F32 dotA = mDot(*a,normal);
|
||||
F32 dotB = mDot(*b,normal);
|
||||
Vertex v;
|
||||
v.point = *a-*b;
|
||||
v.point *= (k-dotB)/(dotA-dotB);
|
||||
v.point += *b;
|
||||
frontVerts.push_back(mVertexList.size());
|
||||
backVerts.push_back(mVertexList.size());
|
||||
frontVerts.push_back(bIdx);
|
||||
mVertexList.push_back(v);
|
||||
b = &mVertexList[bIdx].point; // better get this pointer again since we just incremented vector
|
||||
frontDegen = false;
|
||||
break;
|
||||
}
|
||||
case 6: // A+, B-
|
||||
{
|
||||
// intersect line A-B with plane
|
||||
F32 dotA = mDot(*a,normal);
|
||||
F32 dotB = mDot(*b,normal);
|
||||
Vertex v;
|
||||
v.point = *a-*b;
|
||||
v.point *= (k-dotB)/(dotA-dotB);
|
||||
v.point += *b;
|
||||
frontVerts.push_back(mVertexList.size());
|
||||
backVerts.push_back(mVertexList.size());
|
||||
backVerts.push_back(bIdx);
|
||||
mVertexList.push_back(v);
|
||||
b = &mVertexList[bIdx].point; // better get this pointer again since we just incremented vector
|
||||
backDegen = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for degeneracy.
|
||||
|
||||
if (!ALWAYS_RETURN_FRONT_AND_BACK)
|
||||
{
|
||||
if (frontVerts.size()<3 || backVerts.size()<3 || frontDegen || backDegen)
|
||||
{
|
||||
// didn't need to be split...return two empty polys
|
||||
// and restore vertex list to how it was
|
||||
mVertexList.setSize(startSize);
|
||||
frontPoly.vertexCount = backPoly.vertexCount = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (frontDegen)
|
||||
frontVerts.clear();
|
||||
if (backDegen)
|
||||
backVerts.clear();
|
||||
}
|
||||
|
||||
// front poly
|
||||
frontPoly.plane = src.plane;
|
||||
frontPoly.object = src.object;
|
||||
frontPoly.material = src.material;
|
||||
frontPoly.vertexStart = mIndexList.size();
|
||||
frontPoly.vertexCount = frontVerts.size();
|
||||
frontPoly.surfaceKey = src.surfaceKey;
|
||||
|
||||
// back poly
|
||||
backPoly.plane = src.plane;
|
||||
backPoly.object = src.object;
|
||||
backPoly.material = src.material;
|
||||
backPoly.vertexStart = frontPoly.vertexStart + frontPoly.vertexCount;
|
||||
backPoly.vertexCount = backVerts.size();
|
||||
backPoly.surfaceKey = src.surfaceKey;
|
||||
|
||||
// add indices
|
||||
mIndexList.setSize(backPoly.vertexStart+backPoly.vertexCount);
|
||||
|
||||
if( frontPoly.vertexCount ) {
|
||||
dMemcpy(&mIndexList[frontPoly.vertexStart],
|
||||
frontVerts.address(),
|
||||
sizeof(U32)*frontPoly.vertexCount);
|
||||
}
|
||||
|
||||
if( backPoly.vertexCount ) {
|
||||
dMemcpy(&mIndexList[backPoly.vertexStart],
|
||||
backVerts.address(),
|
||||
sizeof(U32)*backPoly.vertexCount);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
Vector<DepthSortList::Poly> gWorkListA(256);
|
||||
Vector<DepthSortList::Poly> gWorkListB(256);
|
||||
|
||||
void DepthSortList::depthPartition(const Point3F * sourceVerts, U32 numVerts, Vector<Poly> & partition, Vector<Point3F> & partitionVerts)
|
||||
{
|
||||
// create the depth partition of the passed poly
|
||||
// a depth partition is a partition of the poly on the
|
||||
// x-z plane so that each sub-poly in the partition can be
|
||||
// mapped onto exactly one plane in the depth list (i.e.,
|
||||
// those polys found in mPolyIndexList... the ones that are
|
||||
// depth sorted). The plane the sub-polys are mapped onto
|
||||
// is the plane of the closest facing poly.
|
||||
//
|
||||
// y-coord of input polys are ignored, and are remapped
|
||||
// on output to put the output polys on the
|
||||
// corresponding planes.
|
||||
|
||||
// This routine is confusing because there are three lists of polys.
|
||||
//
|
||||
// The source list (passed in as a single poly, but becomes a list as
|
||||
// it is split up) comprises the poly to be partitioned. Verts for sourcePoly
|
||||
// are held in sourceVerts when passed to this routine, but immediately copied
|
||||
// to mVertexList (and indices are added for each vert to mIndexList).
|
||||
//
|
||||
// The scraps list is generated from the source poly (it contains the outside
|
||||
// piece of each cut that is made). Indices for polys in the scraps list are
|
||||
// found in mIndexList and verts are found in mVerts list. Note that the depthPartition
|
||||
// routine will add verts and indices to the member lists, but not polys.
|
||||
//
|
||||
// Finally, the partition list is the end result -- the depth partition. These
|
||||
// polys are not indexed polys. The vertexStart field indexes directly into partitionVerts
|
||||
// array.
|
||||
|
||||
if (mBase<0)
|
||||
// begin the depth sort
|
||||
sortByYExtents();
|
||||
|
||||
// apply cookie cutter to these polys
|
||||
Vector<Poly> * sourceList = &gWorkListA;
|
||||
sourceList->clear();
|
||||
|
||||
// add source poly for to passed verts
|
||||
sourceList->increment();
|
||||
sourceList->last().vertexStart = mIndexList.size();
|
||||
sourceList->last().vertexCount = numVerts;
|
||||
|
||||
// add verts of source poly to mVertexList and mIndexList
|
||||
mVertexList.setSize(mVertexList.size()+numVerts);
|
||||
mIndexList.setSize(mIndexList.size()+numVerts);
|
||||
for (S32 v=0; v<numVerts; v++)
|
||||
{
|
||||
mVertexList[mVertexList.size()-numVerts+v].point = sourceVerts[v];
|
||||
mIndexList[mIndexList.size()-numVerts+v] = mVertexList.size()-numVerts+v;
|
||||
}
|
||||
|
||||
// put scraps from cookie cutter in this list
|
||||
Vector<Poly> * scraps = &gWorkListB;
|
||||
scraps->clear();
|
||||
|
||||
S32 i=0;
|
||||
while (sourceList->size())
|
||||
{
|
||||
if (i>=mBase && !sortNext())
|
||||
// that's it, no more polys to sort
|
||||
break;
|
||||
AssertFatal(i<=mBase,"DepthSortList::depthPartition - exceeded mBase.");
|
||||
|
||||
// use the topmost poly as the cookie cutter
|
||||
Poly & cutter = mPolyList[mPolyIndexList[i]];
|
||||
S32 startVert = partitionVerts.size();
|
||||
S32 j;
|
||||
for (j=0; j<sourceList->size(); j++)
|
||||
{
|
||||
Poly & toCut = (*sourceList)[j];
|
||||
cookieCutter(cutter,toCut,*scraps,partition,partitionVerts);
|
||||
}
|
||||
|
||||
// project all the new verts onto the cutter's plane
|
||||
AssertFatal(mFabs(cutter.plane.y)>=MIN_Y_DOT,"DepthSortList::depthPartition - below MIN_Y_DOT.");
|
||||
F32 invY = -1.0f / cutter.plane.y;
|
||||
for (j=startVert; j<partitionVerts.size(); j++)
|
||||
partitionVerts[j].y = invY * (cutter.plane.d + cutter.plane.x * partitionVerts[j].x + cutter.plane.z * partitionVerts[j].z);
|
||||
|
||||
sourceList->clear();
|
||||
|
||||
// swap work lists -- scraps become source for next closest poly
|
||||
Vector<Poly> * tmpListPtr = sourceList;
|
||||
sourceList = scraps;
|
||||
scraps = tmpListPtr;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void DepthSortList::cookieCutter(Poly & cutter, Poly & cuttee,
|
||||
Vector<Poly> & scraps, // outsides
|
||||
Vector<Poly> & cookies, Vector<Point3F> & cookieVerts) // insides
|
||||
{
|
||||
// perhaps going too far with the cookie cutter analogy, but...
|
||||
// cutter is used to cut cuttee
|
||||
//
|
||||
// the part that is inside of all cutter edges (on x-z plane)
|
||||
// is put into the "cookie" list, parts that are outside are put
|
||||
// onto the "scraps" list. "scraps" are indexed polys with indices
|
||||
// and vertices in mIndexList and mVertexList. Cookies aren't indexed
|
||||
// and points go into cookieVerts list.
|
||||
|
||||
ALWAYS_RETURN_FRONT_AND_BACK = true; // split routine will return polys even if no split occurs
|
||||
|
||||
// save off current state in case nothing inside all the edges of cutter (i.e., no "cookie")
|
||||
S32 vsStart = cuttee.vertexStart;
|
||||
S32 vcStart = cuttee.vertexCount;
|
||||
S32 milStart = mIndexList.size();
|
||||
S32 mvlStart = mVertexList.size();
|
||||
S32 scStart = scraps.size();
|
||||
|
||||
Point3F a, b;
|
||||
Poly scrap;
|
||||
a = mVertexList[mIndexList[cutter.vertexStart+cutter.vertexCount-1]].point;
|
||||
for (S32 i=0; i<cutter.vertexCount; i++)
|
||||
{
|
||||
b = mVertexList[mIndexList[cutter.vertexStart+i]].point;
|
||||
Point3F n(a.z-b.z,0.0f,b.x-a.x);
|
||||
F32 k = mDot(n,a);
|
||||
splitPoly(cuttee,n,k,scrap,cuttee);
|
||||
if (scrap.vertexCount)
|
||||
scraps.push_back(scrap);
|
||||
if (!cuttee.vertexCount)
|
||||
// cut down to nothing...no need to keep cutting
|
||||
break;
|
||||
a = b;
|
||||
}
|
||||
if (cuttee.vertexCount)
|
||||
{
|
||||
// cuttee is non-degenerate, add it to cookies
|
||||
cookies.push_back(cuttee);
|
||||
cookies.last().vertexStart = cookieVerts.size();
|
||||
for (S32 i=0; i<cuttee.vertexCount; i++)
|
||||
cookieVerts.push_back(mVertexList[mIndexList[cuttee.vertexStart+i]].point);
|
||||
}
|
||||
else
|
||||
{
|
||||
// no cookie -- leave things as they were (except add cuttee to scraps)
|
||||
cuttee.vertexStart = vsStart;
|
||||
cuttee.vertexCount = vcStart;
|
||||
mIndexList.setSize(milStart);
|
||||
mVertexList.setSize(mvlStart);
|
||||
scraps.setSize(scStart);
|
||||
scraps.push_back(cuttee);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
108
engine/collision/depthSortList.h
Executable file
108
engine/collision/depthSortList.h
Executable file
@ -0,0 +1,108 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _DEPTHSORTLIST_H_
|
||||
#define _DEPTHSORTLIST_H_
|
||||
|
||||
#ifndef _CLIPPEDPOLYLIST_H_
|
||||
#include "collision/clippedPolyList.h"
|
||||
#endif
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
class DepthSortList : public ClippedPolyList
|
||||
{
|
||||
typedef ClippedPolyList Parent;
|
||||
public:
|
||||
struct PolyExtents
|
||||
{
|
||||
// extents of poly on each coordinate axis
|
||||
F32 xMin;
|
||||
F32 xMax;
|
||||
F32 yMin;
|
||||
F32 yMax;
|
||||
F32 zMin;
|
||||
F32 zMax;
|
||||
};
|
||||
|
||||
typedef Vector<PolyExtents> PolyExtentsList;
|
||||
typedef Vector<U32> PolyIndexList;
|
||||
|
||||
// Internal data
|
||||
PolyExtentsList mPolyExtentsList;
|
||||
PolyIndexList mPolyIndexList;
|
||||
Point3F mExtent; // dimensions of the sort area
|
||||
S32 mBase; // base position in the list...everything before this is sorted correctly
|
||||
Poly * mBasePoly; // poly currently in base position
|
||||
Point3F * mBaseNormal; // normal of poly currently in base position
|
||||
F32 mBaseDot; // dot of basePoly with baseNormal
|
||||
F32 mBaseYMax; // max y extent of base poly
|
||||
S32 mMaxTouched; // highest index swapped into thus far...y-extents may be improperly sorted before this index
|
||||
PolyExtents * mBaseExtents; // x,y,z extents of basePoly
|
||||
|
||||
// set the base position -- everything before this point should be correctly sorted
|
||||
void setBase(S32);
|
||||
|
||||
// some utilities used for sorting
|
||||
bool splitPoly(const Poly & sourcePoly, Point3F & normal, F32 k, Poly & front, Poly & back);
|
||||
bool overlap(Poly *, Poly *);
|
||||
void handleOverlap(Poly * testPoly, Point3F & testNormal, F32 testDot, S32 & testOffset, bool & switched);
|
||||
void sortByYExtents();
|
||||
void setExtents(Poly &, PolyExtents &);
|
||||
|
||||
// one iteration of the sort routine -- finds a poly to fill current base position
|
||||
bool sortNext();
|
||||
|
||||
// used by depthPartition
|
||||
void cookieCutter(Poly & cutter, Poly & cuttee,
|
||||
Vector<Poly> & scraps,
|
||||
Vector<Poly> & cookies, Vector<Point3F> & cookieVerts);
|
||||
|
||||
void wireCube(const Point3F & size, const Point3F & pos);
|
||||
|
||||
public:
|
||||
|
||||
//
|
||||
DepthSortList();
|
||||
~DepthSortList();
|
||||
void set(const MatrixF & mat, Point3F & extents);
|
||||
void clear();
|
||||
void clearSort();
|
||||
|
||||
// the point of this class
|
||||
void sort();
|
||||
void depthPartition(const Point3F * sourceVerts, U32 numVerts, Vector<Poly> & partition, Vector<Point3F> & partitionVerts);
|
||||
|
||||
// Virtual methods
|
||||
void end();
|
||||
// U32 addPoint(const Point3F& p);
|
||||
// bool isEmpty() const;
|
||||
// void begin(U32 material,U32 surfaceKey);
|
||||
// void plane(U32 v1,U32 v2,U32 v3);
|
||||
// void plane(const PlaneF& p);
|
||||
// void vertex(U32 vi);
|
||||
bool getMapping(MatrixF *, Box3F *);
|
||||
|
||||
// access to the polys in order (note: returned pointers are volatile, may change if polys added).
|
||||
void getOrderedPoly(U32 ith, Poly ** poly, PolyExtents ** polyExtent);
|
||||
|
||||
// unordered access
|
||||
PolyExtents & getExtents(U32 idx) { return mPolyExtentsList[idx]; }
|
||||
Poly & getPoly(U32 idx) { return mPolyList[idx]; }
|
||||
|
||||
//
|
||||
void render();
|
||||
|
||||
static bool renderWithDepth;
|
||||
};
|
||||
|
||||
inline void DepthSortList::getOrderedPoly(U32 ith, Poly ** poly, PolyExtents ** polyExtent)
|
||||
{
|
||||
*poly = &mPolyList[mPolyIndexList[ith]];
|
||||
*polyExtent = &mPolyExtentsList[mPolyIndexList[ith]];
|
||||
}
|
||||
|
||||
#endif
|
267
engine/collision/earlyOutPolyList.cc
Executable file
267
engine/collision/earlyOutPolyList.cc
Executable file
@ -0,0 +1,267 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "dgl/dgl.h"
|
||||
#include "math/mMath.h"
|
||||
#include "console/console.h"
|
||||
#include "collision/earlyOutPolyList.h"
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
EarlyOutPolyList::EarlyOutPolyList()
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION(mPolyList);
|
||||
VECTOR_SET_ASSOCIATION(mVertexList);
|
||||
VECTOR_SET_ASSOCIATION(mIndexList);
|
||||
VECTOR_SET_ASSOCIATION(mPolyPlaneList);
|
||||
VECTOR_SET_ASSOCIATION(mPlaneList);
|
||||
|
||||
mNormal.set(0, 0, 0);
|
||||
mIndexList.reserve(100);
|
||||
|
||||
mEarlyOut = false;
|
||||
}
|
||||
|
||||
EarlyOutPolyList::~EarlyOutPolyList()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void EarlyOutPolyList::clear()
|
||||
{
|
||||
// Only clears internal data
|
||||
mPolyList.clear();
|
||||
mVertexList.clear();
|
||||
mIndexList.clear();
|
||||
mPolyPlaneList.clear();
|
||||
|
||||
mEarlyOut = false;
|
||||
}
|
||||
|
||||
bool EarlyOutPolyList::isEmpty() const
|
||||
{
|
||||
return mEarlyOut == false;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
U32 EarlyOutPolyList::addPoint(const Point3F& p)
|
||||
{
|
||||
if (mEarlyOut == true)
|
||||
return 0;
|
||||
|
||||
mVertexList.increment();
|
||||
Vertex& v = mVertexList.last();
|
||||
v.point.x = p.x * mScale.x;
|
||||
v.point.y = p.y * mScale.y;
|
||||
v.point.z = p.z * mScale.z;
|
||||
mMatrix.mulP(v.point);
|
||||
|
||||
// Build the plane mask
|
||||
v.mask = 0;
|
||||
for (U32 i = 0; i < mPlaneList.size(); i++)
|
||||
if (mPlaneList[i].distToPlane(v.point) > 0)
|
||||
v.mask |= 1 << i;
|
||||
|
||||
// If the point is inside all the planes, then we're done!
|
||||
if (v.mask == 0)
|
||||
mEarlyOut = true;
|
||||
|
||||
return mVertexList.size() - 1;
|
||||
}
|
||||
|
||||
|
||||
U32 EarlyOutPolyList::addPlane(const PlaneF& plane)
|
||||
{
|
||||
mPolyPlaneList.increment();
|
||||
mPlaneTransformer.transform(plane, mPolyPlaneList.last());
|
||||
|
||||
return mPolyPlaneList.size() - 1;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void EarlyOutPolyList::begin(U32 material,U32 surfaceKey)
|
||||
{
|
||||
if (mEarlyOut == true)
|
||||
return;
|
||||
|
||||
mPolyList.increment();
|
||||
Poly& poly = mPolyList.last();
|
||||
poly.object = mCurrObject;
|
||||
poly.material = material;
|
||||
poly.vertexStart = mIndexList.size();
|
||||
poly.surfaceKey = surfaceKey;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void EarlyOutPolyList::plane(U32 v1,U32 v2,U32 v3)
|
||||
{
|
||||
if (mEarlyOut == true)
|
||||
return;
|
||||
|
||||
mPolyList.last().plane.set(mVertexList[v1].point,
|
||||
mVertexList[v2].point,mVertexList[v3].point);
|
||||
}
|
||||
|
||||
void EarlyOutPolyList::plane(const PlaneF& p)
|
||||
{
|
||||
if (mEarlyOut == true)
|
||||
return;
|
||||
|
||||
mPlaneTransformer.transform(p, mPolyList.last().plane);
|
||||
}
|
||||
|
||||
void EarlyOutPolyList::plane(const U32 index)
|
||||
{
|
||||
if (mEarlyOut == true)
|
||||
return;
|
||||
|
||||
AssertFatal(index < mPolyPlaneList.size(), "Out of bounds index!");
|
||||
mPolyList.last().plane = mPolyPlaneList[index];
|
||||
}
|
||||
|
||||
const PlaneF& EarlyOutPolyList::getIndexedPlane(const U32 index)
|
||||
{
|
||||
AssertFatal(index < mPolyPlaneList.size(), "Out of bounds index!");
|
||||
return mPolyPlaneList[index];
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void EarlyOutPolyList::vertex(U32 vi)
|
||||
{
|
||||
if (mEarlyOut == true)
|
||||
return;
|
||||
|
||||
mIndexList.push_back(vi);
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void EarlyOutPolyList::end()
|
||||
{
|
||||
if (mEarlyOut == true)
|
||||
return;
|
||||
|
||||
Poly& poly = mPolyList.last();
|
||||
|
||||
// Anything facing away from the mNormal is rejected
|
||||
if (mDot(poly.plane,mNormal) > 0) {
|
||||
mIndexList.setSize(poly.vertexStart);
|
||||
mPolyList.decrement();
|
||||
return;
|
||||
}
|
||||
|
||||
// Build intial inside/outside plane masks
|
||||
U32 indexStart = poly.vertexStart;
|
||||
U32 vertexCount = mIndexList.size() - indexStart;
|
||||
|
||||
U32 frontMask = 0,backMask = 0;
|
||||
U32 i;
|
||||
for (i = indexStart; i < mIndexList.size(); i++) {
|
||||
U32 mask = mVertexList[mIndexList[i]].mask;
|
||||
frontMask |= mask;
|
||||
backMask |= ~mask;
|
||||
}
|
||||
|
||||
// Trivial accept if all the vertices are on the backsides of
|
||||
// all the planes.
|
||||
if (!frontMask) {
|
||||
poly.vertexCount = vertexCount;
|
||||
mEarlyOut = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Trivial reject if any plane not crossed has all it's points
|
||||
// on the front.
|
||||
U32 crossMask = frontMask & backMask;
|
||||
if (~crossMask & frontMask) {
|
||||
mIndexList.setSize(poly.vertexStart);
|
||||
mPolyList.decrement();
|
||||
return;
|
||||
}
|
||||
|
||||
// Need to do some clipping
|
||||
for (U32 p = 0; p < mPlaneList.size(); p++) {
|
||||
U32 pmask = 1 << p;
|
||||
// Only test against this plane if we have something
|
||||
// on both sides
|
||||
if (crossMask & pmask) {
|
||||
U32 indexEnd = mIndexList.size();
|
||||
U32 i1 = indexEnd - 1;
|
||||
U32 mask1 = mVertexList[mIndexList[i1]].mask;
|
||||
|
||||
for (U32 i2 = indexStart; i2 < indexEnd; i2++) {
|
||||
U32 mask2 = mVertexList[mIndexList[i2]].mask;
|
||||
if ((mask1 ^ mask2) & pmask) {
|
||||
//
|
||||
mVertexList.increment();
|
||||
VectorF& v1 = mVertexList[mIndexList[i1]].point;
|
||||
VectorF& v2 = mVertexList[mIndexList[i2]].point;
|
||||
VectorF vv = v2 - v1;
|
||||
F32 t = -mPlaneList[p].distToPlane(v1) / mDot(mPlaneList[p],vv);
|
||||
|
||||
mIndexList.push_back(mVertexList.size() - 1);
|
||||
Vertex& iv = mVertexList.last();
|
||||
iv.point.x = v1.x + vv.x * t;
|
||||
iv.point.y = v1.y + vv.y * t;
|
||||
iv.point.z = v1.z + vv.z * t;
|
||||
iv.mask = 0;
|
||||
|
||||
// Test against the remaining planes
|
||||
for (U32 i = p + 1; i < mPlaneList.size(); i++)
|
||||
if (mPlaneList[i].distToPlane(iv.point) > 0) {
|
||||
iv.mask = 1 << i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!(mask2 & pmask)) {
|
||||
U32 index = mIndexList[i2];
|
||||
mIndexList.push_back(index);
|
||||
}
|
||||
mask1 = mask2;
|
||||
i1 = i2;
|
||||
}
|
||||
|
||||
// Check for degenerate
|
||||
indexStart = indexEnd;
|
||||
if (mIndexList.size() - indexStart < 3) {
|
||||
mIndexList.setSize(poly.vertexStart);
|
||||
mPolyList.decrement();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we reach here, then there's a poly!
|
||||
mEarlyOut = true;
|
||||
|
||||
// Emit what's left and compress the index list.
|
||||
poly.vertexCount = mIndexList.size() - indexStart;
|
||||
memcpy(&mIndexList[poly.vertexStart],
|
||||
&mIndexList[indexStart],poly.vertexCount);
|
||||
mIndexList.setSize(poly.vertexStart + poly.vertexCount);
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void EarlyOutPolyList::memcpy(U32* dst, U32* src,U32 size)
|
||||
{
|
||||
U32* end = src + size;
|
||||
while (src != end)
|
||||
*dst++ = *src++;
|
||||
}
|
||||
|
80
engine/collision/earlyOutPolyList.h
Executable file
80
engine/collision/earlyOutPolyList.h
Executable file
@ -0,0 +1,80 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _EARLYOUTPOLYLIST_H_
|
||||
#define _EARLYOUTPOLYLIST_H_
|
||||
|
||||
#ifndef _ABSTRACTPOLYLIST_H_
|
||||
#include "collision/abstractPolyList.h"
|
||||
#endif
|
||||
|
||||
|
||||
/// Early out check PolyList
|
||||
///
|
||||
/// This class is used primarily for triggers and similar checks. It checks to see
|
||||
/// if any of the geometry you feed it is inside its area, and if it is, it stops
|
||||
/// checking for any more data and returns a true value. This is good if you want
|
||||
/// to know if anything is in your "trigger" area, for instance.
|
||||
///
|
||||
/// @see AbstractPolyList
|
||||
class EarlyOutPolyList : public AbstractPolyList
|
||||
{
|
||||
void memcpy(U32* d, U32* s,U32 size);
|
||||
|
||||
// Internal data
|
||||
struct Vertex {
|
||||
Point3F point;
|
||||
U32 mask;
|
||||
};
|
||||
|
||||
struct Poly {
|
||||
PlaneF plane;
|
||||
SceneObject* object;
|
||||
U32 material;
|
||||
U32 vertexStart;
|
||||
U32 vertexCount;
|
||||
U32 surfaceKey;
|
||||
};
|
||||
|
||||
public:
|
||||
typedef Vector<PlaneF> PlaneList;
|
||||
private:
|
||||
typedef Vector<Vertex> VertexList;
|
||||
typedef Vector<Poly> PolyList;
|
||||
typedef Vector<U32> IndexList;
|
||||
|
||||
PolyList mPolyList;
|
||||
VertexList mVertexList;
|
||||
IndexList mIndexList;
|
||||
bool mEarlyOut;
|
||||
|
||||
PlaneList mPolyPlaneList;
|
||||
|
||||
public:
|
||||
// Data set by caller
|
||||
PlaneList mPlaneList;
|
||||
VectorF mNormal;
|
||||
|
||||
public:
|
||||
EarlyOutPolyList();
|
||||
~EarlyOutPolyList();
|
||||
void clear();
|
||||
|
||||
// Virtual methods
|
||||
bool isEmpty() const;
|
||||
U32 addPoint(const Point3F& p);
|
||||
U32 addPlane(const PlaneF& plane);
|
||||
void begin(U32 material,U32 surfaceKey);
|
||||
void plane(U32 v1,U32 v2,U32 v3);
|
||||
void plane(const PlaneF& p);
|
||||
void plane(const U32 index);
|
||||
void vertex(U32 vi);
|
||||
void end();
|
||||
|
||||
protected:
|
||||
const PlaneF& getIndexedPlane(const U32 index);
|
||||
};
|
||||
|
||||
#endif // _H_EARLYOUTPOLYLIST_
|
492
engine/collision/extrudedPolyList.cc
Executable file
492
engine/collision/extrudedPolyList.cc
Executable file
@ -0,0 +1,492 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "dgl/dgl.h"
|
||||
#include "math/mMath.h"
|
||||
#include "console/console.h"
|
||||
#include "collision/extrudedPolyList.h"
|
||||
#include "collision/polyhedron.h"
|
||||
#include "collision/collision.h"
|
||||
|
||||
|
||||
const F32 sgFrontEpsilon = 0.01;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
// use table for U64 shifts of the form: 1 << N
|
||||
// because compiler makes it a function call if done directly...
|
||||
U32 U32leftShift[33] =
|
||||
{
|
||||
U32(1) << 0,
|
||||
U32(1) << 1,
|
||||
U32(1) << 2,
|
||||
U32(1) << 3,
|
||||
U32(1) << 4,
|
||||
U32(1) << 5,
|
||||
U32(1) << 6,
|
||||
U32(1) << 7,
|
||||
U32(1) << 8,
|
||||
U32(1) << 9,
|
||||
|
||||
U32(1) << 10,
|
||||
U32(1) << 11,
|
||||
U32(1) << 12,
|
||||
U32(1) << 13,
|
||||
U32(1) << 14,
|
||||
U32(1) << 15,
|
||||
U32(1) << 16,
|
||||
U32(1) << 17,
|
||||
U32(1) << 18,
|
||||
U32(1) << 19,
|
||||
|
||||
U32(1) << 20,
|
||||
U32(1) << 21,
|
||||
U32(1) << 22,
|
||||
U32(1) << 23,
|
||||
U32(1) << 24,
|
||||
U32(1) << 25,
|
||||
U32(1) << 26,
|
||||
U32(1) << 27,
|
||||
U32(1) << 28,
|
||||
U32(1) << 29,
|
||||
|
||||
U32(1) << 30,
|
||||
U32(1) << 31,
|
||||
U32(0) // one more for good measure
|
||||
};
|
||||
|
||||
// Minimum distance from a face
|
||||
F32 ExtrudedPolyList::FaceEpsilon = 0.01;
|
||||
|
||||
// Value used to compare collision times
|
||||
F32 ExtrudedPolyList::EqualEpsilon = 0.0001;
|
||||
|
||||
ExtrudedPolyList::ExtrudedPolyList()
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION(mVertexList);
|
||||
VECTOR_SET_ASSOCIATION(mIndexList);
|
||||
VECTOR_SET_ASSOCIATION(mExtrudedList);
|
||||
VECTOR_SET_ASSOCIATION(mPlaneList);
|
||||
VECTOR_SET_ASSOCIATION(mPolyPlaneList);
|
||||
|
||||
mVelocity.set(0,0,0);
|
||||
mIndexList.reserve(128);
|
||||
mVertexList.reserve(64);
|
||||
mPolyPlaneList.reserve(64);
|
||||
mPlaneList.reserve(64);
|
||||
mCollisionList = 0;
|
||||
}
|
||||
|
||||
ExtrudedPolyList::~ExtrudedPolyList()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
bool ExtrudedPolyList::isEmpty() const
|
||||
{
|
||||
return mCollisionList->count == 0;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void ExtrudedPolyList::extrude(const Polyhedron& pt, const VectorF& vector)
|
||||
{
|
||||
// Clear state
|
||||
mIndexList.clear();
|
||||
mVertexList.clear();
|
||||
mPlaneList.clear();
|
||||
mPolyPlaneList.clear();
|
||||
mFaceShift = 0;
|
||||
|
||||
// Determine which faces will be extruded.
|
||||
mExtrudedList.setSize(pt.planeList.size());
|
||||
for (U32 f = 0; f < pt.planeList.size(); f++) {
|
||||
const PlaneF& face = pt.planeList[f];
|
||||
ExtrudedFace& eface = mExtrudedList[f];
|
||||
F32 dot = mDot(face,vector);
|
||||
eface.active = dot > EqualEpsilon;
|
||||
if (eface.active) {
|
||||
eface.faceShift = FaceEpsilon / dot;
|
||||
eface.maxDistance = dot;
|
||||
eface.plane = face;
|
||||
eface.planeMask = U32leftShift[mPlaneList.size()]; // U64(1) << mPlaneList.size();
|
||||
|
||||
// Add the face as a plane to clip against.
|
||||
mPlaneList.increment(2);
|
||||
PlaneF* plane = mPlaneList.end() - 2;
|
||||
plane[0] = plane[1] = face;
|
||||
plane[0].invert();
|
||||
}
|
||||
}
|
||||
|
||||
// Produce extruded planes for bounding and internal edges
|
||||
for (U32 e = 0; e < pt.edgeList.size(); e++) {
|
||||
Polyhedron::Edge const& edge = pt.edgeList[e];
|
||||
ExtrudedFace& ef1 = mExtrudedList[edge.face[0]];
|
||||
ExtrudedFace& ef2 = mExtrudedList[edge.face[1]];
|
||||
if (ef1.active || ef2.active) {
|
||||
|
||||
// Assumes that the edge points are clockwise
|
||||
// for face[0].
|
||||
const Point3F& p1 = pt.pointList[edge.vertex[1]];
|
||||
const Point3F &p2 = pt.pointList[edge.vertex[0]];
|
||||
Point3F p3 = p2 + vector;
|
||||
|
||||
mPlaneList.increment(2);
|
||||
PlaneF* plane = mPlaneList.end() - 2;
|
||||
plane[0].set(p3,p2,p1);
|
||||
plane[1] = plane[0];
|
||||
plane[1].invert();
|
||||
|
||||
U32 pmask = U32leftShift[mPlaneList.size()-2]; // U64(1) << (mPlaneList.size()-2)
|
||||
ef1.planeMask |= pmask;
|
||||
ef2.planeMask |= pmask << 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void ExtrudedPolyList::setCollisionList(CollisionList* info)
|
||||
{
|
||||
mCollisionList = info;
|
||||
mCollisionList->count = 0;
|
||||
mCollisionList->t = 2;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void ExtrudedPolyList::adjustCollisionTime()
|
||||
{
|
||||
// Called after all the polys have been added.
|
||||
// There was a reason for doing it here instead of subtracting
|
||||
// the face Epsilon (faceShift) when the closest point is calculated,
|
||||
// but I can't remember what it is...
|
||||
if (mCollisionList->count) {
|
||||
mCollisionList->t -= mFaceShift;
|
||||
if (mCollisionList->t > 1)
|
||||
mCollisionList->t = 1;
|
||||
else
|
||||
if (mCollisionList->t < 0)
|
||||
mCollisionList->t = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
U32 ExtrudedPolyList::addPoint(const Point3F& p)
|
||||
{
|
||||
mVertexList.increment();
|
||||
Vertex& v = mVertexList.last();
|
||||
|
||||
v.point.x = p.x * mScale.x;
|
||||
v.point.y = p.y * mScale.y;
|
||||
v.point.z = p.z * mScale.z;
|
||||
mMatrix.mulP(v.point);
|
||||
|
||||
// Build the plane mask, planes come in pairs
|
||||
v.mask = 0;
|
||||
for (U32 i = 0; i < mPlaneList.size(); i += 2)
|
||||
{
|
||||
F32 dist = mPlaneList[i].distToPlane(v.point);
|
||||
if (dist >= sgFrontEpsilon)
|
||||
v.mask |= U32leftShift[i]; // U64(1) << i;
|
||||
else
|
||||
v.mask |= U32leftShift[i+1]; // U64(2) << i;
|
||||
}
|
||||
return mVertexList.size() - 1;
|
||||
}
|
||||
|
||||
|
||||
U32 ExtrudedPolyList::addPlane(const PlaneF& plane)
|
||||
{
|
||||
mPolyPlaneList.increment();
|
||||
mPlaneTransformer.transform(plane, mPolyPlaneList.last());
|
||||
|
||||
return mPolyPlaneList.size() - 1;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void ExtrudedPolyList::begin(U32 material, U32 /*surfaceKey*/)
|
||||
{
|
||||
mPoly.object = mCurrObject;
|
||||
mPoly.material = material;
|
||||
mIndexList.clear();
|
||||
}
|
||||
|
||||
void ExtrudedPolyList::plane(U32 v1, U32 v2, U32 v3)
|
||||
{
|
||||
mPoly.plane.set(mVertexList[v1].point,
|
||||
mVertexList[v2].point,
|
||||
mVertexList[v3].point);
|
||||
}
|
||||
|
||||
void ExtrudedPolyList::plane(const PlaneF& p)
|
||||
{
|
||||
mPlaneTransformer.transform(p, mPoly.plane);
|
||||
}
|
||||
|
||||
void ExtrudedPolyList::plane(const U32 index)
|
||||
{
|
||||
AssertFatal(index < mPolyPlaneList.size(), "Out of bounds index!");
|
||||
mPoly.plane = mPolyPlaneList[index];
|
||||
}
|
||||
|
||||
const PlaneF& ExtrudedPolyList::getIndexedPlane(const U32 index)
|
||||
{
|
||||
AssertFatal(index < mPolyPlaneList.size(), "Out of bounds index!");
|
||||
return mPolyPlaneList[index];
|
||||
}
|
||||
|
||||
|
||||
void ExtrudedPolyList::vertex(U32 vi)
|
||||
{
|
||||
mIndexList.push_back(vi);
|
||||
}
|
||||
|
||||
void ExtrudedPolyList::end()
|
||||
{
|
||||
// Anything facing away from the mVelocity is rejected
|
||||
if (mCollisionList->count >= CollisionList::MaxCollisions ||
|
||||
mDot(mPoly.plane, mNormalVelocity) > 0)
|
||||
return;
|
||||
|
||||
// Test the mPoly against the planes each extruded face.
|
||||
U32 cFaceCount = 0;
|
||||
ExtrudedFace* cFace[30];
|
||||
bool cEdgeColl[30];
|
||||
ExtrudedFace* face = mExtrudedList.begin();
|
||||
ExtrudedFace* end = mExtrudedList.end();
|
||||
for (; face != end; face++)
|
||||
{
|
||||
if (face->active && (face->faceDot = -mDot(face->plane,mPoly.plane)) > 0)
|
||||
{
|
||||
if (testPoly(*face)) {
|
||||
cFace[cFaceCount] = face;
|
||||
cEdgeColl[cFaceCount++] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!cFaceCount)
|
||||
{
|
||||
face = mExtrudedList.begin();
|
||||
end = mExtrudedList.end();
|
||||
for (; face != end; face++)
|
||||
{
|
||||
if (face->active && (face->faceDot = -mDot(face->plane,mPoly.plane)) <= 0)
|
||||
{
|
||||
if (testPoly(*face)) {
|
||||
cFace[cFaceCount] = face;
|
||||
cEdgeColl[cFaceCount++] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!cFaceCount)
|
||||
return;
|
||||
|
||||
// Pick the best collision face
|
||||
face = cFace[0];
|
||||
bool edge = cEdgeColl[0];
|
||||
for (U32 f = 1; f < cFaceCount; f++)
|
||||
{
|
||||
if (cFace[f]->faceDot > face->faceDot)
|
||||
{
|
||||
face = cFace[f];
|
||||
edge = cEdgeColl[f];
|
||||
}
|
||||
}
|
||||
// Add it to the collision list if it's better and/or equal
|
||||
// to our current best.
|
||||
if (face->time <= mCollisionList->t + EqualEpsilon) {
|
||||
if (face->time < mCollisionList->t - EqualEpsilon) {
|
||||
mFaceShift = face->faceShift;
|
||||
mCollisionList->t = face->time;
|
||||
mCollisionList->count = 0;
|
||||
mCollisionList->maxHeight = face->height;
|
||||
}
|
||||
else {
|
||||
if (face->height > mCollisionList->maxHeight)
|
||||
mCollisionList->maxHeight = face->height;
|
||||
if (face->faceShift > mFaceShift)
|
||||
mFaceShift = face->faceShift;
|
||||
}
|
||||
Collision& collision = mCollisionList->collision[mCollisionList->count++];
|
||||
collision.point = face->point;
|
||||
collision.faceDot = face->faceDot;
|
||||
collision.face = face - mExtrudedList.begin();
|
||||
collision.object = mPoly.object;
|
||||
collision.normal = mPoly.plane;
|
||||
collision.material = mPoly.material;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
bool ExtrudedPolyList::testPoly(ExtrudedFace& face)
|
||||
{
|
||||
// Build intial inside/outside plane masks
|
||||
U32 indexStart = 0;
|
||||
U32 indexEnd = mIndexList.size();
|
||||
U32 oVertexSize = mVertexList.size();
|
||||
U32 oIndexSize = mIndexList.size();
|
||||
|
||||
U32 frontMask = 0,backMask = 0;
|
||||
for (U32 i = indexStart; i < indexEnd; i++) {
|
||||
U32 mask = mVertexList[mIndexList[i]].mask & face.planeMask;
|
||||
frontMask |= mask;
|
||||
backMask |= ~mask;
|
||||
}
|
||||
|
||||
// Clip the mPoly against the planes that bound the face...
|
||||
// Trivial accept if all the vertices are on the backsides of
|
||||
// all the planes.
|
||||
if (frontMask) {
|
||||
// Trivial reject if any plane not crossed has all it's points
|
||||
// on the front.
|
||||
U32 crossMask = frontMask & backMask;
|
||||
if (~crossMask & frontMask)
|
||||
return false;
|
||||
|
||||
// Need to do some clipping
|
||||
for (U32 p = 0; p < mPlaneList.size(); p++) {
|
||||
U32 pmask = U32leftShift[p]; // U64(1) << p;
|
||||
U32 newStart = mIndexList.size();
|
||||
|
||||
// Only test against this plane if we have something
|
||||
// on both sides
|
||||
if (face.planeMask & crossMask & pmask) {
|
||||
U32 i1 = indexEnd - 1;
|
||||
U32 mask1 = mVertexList[mIndexList[i1]].mask;
|
||||
|
||||
for (U32 i2 = indexStart; i2 < indexEnd; i2++) {
|
||||
U32 mask2 = mVertexList[mIndexList[i2]].mask;
|
||||
if ((mask1 ^ mask2) & pmask) {
|
||||
//
|
||||
mVertexList.increment();
|
||||
VectorF& v1 = mVertexList[mIndexList[i1]].point;
|
||||
VectorF& v2 = mVertexList[mIndexList[i2]].point;
|
||||
VectorF vv = v2 - v1;
|
||||
F32 t = -mPlaneList[p].distToPlane(v1) / mDot(mPlaneList[p],vv);
|
||||
|
||||
mIndexList.push_back(mVertexList.size() - 1);
|
||||
Vertex& iv = mVertexList.last();
|
||||
iv.point.x = v1.x + vv.x * t;
|
||||
iv.point.y = v1.y + vv.y * t;
|
||||
iv.point.z = v1.z + vv.z * t;
|
||||
iv.mask = 0;
|
||||
|
||||
// Test against the remaining planes
|
||||
for (U32 i = p + 1; i < mPlaneList.size(); i++) {
|
||||
U32 mask = U32leftShift[i]; // U64(1) << i;
|
||||
if (face.planeMask & mask &&
|
||||
mPlaneList[i].distToPlane(iv.point) > 0) {
|
||||
iv.mask = mask;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!(mask2 & pmask)) {
|
||||
U32 index = mIndexList[i2];
|
||||
mIndexList.push_back(index);
|
||||
}
|
||||
mask1 = mask2;
|
||||
i1 = i2;
|
||||
}
|
||||
|
||||
// Check for degenerate
|
||||
indexStart = newStart;
|
||||
indexEnd = mIndexList.size();
|
||||
if (mIndexList.size() - indexStart < 3) {
|
||||
mVertexList.setSize(oVertexSize);
|
||||
mIndexList.setSize(oIndexSize);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find closest point on the mPoly
|
||||
Point3F bp;
|
||||
F32 bd = 1E30;
|
||||
F32 height = -1E30;
|
||||
for (U32 b = indexStart; b < indexEnd; b++) {
|
||||
Vertex& vertex = mVertexList[mIndexList[b]];
|
||||
F32 dist = face.plane.distToPlane(vertex.point);
|
||||
if (dist <= bd) {
|
||||
bd = (dist < 0)? 0: dist;
|
||||
bp = vertex.point;
|
||||
}
|
||||
// Since we don't clip against the back plane, we'll
|
||||
// only include vertex heights that are within range.
|
||||
if (vertex.point.z > height && dist < face.maxDistance)
|
||||
height = vertex.point.z;
|
||||
}
|
||||
|
||||
// Remove temporary data
|
||||
mVertexList.setSize(oVertexSize);
|
||||
mIndexList.setSize(oIndexSize);
|
||||
|
||||
// Add it to the collision list if it's better and/or equal
|
||||
// to our current best.
|
||||
if (bd < face.maxDistance + FaceEpsilon) {
|
||||
face.time = bd / face.maxDistance;
|
||||
face.height = height;
|
||||
face.point = bp;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void ExtrudedPolyList::render()
|
||||
{
|
||||
if (!mCollisionList)
|
||||
return;
|
||||
|
||||
glBegin(GL_LINES);
|
||||
glColor3f(1,1,0);
|
||||
|
||||
for (U32 d = 0; d < mCollisionList->count; d++) {
|
||||
Collision& face = mCollisionList->collision[d];
|
||||
Point3F ep = face.point;
|
||||
ep += face.normal;
|
||||
glVertex3fv(face.point);
|
||||
glVertex3fv(ep);
|
||||
}
|
||||
|
||||
glEnd();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
void ExtrudedPolyList::setVelocity(const VectorF& velocity)
|
||||
{
|
||||
mVelocity = velocity;
|
||||
if (velocity.isZero() == false)
|
||||
{
|
||||
mNormalVelocity = velocity;
|
||||
mNormalVelocity.normalize();
|
||||
setInterestNormal(mNormalVelocity);
|
||||
}
|
||||
else
|
||||
{
|
||||
mNormalVelocity.set(0, 0, 0);
|
||||
clearInterestNormal();
|
||||
}
|
||||
}
|
||||
|
||||
|
107
engine/collision/extrudedPolyList.h
Executable file
107
engine/collision/extrudedPolyList.h
Executable file
@ -0,0 +1,107 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _EXTRUDEDPOLYLIST_H_
|
||||
#define _EXTRUDEDPOLYLIST_H_
|
||||
|
||||
#ifndef _MMATH_H_
|
||||
#include "math/mMath.h"
|
||||
#endif
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/tVector.h"
|
||||
#endif
|
||||
#ifndef _ABSTRACTPOLYLIST_H_
|
||||
#include "collision/abstractPolyList.h"
|
||||
#endif
|
||||
|
||||
struct Polyhedron;
|
||||
struct CollisionList;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// Extruded Polytope PolyList
|
||||
///
|
||||
/// This class is used primarily for collision detection, by objects which need
|
||||
/// to check for obstructions along their path. You feed it a polytope to
|
||||
/// extrude along the direction of movement, and it gives you a list of collisions.
|
||||
///
|
||||
/// @see AbstractPolyList
|
||||
class ExtrudedPolyList: public AbstractPolyList
|
||||
{
|
||||
public:
|
||||
struct Vertex {
|
||||
Point3F point;
|
||||
U32 mask;
|
||||
};
|
||||
|
||||
struct Poly {
|
||||
PlaneF plane;
|
||||
SceneObject* object;
|
||||
U32 material;
|
||||
};
|
||||
|
||||
struct ExtrudedFace {
|
||||
bool active;
|
||||
PlaneF plane;
|
||||
F32 maxDistance;
|
||||
U32 planeMask;
|
||||
F32 faceDot;
|
||||
F32 faceShift;
|
||||
F32 time;
|
||||
Point3F point;
|
||||
F32 height;
|
||||
};
|
||||
|
||||
typedef Vector<ExtrudedFace> ExtrudedList;
|
||||
typedef Vector<PlaneF> PlaneList;
|
||||
typedef Vector<Vertex> VertexList;
|
||||
typedef Vector<U32> IndexList;
|
||||
|
||||
static F32 EqualEpsilon;
|
||||
static F32 FaceEpsilon;
|
||||
|
||||
// Internal data
|
||||
VertexList mVertexList;
|
||||
IndexList mIndexList;
|
||||
ExtrudedList mExtrudedList;
|
||||
PlaneList mPlaneList;
|
||||
VectorF mVelocity;
|
||||
VectorF mNormalVelocity;
|
||||
F32 mFaceShift;
|
||||
Poly mPoly;
|
||||
|
||||
// Returned info
|
||||
CollisionList* mCollisionList;
|
||||
|
||||
PlaneList mPolyPlaneList;
|
||||
|
||||
//
|
||||
private:
|
||||
bool testPoly(ExtrudedFace&);
|
||||
|
||||
public:
|
||||
ExtrudedPolyList();
|
||||
~ExtrudedPolyList();
|
||||
void extrude(const Polyhedron&, const VectorF& vec);
|
||||
void setVelocity(const VectorF& velocity);
|
||||
void setCollisionList(CollisionList*);
|
||||
void adjustCollisionTime();
|
||||
void render();
|
||||
|
||||
// Virtual methods
|
||||
bool isEmpty() const;
|
||||
U32 addPoint(const Point3F& p);
|
||||
U32 addPlane(const PlaneF& plane);
|
||||
void begin(U32 material, U32 surfaceKey);
|
||||
void plane(U32 v1,U32 v2,U32 v3);
|
||||
void plane(const PlaneF& p);
|
||||
void plane(const U32 index);
|
||||
void vertex(U32 vi);
|
||||
void end();
|
||||
|
||||
protected:
|
||||
const PlaneF& getIndexedPlane(const U32 index);
|
||||
};
|
||||
|
||||
#endif
|
422
engine/collision/gjk.cc
Executable file
422
engine/collision/gjk.cc
Executable file
@ -0,0 +1,422 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//
|
||||
// The core algorithms in this file are based on code written
|
||||
// by G. van den Bergen for his interference detection library,
|
||||
// "SOLID 2.0"
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "dgl/dgl.h"
|
||||
#include "core/dataChunker.h"
|
||||
#include "collision/collision.h"
|
||||
#include "sceneGraph/sceneGraph.h"
|
||||
#include "sim/sceneObject.h"
|
||||
#include "terrain/terrData.h"
|
||||
#include "collision/convex.h"
|
||||
#include "collision/gjk.h"
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
static F32 rel_error = 1E-5; // relative error in the computed distance
|
||||
static F32 sTolerance = 1E-3; // Distance tolerance
|
||||
static F32 sEpsilon2 = 1E-20; // Zero length vector
|
||||
static U32 sIteration = 15; // Stuck in a loop?
|
||||
|
||||
S32 num_iterations = 0;
|
||||
S32 num_irregularities = 0;
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
GjkCollisionState::GjkCollisionState()
|
||||
{
|
||||
a = b = 0;
|
||||
}
|
||||
|
||||
GjkCollisionState::~GjkCollisionState()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void GjkCollisionState::swap()
|
||||
{
|
||||
Convex* t = a; a = b; b = t;
|
||||
CollisionStateList* l = mLista; mLista = mListb; mListb = l;
|
||||
v.neg();
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void GjkCollisionState::compute_det()
|
||||
{
|
||||
// Dot new point with current set
|
||||
for (int i = 0, bit = 1; i < 4; ++i, bit <<=1)
|
||||
if (bits & bit)
|
||||
dp[i][last] = dp[last][i] = mDot(y[i], y[last]);
|
||||
dp[last][last] = mDot(y[last], y[last]);
|
||||
|
||||
// Calulate the determinent
|
||||
det[last_bit][last] = 1;
|
||||
for (int j = 0, sj = 1; j < 4; ++j, sj <<= 1) {
|
||||
if (bits & sj) {
|
||||
int s2 = sj | last_bit;
|
||||
det[s2][j] = dp[last][last] - dp[last][j];
|
||||
det[s2][last] = dp[j][j] - dp[j][last];
|
||||
for (int k = 0, sk = 1; k < j; ++k, sk <<= 1) {
|
||||
if (bits & sk) {
|
||||
int s3 = sk | s2;
|
||||
det[s3][k] = det[s2][j] * (dp[j][j] - dp[j][k]) +
|
||||
det[s2][last] * (dp[last][j] - dp[last][k]);
|
||||
det[s3][j] = det[sk | last_bit][k] * (dp[k][k] - dp[k][j]) +
|
||||
det[sk | last_bit][last] * (dp[last][k] - dp[last][j]);
|
||||
det[s3][last] = det[sk | sj][k] * (dp[k][k] - dp[k][last]) +
|
||||
det[sk | sj][j] * (dp[j][k] - dp[j][last]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (all_bits == 15) {
|
||||
det[15][0] = det[14][1] * (dp[1][1] - dp[1][0]) +
|
||||
det[14][2] * (dp[2][1] - dp[2][0]) +
|
||||
det[14][3] * (dp[3][1] - dp[3][0]);
|
||||
det[15][1] = det[13][0] * (dp[0][0] - dp[0][1]) +
|
||||
det[13][2] * (dp[2][0] - dp[2][1]) +
|
||||
det[13][3] * (dp[3][0] - dp[3][1]);
|
||||
det[15][2] = det[11][0] * (dp[0][0] - dp[0][2]) +
|
||||
det[11][1] * (dp[1][0] - dp[1][2]) +
|
||||
det[11][3] * (dp[3][0] - dp[3][2]);
|
||||
det[15][3] = det[7][0] * (dp[0][0] - dp[0][3]) +
|
||||
det[7][1] * (dp[1][0] - dp[1][3]) +
|
||||
det[7][2] * (dp[2][0] - dp[2][3]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
inline void GjkCollisionState::compute_vector(int bits, VectorF& v)
|
||||
{
|
||||
F32 sum = 0;
|
||||
v.set(0, 0, 0);
|
||||
for (int i = 0, bit = 1; i < 4; ++i, bit <<= 1) {
|
||||
if (bits & bit) {
|
||||
sum += det[bits][i];
|
||||
v += y[i] * det[bits][i];
|
||||
}
|
||||
}
|
||||
v *= 1 / sum;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
inline bool GjkCollisionState::valid(int s)
|
||||
{
|
||||
for (int i = 0, bit = 1; i < 4; ++i, bit <<= 1) {
|
||||
if (all_bits & bit) {
|
||||
if (s & bit) {
|
||||
if (det[s][i] <= 0)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
if (det[s | bit][i] > 0)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
inline bool GjkCollisionState::closest(VectorF& v)
|
||||
{
|
||||
compute_det();
|
||||
for (int s = bits; s; --s) {
|
||||
if ((s & bits) == s) {
|
||||
if (valid(s | last_bit)) {
|
||||
bits = s | last_bit;
|
||||
if (bits != 15)
|
||||
compute_vector(bits, v);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (valid(last_bit)) {
|
||||
bits = last_bit;
|
||||
v = y[last];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
inline bool GjkCollisionState::degenerate(const VectorF& w)
|
||||
{
|
||||
for (int i = 0, bit = 1; i < 4; ++i, bit <<= 1)
|
||||
if ((all_bits & bit) && y[i] == w)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
inline void GjkCollisionState::nextBit()
|
||||
{
|
||||
last = 0;
|
||||
last_bit = 1;
|
||||
while (bits & last_bit) {
|
||||
++last;
|
||||
last_bit <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void GjkCollisionState::set(Convex* aa, Convex* bb,
|
||||
const MatrixF& a2w, const MatrixF& b2w)
|
||||
{
|
||||
a = aa;
|
||||
b = bb;
|
||||
|
||||
bits = 0;
|
||||
all_bits = 0;
|
||||
reset(a2w,b2w);
|
||||
|
||||
// link
|
||||
mLista = CollisionStateList::alloc();
|
||||
mLista->mState = this;
|
||||
mListb = CollisionStateList::alloc();
|
||||
mListb->mState = this;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void GjkCollisionState::reset(const MatrixF& a2w, const MatrixF& b2w)
|
||||
{
|
||||
VectorF zero(0,0,0),sa,sb;
|
||||
a2w.mulP(a->support(zero),&sa);
|
||||
b2w.mulP(b->support(zero),&sb);
|
||||
v = sa - sb;
|
||||
dist = v.len();
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void GjkCollisionState::getCollisionInfo(const MatrixF& mat, Collision* info)
|
||||
{
|
||||
AssertFatal(false, "GjkCollisionState::getCollisionInfo() - There remain scaling problems here.");
|
||||
// This assumes that the shapes do not intersect
|
||||
Point3F pa,pb;
|
||||
if (bits) {
|
||||
getClosestPoints(pa,pb);
|
||||
mat.mulP(pa,&info->point);
|
||||
b->getTransform().mulP(pb,&pa);
|
||||
info->normal = info->point - pa;
|
||||
}
|
||||
else {
|
||||
mat.mulP(p[last],&info->point);
|
||||
info->normal = v;
|
||||
}
|
||||
info->normal.normalize();
|
||||
info->object = b->getObject();
|
||||
}
|
||||
|
||||
void GjkCollisionState::getClosestPoints(Point3F& p1, Point3F& p2)
|
||||
{
|
||||
F32 sum = 0;
|
||||
p1.set(0, 0, 0);
|
||||
p2.set(0, 0, 0);
|
||||
for (int i = 0, bit = 1; i < 4; ++i, bit <<= 1) {
|
||||
if (bits & bit) {
|
||||
sum += det[bits][i];
|
||||
p1 += p[i] * det[bits][i];
|
||||
p2 += q[i] * det[bits][i];
|
||||
}
|
||||
}
|
||||
F32 s = 1 / sum;
|
||||
p1 *= s;
|
||||
p2 *= s;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
bool GjkCollisionState::intersect(const MatrixF& a2w, const MatrixF& b2w)
|
||||
{
|
||||
num_iterations = 0;
|
||||
MatrixF w2a,w2b;
|
||||
|
||||
w2a = a2w;
|
||||
w2b = b2w;
|
||||
w2a.inverse();
|
||||
w2b.inverse();
|
||||
reset(a2w,b2w);
|
||||
|
||||
bits = 0;
|
||||
all_bits = 0;
|
||||
|
||||
do {
|
||||
nextBit();
|
||||
|
||||
VectorF va,sa;
|
||||
w2a.mulV(-v,&va);
|
||||
p[last] = a->support(va);
|
||||
a2w.mulP(p[last],&sa);
|
||||
|
||||
VectorF vb,sb;
|
||||
w2b.mulV(v,&vb);
|
||||
q[last] = b->support(vb);
|
||||
b2w.mulP(q[last],&sb);
|
||||
|
||||
VectorF w = sa - sb;
|
||||
if (mDot(v,w) > 0)
|
||||
return false;
|
||||
if (degenerate(w)) {
|
||||
++num_irregularities;
|
||||
return false;
|
||||
}
|
||||
|
||||
y[last] = w;
|
||||
all_bits = bits | last_bit;
|
||||
|
||||
++num_iterations;
|
||||
if (!closest(v) || num_iterations > sIteration) {
|
||||
++num_irregularities;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
while (bits < 15 && v.lenSquared() > sEpsilon2);
|
||||
return true;
|
||||
}
|
||||
|
||||
F32 GjkCollisionState::distance(const MatrixF& a2w, const MatrixF& b2w,
|
||||
const F32 dontCareDist, const MatrixF* _w2a, const MatrixF* _w2b)
|
||||
{
|
||||
num_iterations = 0;
|
||||
MatrixF w2a,w2b;
|
||||
|
||||
if (_w2a == NULL || _w2b == NULL) {
|
||||
w2a = a2w;
|
||||
w2b = b2w;
|
||||
w2a.inverse();
|
||||
w2b.inverse();
|
||||
}
|
||||
else {
|
||||
w2a = *_w2a;
|
||||
w2b = *_w2b;
|
||||
}
|
||||
|
||||
reset(a2w,b2w);
|
||||
bits = 0;
|
||||
all_bits = 0;
|
||||
F32 mu = 0;
|
||||
|
||||
do {
|
||||
nextBit();
|
||||
|
||||
VectorF va,sa;
|
||||
w2a.mulV(-v,&va);
|
||||
p[last] = a->support(va);
|
||||
a2w.mulP(p[last],&sa);
|
||||
|
||||
VectorF vb,sb;
|
||||
w2b.mulV(v,&vb);
|
||||
q[last] = b->support(vb);
|
||||
b2w.mulP(q[last],&sb);
|
||||
|
||||
VectorF w = sa - sb;
|
||||
F32 nm = mDot(v, w) / dist;
|
||||
if (nm > mu)
|
||||
mu = nm;
|
||||
if (mu > dontCareDist)
|
||||
return mu;
|
||||
if (mFabs(dist - mu) <= dist * rel_error)
|
||||
return dist;
|
||||
|
||||
++num_iterations;
|
||||
if (degenerate(w) || num_iterations > sIteration) {
|
||||
++num_irregularities;
|
||||
return dist;
|
||||
}
|
||||
|
||||
y[last] = w;
|
||||
all_bits = bits | last_bit;
|
||||
|
||||
if (!closest(v)) {
|
||||
++num_irregularities;
|
||||
return dist;
|
||||
}
|
||||
|
||||
dist = v.len();
|
||||
}
|
||||
while (bits < 15 && dist > sTolerance) ;
|
||||
|
||||
if (bits == 15 && mu <= 0)
|
||||
dist = 0;
|
||||
return dist;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
inline void renderCross(const Point3F x)
|
||||
{
|
||||
glVertex3fv(x + VectorF(0,0,+0.1));
|
||||
glVertex3fv(x + VectorF(0,0,-0.1));
|
||||
glVertex3fv(x + VectorF(0,+0.1,0));
|
||||
glVertex3fv(x + VectorF(0,-0.1,0));
|
||||
glVertex3fv(x + VectorF(-0.1,0,0));
|
||||
glVertex3fv(x + VectorF(+0.1,0,0));
|
||||
}
|
||||
|
||||
void GjkCollisionState::render()
|
||||
{
|
||||
glBegin(GL_LINES);
|
||||
glColor3f(1,1,0);
|
||||
|
||||
// Cross for each witness point
|
||||
for (int i = 0, bit = 1; i < 4; ++i, bit <<= 1) {
|
||||
Point3F x;
|
||||
if (bits & bit) {
|
||||
a->getTransform().mulP(p[i],&x);
|
||||
renderCross(x);
|
||||
b->getTransform().mulP(q[i],&x);
|
||||
renderCross(x);
|
||||
}
|
||||
}
|
||||
|
||||
// Cross and line for closest points
|
||||
Point3F sp,ep;
|
||||
if (bits) {
|
||||
Point3F p1,p2;
|
||||
getClosestPoints(p1,p2);
|
||||
a->getTransform().mulP(p1,&sp);
|
||||
b->getTransform().mulP(p2,&ep);
|
||||
}
|
||||
else {
|
||||
a->getTransform().mulP(p[0],&sp);
|
||||
b->getTransform().mulP(q[0],&ep);
|
||||
}
|
||||
renderCross(sp);
|
||||
renderCross(ep);
|
||||
glVertex3fv(sp);
|
||||
glVertex3fv(ep);
|
||||
|
||||
glEnd();
|
||||
}
|
66
engine/collision/gjk.h
Executable file
66
engine/collision/gjk.h
Executable file
@ -0,0 +1,66 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _GJK_H_
|
||||
#define _GJK_H_
|
||||
|
||||
#ifndef _MMATH_H_
|
||||
#include "math/mMath.h"
|
||||
#endif
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/tVector.h"
|
||||
#endif
|
||||
#ifndef _CONVEX_H_
|
||||
#include "collision/convex.h"
|
||||
#endif
|
||||
|
||||
class Convex;
|
||||
struct CollisionStateList;
|
||||
struct Collision;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
struct GjkCollisionState: public CollisionState
|
||||
{
|
||||
/// @name Temporary values
|
||||
/// @{
|
||||
Point3F p[4]; ///< support points of object A in local coordinates
|
||||
Point3F q[4]; ///< support points of object B in local coordinates
|
||||
VectorF y[4]; ///< support points of A - B in world coordinates
|
||||
|
||||
S32 bits; ///< identifies current simplex
|
||||
S32 all_bits; ///< all_bits = bits | last_bit
|
||||
F32 det[16][4]; ///< cached sub-determinants
|
||||
F32 dp[4][4]; ///< cached dot products
|
||||
|
||||
S32 last; ///< identifies last found support point
|
||||
S32 last_bit; ///< last_bit = 1<<last
|
||||
/// @}
|
||||
|
||||
///
|
||||
void compute_det();
|
||||
bool valid(int s);
|
||||
void compute_vector(int bits, VectorF& v);
|
||||
bool closest(VectorF& v);
|
||||
bool degenerate(const VectorF& w);
|
||||
void nextBit();
|
||||
void swap();
|
||||
void reset(const MatrixF& a2w, const MatrixF& b2w);
|
||||
|
||||
GjkCollisionState();
|
||||
~GjkCollisionState();
|
||||
|
||||
void set(Convex* a,Convex* b,const MatrixF& a2w, const MatrixF& b2w);
|
||||
|
||||
void getCollisionInfo(const MatrixF& mat, Collision* info);
|
||||
void getClosestPoints(Point3F& p1, Point3F& p2);
|
||||
bool intersect(const MatrixF& a2w, const MatrixF& b2w);
|
||||
F32 distance(const MatrixF& a2w, const MatrixF& b2w, const F32 dontCareDist,
|
||||
const MatrixF* w2a = NULL, const MatrixF* _w2b = NULL);
|
||||
void render();
|
||||
};
|
||||
|
||||
|
||||
#endif
|
286
engine/collision/optimizedPolyList.cc
Executable file
286
engine/collision/optimizedPolyList.cc
Executable file
@ -0,0 +1,286 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "dgl/dgl.h"
|
||||
#include "math/mMath.h"
|
||||
#include "console/console.h"
|
||||
#include "collision/optimizedPolyList.h"
|
||||
#include "core/color.h"
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
OptimizedPolyList::OptimizedPolyList()
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION(mPolyList);
|
||||
VECTOR_SET_ASSOCIATION(mVertexList);
|
||||
VECTOR_SET_ASSOCIATION(mIndexList);
|
||||
VECTOR_SET_ASSOCIATION(mPlaneList);
|
||||
VECTOR_SET_ASSOCIATION(mEdgeList);
|
||||
|
||||
mIndexList.reserve(100);
|
||||
}
|
||||
|
||||
OptimizedPolyList::~OptimizedPolyList()
|
||||
{
|
||||
mPolyList.clear();
|
||||
mVertexList.clear();
|
||||
mIndexList.clear();
|
||||
mPlaneList.clear();
|
||||
mEdgeList.clear();
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
void OptimizedPolyList::clear()
|
||||
{
|
||||
// Only clears internal data
|
||||
mPolyList.clear();
|
||||
mVertexList.clear();
|
||||
mIndexList.clear();
|
||||
mPlaneList.clear();
|
||||
mEdgeList.clear();
|
||||
mPolyPlaneList.clear();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
U32 OptimizedPolyList::addPoint(const Point3F& p)
|
||||
{
|
||||
Point3F v;
|
||||
v.x = p.x * mScale.x;
|
||||
v.y = p.y * mScale.y;
|
||||
v.z = p.z * mScale.z;
|
||||
mMatrix.mulP(v);
|
||||
|
||||
for (U32 i = 0; i < mVertexList.size(); i++)
|
||||
{
|
||||
if (isEqual(v, mVertexList[i]))
|
||||
return i;
|
||||
}
|
||||
|
||||
// If we make it to here then we didn't find the vertex
|
||||
mVertexList.push_back(v);
|
||||
|
||||
return mVertexList.size() - 1;
|
||||
}
|
||||
|
||||
|
||||
U32 OptimizedPolyList::addPlane(const PlaneF& plane)
|
||||
{
|
||||
PlaneF pln;
|
||||
mPlaneTransformer.transform(plane, pln);
|
||||
|
||||
for (U32 i = 0; i < mPlaneList.size(); i++)
|
||||
{
|
||||
// The PlaneF == operator uses the Point3F == operator and
|
||||
// doesn't check the d
|
||||
if (isEqual(pln, mPlaneList[i]) &&
|
||||
mFabs(pln.d - mPlaneList[i].d) < DEV)
|
||||
return i;
|
||||
}
|
||||
|
||||
// If we made it here then we didin't find the vertex
|
||||
mPlaneList.push_back(pln);
|
||||
|
||||
return mPlaneList.size() - 1;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void OptimizedPolyList::begin(U32 material,U32 surfaceKey)
|
||||
{
|
||||
mPolyList.increment();
|
||||
Poly& poly = mPolyList.last();
|
||||
poly.object = mCurrObject;
|
||||
poly.material = material;
|
||||
poly.vertexStart = mIndexList.size();
|
||||
poly.surfaceKey = surfaceKey;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void OptimizedPolyList::plane(U32 v1,U32 v2,U32 v3)
|
||||
{
|
||||
AssertFatal(v1 < mVertexList.size() && v2 < mVertexList.size() && v3 < mVertexList.size(),
|
||||
"OptimizedPolyList::plane(): Vertex indices are larger than vertex list size");
|
||||
|
||||
mPolyList.last().plane = addPlane(PlaneF(mVertexList[v1], mVertexList[v2],mVertexList[v3]));
|
||||
}
|
||||
|
||||
void OptimizedPolyList::plane(const PlaneF& p)
|
||||
{
|
||||
mPolyList.last().plane = addPlane(p);
|
||||
}
|
||||
|
||||
void OptimizedPolyList::plane(const U32 index)
|
||||
{
|
||||
AssertFatal(index < mPlaneList.size(), "Out of bounds index!");
|
||||
mPolyList.last().plane = index;
|
||||
}
|
||||
|
||||
const PlaneF& OptimizedPolyList::getIndexedPlane(const U32 index)
|
||||
{
|
||||
AssertFatal(index < mPlaneList.size(), "Out of bounds index!");
|
||||
return mPlaneList[index];
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void OptimizedPolyList::vertex(U32 vi)
|
||||
{
|
||||
mIndexList.push_back(vi);
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
bool OptimizedPolyList::isEmpty() const
|
||||
{
|
||||
return !mPolyList.size();
|
||||
}
|
||||
|
||||
void OptimizedPolyList::end()
|
||||
{
|
||||
Poly& poly = mPolyList.last();
|
||||
poly.vertexCount = mIndexList.size() - poly.vertexStart;
|
||||
}
|
||||
|
||||
|
||||
void OptimizedPolyList::render()
|
||||
{
|
||||
if (mPolyList.size() == 0 || mVertexList.size() == 0)
|
||||
return;
|
||||
|
||||
//glVertexPointer(3, GL_FLOAT,sizeof(Point3F), mVertexList.address());
|
||||
//glEnableClientState(GL_VERTEX_ARRAY);
|
||||
|
||||
//Poly * p;
|
||||
//for (p = mPolyList.begin(); p < mPolyList.end(); p++)
|
||||
// glDrawElements(GL_POLYGON,p->vertexCount, GL_UNSIGNED_INT, &mIndexList[p->vertexStart]);
|
||||
|
||||
//glDisableClientState(GL_VERTEX_ARRAY);
|
||||
|
||||
// If you need normals
|
||||
for (U32 i = 0; i < mPolyList.size(); i++)
|
||||
{
|
||||
if (mPolyList[i].vertexCount < 3)
|
||||
continue;
|
||||
|
||||
ColorF color(gRandGen.randF(0.0f, 1.0f), gRandGen.randF(0.0f, 1.0f), gRandGen.randF(0.0f, 1.0f));
|
||||
glColor3f(color.red, color.green, color.blue);
|
||||
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
|
||||
|
||||
glBegin(GL_TRIANGLES);
|
||||
for (U32 j = 1; j < mPolyList[i].vertexCount - 1; j++)
|
||||
{
|
||||
U32 i0 = mIndexList[mPolyList[i].vertexStart];
|
||||
U32 i1 = mIndexList[mPolyList[i].vertexStart + j];
|
||||
U32 i2 = mIndexList[mPolyList[i].vertexStart + j + 1];
|
||||
|
||||
Point3F p0 = mVertexList[i0];
|
||||
Point3F p1 = mVertexList[i1];
|
||||
Point3F p2 = mVertexList[i2];
|
||||
|
||||
PlaneF normal = mPlaneList[mPolyList[i].plane];
|
||||
glNormal3f(normal.x, normal.y, normal.z);
|
||||
|
||||
glVertex3f(p0.x, p0.y, p0.z);
|
||||
glVertex3f(p1.x, p1.y, p1.z);
|
||||
glVertex3f(p2.x, p2.y, p2.z);
|
||||
}
|
||||
glEnd();
|
||||
}
|
||||
}
|
||||
|
||||
void OptimizedPolyList::copyPolyToList(OptimizedPolyList* target, U32 pdx) const
|
||||
{
|
||||
target->mPolyList.increment();
|
||||
OptimizedPolyList::Poly& tpoly = target->mPolyList.last();
|
||||
|
||||
tpoly.material = mPolyList[pdx].material;
|
||||
tpoly.object = mPolyList[pdx].object;
|
||||
tpoly.surfaceKey = mPolyList[pdx].surfaceKey;
|
||||
tpoly.vertexCount = mPolyList[pdx].vertexCount;
|
||||
|
||||
PlaneF pln = mPlaneList[mPolyList[pdx].plane];
|
||||
tpoly.plane = target->addPlane(pln);
|
||||
|
||||
tpoly.vertexStart = target->mIndexList.size();
|
||||
|
||||
for (U32 i = 0; i < mPolyList[pdx].vertexCount; i++)
|
||||
{
|
||||
U32 idx = mIndexList[mPolyList[pdx].vertexStart + i];
|
||||
Point3F pt = mVertexList[idx];
|
||||
|
||||
target->mIndexList.increment();
|
||||
target->mIndexList.last() = target->addPoint(pt);
|
||||
}
|
||||
}
|
||||
|
||||
void OptimizedPolyList::copyPolyToList(OptimizedPolyList* target, const FullPoly& poly) const
|
||||
{
|
||||
target->mPolyList.increment();
|
||||
OptimizedPolyList::Poly& tpoly = target->mPolyList.last();
|
||||
|
||||
tpoly.material = poly.material;
|
||||
tpoly.object = poly.object;
|
||||
tpoly.surfaceKey = poly.surfaceKey;
|
||||
tpoly.vertexCount = poly.indexes.size();
|
||||
|
||||
PlaneF pln = poly.plane;
|
||||
tpoly.plane = target->addPlane(pln);
|
||||
|
||||
tpoly.vertexStart = target->mIndexList.size();
|
||||
|
||||
for (U32 i = 0; i < poly.indexes.size(); i++)
|
||||
{
|
||||
Point3F pt = poly.vertexes[poly.indexes[i]];
|
||||
|
||||
target->mIndexList.increment();
|
||||
target->mIndexList.last() = target->addPoint(pt);
|
||||
}
|
||||
}
|
||||
|
||||
OptimizedPolyList::FullPoly OptimizedPolyList::getFullPoly(U32 pdx)
|
||||
{
|
||||
FullPoly ret;
|
||||
|
||||
OptimizedPolyList::Poly& poly = mPolyList[pdx];
|
||||
|
||||
ret.material = poly.material;
|
||||
ret.object = poly.object;
|
||||
ret.surfaceKey = poly.surfaceKey;
|
||||
|
||||
for (U32 i = 0; i < poly.vertexCount; i++)
|
||||
{
|
||||
U32 idx = mIndexList[poly.vertexStart + i];
|
||||
Point3F& pt = mVertexList[idx];
|
||||
|
||||
S32 pdx = -1;
|
||||
|
||||
for (U32 j = 0; j < ret.vertexes.size(); j++)
|
||||
{
|
||||
if (pt == ret.vertexes[j])
|
||||
{
|
||||
pdx = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (pdx == -1)
|
||||
{
|
||||
pdx = ret.vertexes.size();
|
||||
ret.vertexes.push_back(pt);
|
||||
}
|
||||
|
||||
ret.indexes.push_back(pdx);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
128
engine/collision/optimizedPolyList.h
Executable file
128
engine/collision/optimizedPolyList.h
Executable file
@ -0,0 +1,128 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _OPTIMIZEDPOLYLIST_H_
|
||||
#define _OPTIMIZEDPOLYLIST_H_
|
||||
|
||||
#ifndef _ABSTRACTPOLYLIST_H_
|
||||
#include "collision/abstractPolyList.h"
|
||||
#endif
|
||||
|
||||
#define DEV 0.01
|
||||
|
||||
/// A concrete, renderable PolyList
|
||||
///
|
||||
/// This class is used to store geometry from a PolyList query.
|
||||
///
|
||||
/// It allows you to render this data, as well.
|
||||
///
|
||||
/// @see AbstractPolyList
|
||||
class OptimizedPolyList : public AbstractPolyList
|
||||
{
|
||||
public:
|
||||
|
||||
struct Poly
|
||||
{
|
||||
S32 plane;
|
||||
SceneObject* object;
|
||||
S32 material;
|
||||
U32 vertexStart;
|
||||
U32 vertexCount;
|
||||
U32 surfaceKey;
|
||||
|
||||
U32 triangleLightingStartIndex;
|
||||
|
||||
Poly::Poly() { plane = -1; vertexCount = 0; material = -1; };
|
||||
};
|
||||
|
||||
struct FullPoly
|
||||
{
|
||||
PlaneF plane;
|
||||
SceneObject* object;
|
||||
S32 material;
|
||||
U32 surfaceKey;
|
||||
|
||||
Vector<U32> indexes;
|
||||
Vector<Point3F> vertexes;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
NonShared = 0,
|
||||
Concave,
|
||||
Convex
|
||||
};
|
||||
|
||||
struct Edge
|
||||
{
|
||||
U32 type;
|
||||
S32 vertexes[2];
|
||||
S32 faces[2];
|
||||
};
|
||||
|
||||
struct TriangleLighting
|
||||
{
|
||||
U32 lightMapId;
|
||||
PlaneF lightMapEquationX;
|
||||
PlaneF lightMapEquationY;
|
||||
};
|
||||
|
||||
Vector<TriangleLighting> mTriangleLightingList;
|
||||
|
||||
const TriangleLighting *getTriangleLighting(const U32 index)
|
||||
{
|
||||
if(index >= mTriangleLightingList.size())
|
||||
return NULL;
|
||||
return &mTriangleLightingList[index];
|
||||
}
|
||||
|
||||
typedef Vector<PlaneF> PlaneList;
|
||||
typedef Vector<Point3F> VertexList;
|
||||
typedef Vector<Poly> PolyList;
|
||||
typedef Vector<U32> IndexList;
|
||||
typedef Vector<Edge> EdgeList;
|
||||
|
||||
PolyList mPolyList;
|
||||
VertexList mVertexList;
|
||||
IndexList mIndexList;
|
||||
PlaneList mPlaneList;
|
||||
EdgeList mEdgeList;
|
||||
|
||||
PlaneList mPolyPlaneList;
|
||||
|
||||
public:
|
||||
OptimizedPolyList();
|
||||
~OptimizedPolyList();
|
||||
void clear();
|
||||
|
||||
// Virtual methods
|
||||
U32 addPoint(const Point3F& p);
|
||||
U32 addPlane(const PlaneF& plane);
|
||||
void begin(U32 material,U32 surfaceKey);
|
||||
void plane(U32 v1,U32 v2,U32 v3);
|
||||
void plane(const PlaneF& p);
|
||||
void plane(const U32 index);
|
||||
void vertex(U32 vi);
|
||||
void end();
|
||||
|
||||
inline bool isEqual(Point3F& a, Point3F& b)
|
||||
{
|
||||
return( ( mFabs( a.x - b.x ) < DEV ) &&
|
||||
( mFabs( a.y - b.y ) < DEV ) &&
|
||||
( mFabs( a.z - b.z ) < DEV ) );
|
||||
}
|
||||
|
||||
void copyPolyToList(OptimizedPolyList* target, U32 pdx) const;
|
||||
void copyPolyToList(OptimizedPolyList* target, const FullPoly& poly) const;
|
||||
FullPoly getFullPoly(U32 pdx);
|
||||
|
||||
void render();
|
||||
bool isEmpty() const;
|
||||
|
||||
protected:
|
||||
const PlaneF& getIndexedPlane(const U32 index);
|
||||
};
|
||||
|
||||
#endif // _OPTIMIZEDPOLYLIST_H_
|
117
engine/collision/planeExtractor.cc
Executable file
117
engine/collision/planeExtractor.cc
Executable file
@ -0,0 +1,117 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "dgl/dgl.h"
|
||||
#include "math/mMath.h"
|
||||
#include "console/console.h"
|
||||
#include "collision/planeExtractor.h"
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Plane matching parameters
|
||||
static F32 NormalEpsilon = 0.93969; // 20 deg.
|
||||
static F32 DistanceEpsilon = 100;
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
PlaneExtractorPolyList::PlaneExtractorPolyList()
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION(mVertexList);
|
||||
VECTOR_SET_ASSOCIATION(mPolyPlaneList);
|
||||
}
|
||||
|
||||
PlaneExtractorPolyList::~PlaneExtractorPolyList()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void PlaneExtractorPolyList::clear()
|
||||
{
|
||||
mVertexList.clear();
|
||||
mPolyPlaneList.clear();
|
||||
}
|
||||
|
||||
U32 PlaneExtractorPolyList::addPoint(const Point3F& p)
|
||||
{
|
||||
mVertexList.increment();
|
||||
Point3F& v = mVertexList.last();
|
||||
v.x = p.x * mScale.x;
|
||||
v.y = p.y * mScale.y;
|
||||
v.z = p.z * mScale.z;
|
||||
mMatrix.mulP(v);
|
||||
return mVertexList.size() - 1;
|
||||
}
|
||||
|
||||
U32 PlaneExtractorPolyList::addPlane(const PlaneF& plane)
|
||||
{
|
||||
mPolyPlaneList.increment();
|
||||
mPlaneTransformer.transform(plane, mPolyPlaneList.last());
|
||||
|
||||
return mPolyPlaneList.size() - 1;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void PlaneExtractorPolyList::plane(U32 v1,U32 v2,U32 v3)
|
||||
{
|
||||
mPlaneList->last().set(mVertexList[v1],
|
||||
mVertexList[v2],mVertexList[v3]);
|
||||
}
|
||||
|
||||
void PlaneExtractorPolyList::plane(const PlaneF& p)
|
||||
{
|
||||
mPlaneTransformer.transform(p, mPlaneList->last());
|
||||
}
|
||||
|
||||
void PlaneExtractorPolyList::plane(const U32 index)
|
||||
{
|
||||
AssertFatal(index < mPolyPlaneList.size(), "Out of bounds index!");
|
||||
mPlaneList->last() = mPolyPlaneList[index];
|
||||
}
|
||||
|
||||
const PlaneF& PlaneExtractorPolyList::getIndexedPlane(const U32 index)
|
||||
{
|
||||
AssertFatal(index < mPolyPlaneList.size(), "Out of bounds index!");
|
||||
return mPolyPlaneList[index];
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
bool PlaneExtractorPolyList::isEmpty() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void PlaneExtractorPolyList::begin(U32,U32)
|
||||
{
|
||||
mPlaneList->increment();
|
||||
}
|
||||
|
||||
void PlaneExtractorPolyList::end()
|
||||
{
|
||||
// See if there are any duplicate planes
|
||||
PlaneF &plane = mPlaneList->last();
|
||||
PlaneF *ptr = mPlaneList->begin();
|
||||
for (; ptr != &plane; ptr++)
|
||||
if (mFabs(ptr->d - plane.d) < DistanceEpsilon &&
|
||||
mDot(*ptr,plane) > NormalEpsilon) {
|
||||
mPlaneList->decrement();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void PlaneExtractorPolyList::vertex(U32)
|
||||
{
|
||||
}
|
||||
|
||||
void PlaneExtractorPolyList::render()
|
||||
{
|
||||
}
|
||||
|
62
engine/collision/planeExtractor.h
Executable file
62
engine/collision/planeExtractor.h
Executable file
@ -0,0 +1,62 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _PLANEEXTRACTOR_H_
|
||||
#define _PLANEEXTRACTOR_H_
|
||||
|
||||
#ifndef _MMATH_H_
|
||||
#include "math/mMath.h"
|
||||
#endif
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/tVector.h"
|
||||
#endif
|
||||
#ifndef _ABSTRACTPOLYLIST_H_
|
||||
#include "collision/abstractPolyList.h"
|
||||
#endif
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
/// Fill a Vector<PlaneF> with the planes from the geometry passed through this
|
||||
/// PolyList.
|
||||
///
|
||||
/// @see AbstractPolyList
|
||||
class PlaneExtractorPolyList: public AbstractPolyList
|
||||
{
|
||||
void memcpy(U32* d, U32* s,U32 size);
|
||||
|
||||
public:
|
||||
// Internal data
|
||||
typedef Vector<Point3F> VertexList;
|
||||
VertexList mVertexList;
|
||||
|
||||
Vector<PlaneF> mPolyPlaneList;
|
||||
|
||||
// Set by caller
|
||||
Vector<PlaneF>* mPlaneList;
|
||||
|
||||
//
|
||||
PlaneExtractorPolyList();
|
||||
~PlaneExtractorPolyList();
|
||||
void clear();
|
||||
void render();
|
||||
|
||||
// Virtual methods
|
||||
bool isEmpty() const;
|
||||
U32 addPoint(const Point3F& p);
|
||||
U32 addPlane(const PlaneF& plane);
|
||||
void begin(U32 material,U32 surfaceKey);
|
||||
void plane(U32 v1,U32 v2,U32 v3);
|
||||
void plane(const PlaneF& p);
|
||||
void plane(const U32 index);
|
||||
void vertex(U32 vi);
|
||||
void end();
|
||||
|
||||
protected:
|
||||
const PlaneF& getIndexedPlane(const U32 index);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
150
engine/collision/polyhedron.cc
Executable file
150
engine/collision/polyhedron.cc
Executable file
@ -0,0 +1,150 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "dgl/dgl.h"
|
||||
#include "console/console.h"
|
||||
#include "collision/polyhedron.h"
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void Polyhedron::buildBox(const MatrixF& transform,const Box3F& box)
|
||||
{
|
||||
// Box is assumed to be axis aligned in the source space.
|
||||
// Transform into geometry space
|
||||
Point3F xvec,yvec,zvec,min;
|
||||
transform.getColumn(0,&xvec);
|
||||
xvec *= box.len_x();
|
||||
transform.getColumn(1,&yvec);
|
||||
yvec *= box.len_y();
|
||||
transform.getColumn(2,&zvec);
|
||||
zvec *= box.len_z();
|
||||
transform.mulP(box.min,&min);
|
||||
|
||||
// Initial vertices
|
||||
pointList.setSize(8);
|
||||
pointList[0] = min;
|
||||
pointList[1] = min + yvec;
|
||||
pointList[2] = min + xvec + yvec;
|
||||
pointList[3] = min + xvec;
|
||||
pointList[4] = pointList[0] + zvec;
|
||||
pointList[5] = pointList[1] + zvec;
|
||||
pointList[6] = pointList[2] + zvec;
|
||||
pointList[7] = pointList[3] + zvec;
|
||||
|
||||
// Initial faces
|
||||
planeList.setSize(6);
|
||||
planeList[0].set(pointList[0],xvec);
|
||||
planeList[0].invert();
|
||||
planeList[1].set(pointList[2],yvec);
|
||||
planeList[2].set(pointList[2],xvec);
|
||||
planeList[3].set(pointList[0],yvec);
|
||||
planeList[3].invert();
|
||||
planeList[4].set(pointList[0],zvec);
|
||||
planeList[4].invert();
|
||||
planeList[5].set(pointList[4],zvec);
|
||||
|
||||
// The edges are constructed so that the vertices
|
||||
// are oriented clockwise for face[0]
|
||||
edgeList.setSize(12);
|
||||
Edge* edge = edgeList.begin();
|
||||
S32 nextEdge = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
S32 n = (i == 3)? 0: i + 1;
|
||||
S32 p = (i == 0)? 3: i - 1;
|
||||
edge->vertex[0] = i;
|
||||
edge->vertex[1] = n;
|
||||
edge->face[0] = i;
|
||||
edge->face[1] = 4;
|
||||
edge++;
|
||||
edge->vertex[0] = 4 + i;
|
||||
edge->vertex[1] = 4 + n;
|
||||
edge->face[0] = 5;
|
||||
edge->face[1] = i;
|
||||
edge++;
|
||||
edge->vertex[0] = i;
|
||||
edge->vertex[1] = 4 + i;
|
||||
edge->face[0] = p;
|
||||
edge->face[1] = i;
|
||||
edge++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void Polyhedron::render()
|
||||
{
|
||||
glVertexPointer(3,GL_FLOAT,sizeof(Point3F),pointList.address());
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glColor3f(1, 0, 1);
|
||||
|
||||
for (S32 e = 0; e < edgeList.size(); e++)
|
||||
glDrawElements(GL_LINES,2,GL_UNSIGNED_INT,&edgeList[e].vertex);
|
||||
|
||||
for (U32 i = 0; i < planeList.size(); i++) {
|
||||
Point3F origin(0, 0, 0);
|
||||
U32 num = 0;
|
||||
for (U32 j = 0; j < edgeList.size(); j++) {
|
||||
if (edgeList[j].face[0] == i || edgeList[j].face[1] == i) {
|
||||
origin += pointList[edgeList[j].vertex[0]];
|
||||
origin += pointList[edgeList[j].vertex[1]];
|
||||
num += 2;
|
||||
}
|
||||
}
|
||||
origin /= F32(num);
|
||||
|
||||
glColor3f(1, 1, 1);
|
||||
glBegin(GL_LINES);
|
||||
glVertex3fv(origin);
|
||||
glVertex3f(origin.x + planeList[i].x * 0.2,
|
||||
origin.y + planeList[i].y * 0.2,
|
||||
origin.z + planeList[i].z * 0.2);
|
||||
glEnd();
|
||||
}
|
||||
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
}
|
||||
|
||||
void Polyhedron::render(const VectorF& vec,F32 time)
|
||||
{
|
||||
bool faceVisible[50];
|
||||
for (int f = 0; f < planeList.size(); f++)
|
||||
faceVisible[f] = mDot(planeList[f],vec) > 0;
|
||||
|
||||
VectorF tvec = vec;
|
||||
tvec *= time;
|
||||
for (int e = 0; e < edgeList.size(); e++) {
|
||||
Polyhedron::Edge const& edge = edgeList[e];
|
||||
if (faceVisible[edge.face[0]] || faceVisible[edge.face[1]]) {
|
||||
Point3F pp;
|
||||
|
||||
glBegin(GL_LINE_LOOP);
|
||||
glColor3f(0.5,0.5,0.5);
|
||||
const Point3F& p1 = pointList[edge.vertex[0]];
|
||||
const Point3F& p2 = pointList[edge.vertex[1]];
|
||||
glVertex3fv(p1);
|
||||
glVertex3fv(p2);
|
||||
pp = p2 + vec;
|
||||
glVertex3fv(pp);
|
||||
pp = p1 + vec;
|
||||
glVertex3fv(pp);
|
||||
glEnd();
|
||||
|
||||
if (time <= 1.0) {
|
||||
glBegin(GL_LINES);
|
||||
glColor3f(0.5,0.5,0);
|
||||
pp = pointList[edge.vertex[0]];
|
||||
pp += tvec;
|
||||
glVertex3fv(pp);
|
||||
pp = pointList[edge.vertex[1]];
|
||||
pp += tvec;
|
||||
glVertex3fv(pp);
|
||||
glEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
45
engine/collision/polyhedron.h
Executable file
45
engine/collision/polyhedron.h
Executable file
@ -0,0 +1,45 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _POLYHEDRON_H_
|
||||
#define _POLYHEDRON_H_
|
||||
|
||||
#ifndef _MMATH_H_
|
||||
#include "math/mMath.h"
|
||||
#endif
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/tVector.h"
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
struct Polyhedron
|
||||
{
|
||||
struct Edge
|
||||
{
|
||||
// Edge vertices must be oriented clockwise
|
||||
// for face[0]
|
||||
U32 face[2];
|
||||
U32 vertex[2];
|
||||
};
|
||||
|
||||
Vector<Point3F> pointList;
|
||||
Vector<PlaneF> planeList;
|
||||
Vector<Edge> edgeList;
|
||||
|
||||
// Misc support methods
|
||||
Polyhedron() {
|
||||
VECTOR_SET_ASSOCIATION(pointList);
|
||||
VECTOR_SET_ASSOCIATION(planeList);
|
||||
VECTOR_SET_ASSOCIATION(edgeList);
|
||||
}
|
||||
|
||||
void buildBox(const MatrixF& mat, const Box3F& box);
|
||||
void render();
|
||||
void render(const VectorF& vec,F32 time);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
433
engine/collision/polytope.cc
Executable file
433
engine/collision/polytope.cc
Executable file
@ -0,0 +1,433 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "dgl/dgl.h"
|
||||
#include "math/mMath.h"
|
||||
#include "collision/collision.h"
|
||||
#include "collision/polytope.h"
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
Polytope::Polytope()
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION(mEdgeList);
|
||||
VECTOR_SET_ASSOCIATION(mFaceList);
|
||||
VECTOR_SET_ASSOCIATION(mVertexList);
|
||||
VECTOR_SET_ASSOCIATION(mVolumeList);
|
||||
|
||||
mVertexList.reserve(100);
|
||||
mFaceList.reserve(200);
|
||||
mEdgeList.reserve(100);
|
||||
mVolumeList.reserve(20);
|
||||
sideCount = 0;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Box should be axis aligned in the transform space provided.
|
||||
|
||||
void Polytope::buildBox(const MatrixF& transform,const Box3F& box)
|
||||
{
|
||||
// Box is assumed to be axis aligned in the source space.
|
||||
// Transform into geometry space
|
||||
Point3F xvec,yvec,zvec,min;
|
||||
transform.getColumn(0,&xvec);
|
||||
xvec *= box.len_x();
|
||||
transform.getColumn(1,&yvec);
|
||||
yvec *= box.len_y();
|
||||
transform.getColumn(2,&zvec);
|
||||
zvec *= box.len_z();
|
||||
transform.mulP(box.min,&min);
|
||||
|
||||
// Initial vertices
|
||||
mVertexList.setSize(8);
|
||||
mVertexList[0].point = min;
|
||||
mVertexList[1].point = min + yvec;
|
||||
mVertexList[2].point = min + xvec + yvec;
|
||||
mVertexList[3].point = min + xvec;
|
||||
mVertexList[4].point = mVertexList[0].point + zvec;
|
||||
mVertexList[5].point = mVertexList[1].point + zvec;
|
||||
mVertexList[6].point = mVertexList[2].point + zvec;
|
||||
mVertexList[7].point = mVertexList[3].point + zvec;
|
||||
S32 i;
|
||||
for (i = 0; i < 8; i++)
|
||||
mVertexList[i].side = 0;
|
||||
|
||||
// Initial faces
|
||||
mFaceList.setSize(6);
|
||||
for (S32 f = 0; f < 6; f++) {
|
||||
Face& face = mFaceList[f];
|
||||
face.original = true;
|
||||
face.vertex = 0;
|
||||
}
|
||||
|
||||
mFaceList[0].plane.set(mVertexList[0].point,xvec);
|
||||
mFaceList[0].plane.invert();
|
||||
mFaceList[1].plane.set(mVertexList[2].point,yvec);
|
||||
mFaceList[2].plane.set(mVertexList[2].point,xvec);
|
||||
mFaceList[3].plane.set(mVertexList[0].point,yvec);
|
||||
mFaceList[3].plane.invert();
|
||||
mFaceList[4].plane.set(mVertexList[0].point,zvec);
|
||||
mFaceList[4].plane.invert();
|
||||
mFaceList[5].plane.set(mVertexList[4].point,zvec);
|
||||
|
||||
// Initial edges
|
||||
mEdgeList.setSize(12);
|
||||
Edge* edge = mEdgeList.begin();
|
||||
S32 nextEdge = 0;
|
||||
for (i = 0; i < 4; i++) {
|
||||
S32 n = (i == 3)? 0: i + 1;
|
||||
S32 p = (i == 0)? 3: i - 1;
|
||||
edge->vertex[0] = i;
|
||||
edge->vertex[1] = n;
|
||||
edge->face[0] = i;
|
||||
edge->face[1] = 4;
|
||||
edge->next = ++nextEdge;
|
||||
edge++;
|
||||
edge->vertex[0] = 4 + i;
|
||||
edge->vertex[1] = 4 + n;
|
||||
edge->face[0] = i;
|
||||
edge->face[1] = 5;
|
||||
edge->next = ++nextEdge;
|
||||
edge++;
|
||||
edge->vertex[0] = i;
|
||||
edge->vertex[1] = 4 + i;
|
||||
edge->face[0] = i;
|
||||
edge->face[1] = p;
|
||||
edge->next = ++nextEdge;
|
||||
edge++;
|
||||
}
|
||||
edge[-1].next = -1;
|
||||
|
||||
// Volume
|
||||
mVolumeList.setSize(1);
|
||||
Volume& volume = mVolumeList.last();
|
||||
volume.edgeList = 0;
|
||||
volume.material = -1;
|
||||
volume.object = 0;
|
||||
sideCount = 0;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void Polytope::intersect(SimObject* object,const BSPNode* root)
|
||||
{
|
||||
AssertFatal(mVolumeList.size() > 0,"Polytope::intersect: Missing initial volume");
|
||||
|
||||
// Always clips the first volume against the BSP
|
||||
VolumeStack stack;
|
||||
stack.reserve(50);
|
||||
stack.increment();
|
||||
stack.last().edgeList = mVolumeList[0].edgeList;
|
||||
stack.last().node = root;
|
||||
|
||||
while (!stack.empty()) {
|
||||
StackElement volume = stack.last();
|
||||
stack.pop_back();
|
||||
|
||||
// If it's a solid node, export the volume
|
||||
const BSPNode* node = volume.node;
|
||||
if (!node->backNode && !node->frontNode) {
|
||||
mVolumeList.increment();
|
||||
Volume& vol = mVolumeList.last();
|
||||
vol.object = object;
|
||||
vol.material = node->material;
|
||||
vol.edgeList = volume.edgeList;
|
||||
continue;
|
||||
}
|
||||
|
||||
// New front and back faces
|
||||
mFaceList.increment(2);
|
||||
Face& frontFace = mFaceList.last();
|
||||
Face& backFace = *(&frontFace - 1);
|
||||
|
||||
backFace.original = frontFace.original = false;
|
||||
backFace.vertex = frontFace.vertex = 0;
|
||||
backFace.plane = frontFace.plane = node->plane;
|
||||
backFace.plane.invert();
|
||||
|
||||
// New front and back volumes
|
||||
StackElement frontVolume,backVolume;
|
||||
frontVolume.edgeList = backVolume.edgeList = -1;
|
||||
|
||||
const PlaneF& plane = node->plane;
|
||||
S32 startVertex = mVertexList.size();
|
||||
|
||||
// Test & clip all the edges
|
||||
S32 sideBase = ++sideCount << 1;
|
||||
for (S32 i = volume.edgeList; i >= 0; i = mEdgeList[i].next) {
|
||||
|
||||
// Copy into tmp first to avoid problems with the array.
|
||||
Edge edge = mEdgeList[i];
|
||||
|
||||
Vertex& v0 = mVertexList[edge.vertex[0]];
|
||||
if (v0.side < sideBase)
|
||||
v0.side = sideBase + ((plane.distToPlane(v0.point) >= 0)? 0: 1);
|
||||
Vertex& v1 = mVertexList[edge.vertex[1]];
|
||||
if (v1.side < sideBase)
|
||||
v1.side = sideBase + ((plane.distToPlane(v1.point) >= 0)? 0: 1);
|
||||
|
||||
if (v0.side != v1.side) {
|
||||
S32 s = v0.side - sideBase;
|
||||
intersect(plane,v0.point,v1.point);
|
||||
|
||||
// Split the edge into each volume
|
||||
mEdgeList.increment(2);
|
||||
Edge& e0 = mEdgeList.last();
|
||||
e0.next = frontVolume.edgeList;
|
||||
frontVolume.edgeList = mEdgeList.size() - 1;
|
||||
|
||||
Edge& e1 = *(&e0 - 1);
|
||||
e1.next = backVolume.edgeList;
|
||||
backVolume.edgeList = frontVolume.edgeList - 1;
|
||||
|
||||
e0.vertex[0] = edge.vertex[s];
|
||||
e1.vertex[0] = edge.vertex[s ^ 1];
|
||||
e0.vertex[1] = e1.vertex[1] = mVertexList.size() - 1;
|
||||
e0.face[0] = e1.face[0] = edge.face[0];
|
||||
e0.face[1] = e1.face[1] = edge.face[1];
|
||||
|
||||
// Add new edges on the plane, one to each volume
|
||||
for (S32 f = 0; f < 2; f++) {
|
||||
Face& face = mFaceList[edge.face[f]];
|
||||
if (face.vertex < startVertex)
|
||||
face.vertex = mVertexList.size() - 1;
|
||||
else {
|
||||
mEdgeList.increment(2);
|
||||
Edge& e0 = mEdgeList.last();
|
||||
e0.next = frontVolume.edgeList;
|
||||
frontVolume.edgeList = mEdgeList.size() - 1;
|
||||
|
||||
Edge& e1 = *(&e0 - 1);
|
||||
e1.next = backVolume.edgeList;
|
||||
backVolume.edgeList = frontVolume.edgeList - 1;
|
||||
|
||||
e1.vertex[0] = e0.vertex[0] = face.vertex;
|
||||
e1.vertex[1] = e0.vertex[1] = mVertexList.size() - 1;
|
||||
e1.face[0] = e0.face[0] = edge.face[f];
|
||||
e1.face[1] = mFaceList.size() - 1;
|
||||
e0.face[1] = e1.face[1] - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
if (v0.side == sideBase) {
|
||||
mEdgeList.push_back(edge);
|
||||
Edge& ne = mEdgeList.last();
|
||||
ne.next = frontVolume.edgeList;
|
||||
frontVolume.edgeList = mEdgeList.size() - 1;
|
||||
}
|
||||
else {
|
||||
mEdgeList.push_back(edge);
|
||||
Edge& ne = mEdgeList.last();
|
||||
ne.next = backVolume.edgeList;
|
||||
backVolume.edgeList = mEdgeList.size() - 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Push the front and back nodes
|
||||
if (node->frontNode && frontVolume.edgeList >= 0) {
|
||||
frontVolume.node = node->frontNode;
|
||||
stack.push_back(frontVolume);
|
||||
}
|
||||
if (node->backNode && backVolume.edgeList >= 0) {
|
||||
backVolume.node = node->backNode;
|
||||
stack.push_back(backVolume);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
bool Polytope::intersect(const PlaneF& plane,const Point3F& sp,const Point3F& ep)
|
||||
{
|
||||
// If den == 0 then the line and plane are parallel.
|
||||
F32 den;
|
||||
Point3F dt = ep - sp;
|
||||
if ((den = plane.x * dt.x + plane.y * dt.y + plane.z * dt.z) == 0)
|
||||
return false;
|
||||
|
||||
mVertexList.increment();
|
||||
Vertex& v = mVertexList.last();
|
||||
F32 s = -(plane.x * sp.x + plane.y * sp.y + plane.z * sp.z + plane.d) / den;
|
||||
v.point.x = sp.x + dt.x * s;
|
||||
v.point.y = sp.y + dt.y * s;
|
||||
v.point.z = sp.z + dt.z * s;
|
||||
v.side = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void Polytope::render()
|
||||
{
|
||||
glVertexPointer(3,GL_FLOAT,sizeof(Vertex),mVertexList.address());
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glColor3f(0.5, 0.5, 0.5);
|
||||
|
||||
for (VolumeList::iterator itr = mVolumeList.begin();
|
||||
itr < mVolumeList.end(); itr++) {
|
||||
for (S32 e = itr->edgeList; e >= 0; e = mEdgeList[e].next)
|
||||
glDrawElements(GL_LINES,2,GL_UNSIGNED_INT,&mEdgeList[e].vertex);
|
||||
glColor3f(1, 1, 1);
|
||||
}
|
||||
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void Polytope::extrudeFace(int faceIdx,const VectorF& vec,Polytope* out)
|
||||
{
|
||||
// Assumes the face belongs to the first volume.
|
||||
out->mVertexList.clear();
|
||||
out->mFaceList.clear();
|
||||
out->mEdgeList.clear();
|
||||
out->mVolumeList.clear();
|
||||
sideCount++;
|
||||
|
||||
// Front & end faces
|
||||
Face nface;
|
||||
nface.original = true;
|
||||
nface.vertex = 0;
|
||||
nface.plane = mFaceList[faceIdx].plane;
|
||||
out->mFaceList.setSize(2);
|
||||
out->mFaceList[0] = out->mFaceList[1] = nface;
|
||||
out->mFaceList[0].plane.invert();
|
||||
|
||||
for (S32 e = mVolumeList[0].edgeList; e >= 0; e = mEdgeList[e].next) {
|
||||
Edge& edge = mEdgeList[e];
|
||||
if (edge.face[0] == faceIdx || edge.face[1] == faceIdx) {
|
||||
|
||||
// Build face for this edge
|
||||
// Should think about calulating the plane
|
||||
S32 fi = out->mFaceList.size();
|
||||
out->mFaceList.push_back(nface);
|
||||
|
||||
// Reserve 4 entries to make sure the ve[] pointers
|
||||
// into the list don't get invalidated.
|
||||
out->mEdgeList.reserve(out->mEdgeList.size() + 4);
|
||||
Edge* ve[2];
|
||||
|
||||
// Build edges for each vertex
|
||||
for (S32 v = 0; v < 2; v++) {
|
||||
if (mVertexList[edge.vertex[v]].side < sideCount) {
|
||||
mVertexList[edge.vertex[v]].side = sideCount + out->mEdgeList.size();
|
||||
|
||||
out->mVertexList.increment(2);
|
||||
out->mVertexList.end()[-1] =
|
||||
out->mVertexList.end()[-2] =
|
||||
mVertexList[edge.vertex[v]];
|
||||
out->mVertexList.last().point += vec;
|
||||
|
||||
out->mEdgeList.increment();
|
||||
Edge& ne = out->mEdgeList.last();
|
||||
ne.next = out->mEdgeList.size();
|
||||
ne.vertex[1] = out->mVertexList.size() - 1;
|
||||
ne.vertex[0] = ne.vertex[1] - 1;
|
||||
ne.face[0] = ne.face[1] = -1;
|
||||
ve[v] = ≠
|
||||
}
|
||||
else {
|
||||
S32 ei = mVertexList[edge.vertex[v]].side - sideCount;
|
||||
ve[v] = &out->mEdgeList[ei];
|
||||
}
|
||||
|
||||
// Edge should share this face
|
||||
if (ve[v]->face[0] == -1)
|
||||
ve[v]->face[0] = fi;
|
||||
else
|
||||
ve[v]->face[1] = fi;
|
||||
}
|
||||
|
||||
// Build parallel edges
|
||||
out->mEdgeList.increment(2);
|
||||
for (S32 i = 0; i < 2; i++ ) {
|
||||
Edge& ne = out->mEdgeList.end()[i - 2];
|
||||
ne.next = out->mEdgeList.size() - 1 + i;
|
||||
ne.vertex[0] = ve[0]->vertex[i];
|
||||
ne.vertex[1] = ve[1]->vertex[i];
|
||||
ne.face[0] = i;
|
||||
ne.face[1] = fi;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out->mEdgeList.last().next = -1;
|
||||
out->mVolumeList.increment();
|
||||
Volume& nv = out->mVolumeList.last();
|
||||
nv.edgeList = 0;
|
||||
nv.object = 0;
|
||||
nv.material = -1;
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
bool Polytope::findCollision(const VectorF& vec,Polytope::Collision *best)
|
||||
{
|
||||
if (mVolumeList.size() <= 1)
|
||||
return false;
|
||||
if (!best->object)
|
||||
best->distance = 1.0E30;
|
||||
int bestVertex = -1;
|
||||
Volume* bestVolume;
|
||||
sideCount++;
|
||||
|
||||
// Find the closest point
|
||||
for (Volume* vol = mVolumeList.begin() + 1;
|
||||
vol < mVolumeList.end(); vol++) {
|
||||
for (S32 e = vol->edgeList; e >= 0; e = mEdgeList[e].next) {
|
||||
Edge& edge = mEdgeList[e];
|
||||
if (mFaceList[edge.face[0]].original &&
|
||||
mFaceList[edge.face[1]].original)
|
||||
continue;
|
||||
for (S32 v = 0; v < 2; v++) {
|
||||
S32 vi = edge.vertex[v];
|
||||
Vertex& vr = mVertexList[vi];
|
||||
if (vr.side != sideCount) {
|
||||
vr.side = sideCount;
|
||||
F32 dist = mDot(vr.point,vec);
|
||||
if (dist < best->distance) {
|
||||
best->distance = dist;
|
||||
bestVertex = vi;
|
||||
bestVolume = vol;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bestVertex == -1)
|
||||
return false;
|
||||
|
||||
// Fill in the return value
|
||||
best->point = mVertexList[bestVertex].point;
|
||||
best->object = bestVolume->object;
|
||||
best->material = bestVolume->material;
|
||||
|
||||
// Pick the best face
|
||||
F32 bestFaceDot = 1;
|
||||
for (S32 e = bestVolume->edgeList; e >= 0; e = mEdgeList[e].next) {
|
||||
Edge& edge = mEdgeList[e];
|
||||
if (edge.vertex[0] == bestVertex || edge.vertex[1] == bestVertex) {
|
||||
for (S32 f = 0; f < 2; f++) {
|
||||
Face& tf = mFaceList[edge.face[f]];
|
||||
F32 fd = mDot(tf.plane,vec);
|
||||
if (fd < bestFaceDot) {
|
||||
bestFaceDot = fd;
|
||||
best->plane = tf.plane;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
96
engine/collision/polytope.h
Executable file
96
engine/collision/polytope.h
Executable file
@ -0,0 +1,96 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _POLYTOPE_H_
|
||||
#define _POLYTOPE_H_
|
||||
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/tVector.h"
|
||||
#endif
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
class SimObject;
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
class Polytope
|
||||
{
|
||||
// Convex Polyhedron
|
||||
public:
|
||||
struct Vertex {
|
||||
Point3F point;
|
||||
/// Temp BSP clip info
|
||||
S32 side;
|
||||
};
|
||||
struct Edge {
|
||||
S32 vertex[2];
|
||||
S32 face[2];
|
||||
S32 next;
|
||||
};
|
||||
struct Face {
|
||||
PlaneF plane;
|
||||
S32 original;
|
||||
/// Temp BSP clip info
|
||||
S32 vertex;
|
||||
};
|
||||
struct Volume
|
||||
{
|
||||
S32 edgeList;
|
||||
S32 material;
|
||||
SimObject* object;
|
||||
};
|
||||
struct StackElement
|
||||
{
|
||||
S32 edgeList;
|
||||
const BSPNode *node;
|
||||
};
|
||||
struct Collision {
|
||||
SimObject* object;
|
||||
S32 material;
|
||||
PlaneF plane;
|
||||
Point3F point;
|
||||
F32 distance;
|
||||
|
||||
Collision()
|
||||
{
|
||||
object = NULL;
|
||||
distance = 0.0;
|
||||
}
|
||||
};
|
||||
|
||||
typedef Vector<Edge> EdgeList;
|
||||
typedef Vector<Face> FaceList;
|
||||
typedef Vector<Vertex> VertexList;
|
||||
typedef Vector<Volume> VolumeList;
|
||||
typedef Vector<StackElement> VolumeStack;
|
||||
|
||||
//
|
||||
S32 sideCount;
|
||||
EdgeList mEdgeList;
|
||||
FaceList mFaceList;
|
||||
VertexList mVertexList;
|
||||
VolumeList mVolumeList;
|
||||
|
||||
private:
|
||||
bool intersect(const PlaneF& plane,const Point3F& sp,const Point3F& ep);
|
||||
|
||||
public:
|
||||
//
|
||||
Polytope();
|
||||
void buildBox(const MatrixF& transform,const Box3F& box);
|
||||
void intersect(SimObject*, const BSPNode* node);
|
||||
inline bool didIntersect() { return mVolumeList.size() > 1; }
|
||||
void extrudeFace(int fi,const VectorF& vec,Polytope* out);
|
||||
bool findCollision(const VectorF& vec,Polytope::Collision *best);
|
||||
void render();
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
1951
engine/console/BASgram.cc
Executable file
1951
engine/console/BASgram.cc
Executable file
File diff suppressed because it is too large
Load Diff
533
engine/console/BASgram.y
Executable file
533
engine/console/BASgram.y
Executable file
@ -0,0 +1,533 @@
|
||||
%{
|
||||
// Make sure we don't get gram.h twice.
|
||||
#define _BASGRAM_H_
|
||||
#define _CMDGRAM_H_
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "console/console.h"
|
||||
#include "console/compiler.h"
|
||||
#include "console/consoleInternal.h"
|
||||
|
||||
#ifndef YYDEBUG
|
||||
#define YYDEBUG 0
|
||||
#endif
|
||||
|
||||
#define YYSSIZE 350
|
||||
|
||||
int outtext(char *fmt, ...);
|
||||
extern int serrors;
|
||||
|
||||
#define nil 0
|
||||
#undef YY_ARGS
|
||||
#define YY_ARGS(x) x
|
||||
|
||||
int BASlex();
|
||||
void BASerror(char *a, ...);
|
||||
|
||||
#define alloca dMalloc
|
||||
|
||||
%}
|
||||
%{
|
||||
/* Reserved Word Definitions */
|
||||
|
||||
/* IMPORTANT: SEE COMMENTS BELOW IF YOU ARE THINKING ABOUT MODIFYING TOKENS */
|
||||
|
||||
/*
|
||||
ALL TOKENS BETWEEN HERE AND ADDITIONAL TOKENS BELOW MUST BE IDENTICAL TO THOSE
|
||||
IN CS_CMD.y OTHERWISE BAD STUFF HAPPENS
|
||||
*/
|
||||
%}
|
||||
%token <i> rwDEFINE rwENDDEF rwDECLARE
|
||||
%token <i> rwBREAK rwELSE rwCONTINUE rwGLOBAL
|
||||
%token <i> rwIF rwNIL rwRETURN rwWHILE
|
||||
%token <i> rwENDIF rwENDWHILE rwENDFOR rwDEFAULT
|
||||
%token <i> rwFOR rwDATABLOCK rwSWITCH rwCASE rwSWITCHSTR
|
||||
%token <i> rwCASEOR rwPACKAGE
|
||||
%token ILLEGAL_TOKEN
|
||||
%{
|
||||
/* Constants and Identifier Definitions */
|
||||
%}
|
||||
%token <c> CHRCONST
|
||||
%token <i> INTCONST
|
||||
%token <s> TTAG
|
||||
%token <s> VAR
|
||||
%token <s> IDENT
|
||||
%token <str> STRATOM
|
||||
%token <str> TAGATOM
|
||||
%token <f> FLTCONST
|
||||
|
||||
%{
|
||||
/* Operator Definitions */
|
||||
%}
|
||||
%token <i> '+' '-' '*' '/' '<' '>' '=' '.' '|' '&' '%'
|
||||
%token <i> '(' ')' ',' ':' ';' '{' '}' '^' '~' '!' '@'
|
||||
%token <i> opMINUSMINUS opPLUSPLUS
|
||||
%token <i> STMT_SEP
|
||||
%token <i> opSHL opSHR opPLASN opMIASN opMLASN opDVASN opMODASN opANDASN
|
||||
%token <i> opXORASN opORASN opSLASN opSRASN opCAT
|
||||
%token <i> opEQ opNE opGE opLE opAND opOR opSTREQ
|
||||
%token <i> opCOLONCOLON
|
||||
|
||||
%union {
|
||||
char c;
|
||||
int i;
|
||||
const char *s;
|
||||
char *str;
|
||||
double f;
|
||||
StmtNode *stmt;
|
||||
ExprNode *expr;
|
||||
SlotAssignNode *slist;
|
||||
VarNode *var;
|
||||
SlotDecl slot;
|
||||
ObjectBlockDecl odcl;
|
||||
ObjectDeclNode *od;
|
||||
AssignDecl asn;
|
||||
IfStmtNode *ifnode;
|
||||
}
|
||||
|
||||
%type <s> parent_block
|
||||
%type <ifnode> case_block
|
||||
%type <stmt> switch_stmt
|
||||
%type <stmt> decl
|
||||
%type <stmt> decl_list
|
||||
%type <stmt> package_decl
|
||||
%type <stmt> fn_decl_stmt
|
||||
%type <stmt> fn_decl_list
|
||||
%type <stmt> statement_list
|
||||
%type <stmt> stmt
|
||||
%type <expr> expr_list
|
||||
%type <expr> expr_list_decl
|
||||
%type <expr> aidx_expr
|
||||
%type <expr> funcall_expr
|
||||
%type <expr> object_name
|
||||
%type <expr> object_args
|
||||
%type <expr> stmt_expr
|
||||
%type <expr> case_expr
|
||||
%type <expr> class_name_expr
|
||||
%type <stmt> if_stmt
|
||||
%type <stmt> while_stmt
|
||||
%type <stmt> for_stmt
|
||||
%type <stmt> stmt_block
|
||||
%type <stmt> datablock_decl
|
||||
%type <od> object_decl
|
||||
%type <od> object_decl_list
|
||||
%type <odcl> object_declare_block
|
||||
%type <expr> expr
|
||||
%type <slist> slot_assign_list
|
||||
%type <slist> slot_assign
|
||||
%type <slot> slot_acc
|
||||
%type <stmt> expression_stmt
|
||||
%type <var> var_list
|
||||
%type <var> var_list_decl
|
||||
%type <asn> assign_op_struct
|
||||
|
||||
|
||||
%left '['
|
||||
%right opMODASN opANDASN opXORASN opPLASN opMIASN opMLASN opDVASN opMDASN opNDASN opNTASN opORASN opSLASN opSRASN '='
|
||||
%left '?' ':'
|
||||
%left opOR
|
||||
%left opAND
|
||||
%left '|'
|
||||
%left '^'
|
||||
%left '&'
|
||||
%left opEQ opNE
|
||||
%left '<' opLE '>' opGE
|
||||
%left '@' opCAT opSTREQ opSTRNE
|
||||
%left opSHL opSHR
|
||||
%left '+' '-'
|
||||
%left '*' '/' '%'
|
||||
%right '!' '~' opPLUSPLUS opMINUSMINUS UNARY
|
||||
%left '.'
|
||||
|
||||
%{
|
||||
/*
|
||||
Additional tokens
|
||||
|
||||
NOTE: These MUST be here, otherwise the definitions of the usual TorqueScript tokens changes
|
||||
which will break the compiler. Double check the standard TS tokens arent being redefined before
|
||||
testing a build.
|
||||
*/
|
||||
%}
|
||||
%token <i> rwTHEN rwEND rwBEGIN rwCFOR rwTO rwSTEP
|
||||
|
||||
%%
|
||||
|
||||
start
|
||||
: decl_list
|
||||
{ }
|
||||
;
|
||||
|
||||
decl_list
|
||||
:
|
||||
{ $$ = nil; }
|
||||
| decl_list decl
|
||||
{ if(!statementList) { statementList = $2; } else { statementList->append($2); } }
|
||||
;
|
||||
|
||||
decl
|
||||
: stmt
|
||||
{ $$ = $1; }
|
||||
| fn_decl_stmt
|
||||
{ $$ = $1; }
|
||||
| package_decl
|
||||
{ $$ = $1; }
|
||||
;
|
||||
|
||||
package_decl
|
||||
: rwPACKAGE IDENT fn_decl_list rwEND
|
||||
{ $$ = $3; for(StmtNode *walk = ($3);walk;walk = walk->getNext() ) walk->setPackage($2); }
|
||||
;
|
||||
|
||||
fn_decl_list
|
||||
: fn_decl_stmt
|
||||
{ $$ = $1; }
|
||||
| fn_decl_list fn_decl_stmt
|
||||
{ $$ = $1; ($1)->append($2); }
|
||||
;
|
||||
|
||||
statement_list
|
||||
:
|
||||
{ $$ = nil; }
|
||||
| statement_list stmt
|
||||
{ if(!$1) { $$ = $2; } else { ($1)->append($2); $$ = $1; } }
|
||||
;
|
||||
|
||||
stmt
|
||||
: if_stmt
|
||||
| while_stmt
|
||||
| for_stmt
|
||||
| datablock_decl
|
||||
| switch_stmt
|
||||
| rwBREAK semicolon
|
||||
{ $$ = BreakStmtNode::alloc(); }
|
||||
| rwCONTINUE semicolon
|
||||
{ $$ = ContinueStmtNode::alloc(); }
|
||||
| rwRETURN semicolon
|
||||
{ $$ = ReturnStmtNode::alloc(NULL); }
|
||||
| rwRETURN expr semicolon
|
||||
{ $$ = ReturnStmtNode::alloc($2); }
|
||||
| expression_stmt semicolon
|
||||
{ $$ = $1; }
|
||||
| TTAG '=' expr semicolon
|
||||
{ $$ = TTagSetStmtNode::alloc($1, $3, NULL); }
|
||||
| TTAG '=' expr ',' expr semicolon
|
||||
{ $$ = TTagSetStmtNode::alloc($1, $3, $5); }
|
||||
;
|
||||
|
||||
fn_decl_stmt
|
||||
: rwDEFINE IDENT '(' var_list_decl ')' statement_list rwEND
|
||||
{ $$ = FunctionDeclStmtNode::alloc($2, NULL, $4, $6); }
|
||||
| rwDEFINE IDENT opCOLONCOLON IDENT '(' var_list_decl ')' statement_list rwEND
|
||||
{ $$ = FunctionDeclStmtNode::alloc($4, $2, $6, $8); }
|
||||
;
|
||||
|
||||
var_list_decl
|
||||
:
|
||||
{ $$ = NULL; }
|
||||
| var_list
|
||||
{ $$ = $1; }
|
||||
;
|
||||
|
||||
var_list
|
||||
: VAR
|
||||
{ $$ = VarNode::alloc($1, NULL); }
|
||||
| var_list ',' VAR
|
||||
{ $$ = $1; ((StmtNode*)($1))->append((StmtNode*)VarNode::alloc($3, NULL)); }
|
||||
;
|
||||
|
||||
datablock_decl
|
||||
: rwDATABLOCK IDENT '(' IDENT parent_block ')' slot_assign_list rwEND
|
||||
{ $$ = ObjectDeclNode::alloc(ConstantNode::alloc($2), ConstantNode::alloc($4), NULL, $5, $7, NULL, true); }
|
||||
;
|
||||
|
||||
object_decl
|
||||
: rwDECLARE class_name_expr '(' object_name parent_block object_args ')' object_declare_block rwEND
|
||||
{ $$ = ObjectDeclNode::alloc($2, $4, $6, $5, $8.slots, $8.decls, false); }
|
||||
| rwDECLARE class_name_expr '(' object_name parent_block object_args ')' rwEND
|
||||
{ $$ = ObjectDeclNode::alloc($2, $4, $6, $5, NULL, NULL, false); }
|
||||
;
|
||||
|
||||
parent_block
|
||||
:
|
||||
{ $$ = NULL; }
|
||||
| ':' IDENT
|
||||
{ $$ = $2; }
|
||||
;
|
||||
|
||||
object_name
|
||||
:
|
||||
{ $$ = StrConstNode::alloc("", false); }
|
||||
| expr
|
||||
{ $$ = $1; }
|
||||
;
|
||||
|
||||
object_args
|
||||
:
|
||||
{ $$ = NULL; }
|
||||
| ',' expr_list
|
||||
{ $$ = $2; }
|
||||
;
|
||||
|
||||
object_declare_block
|
||||
:
|
||||
{ $$.slots = NULL; $$.decls = NULL; }
|
||||
| slot_assign_list
|
||||
{ $$.slots = $1; $$.decls = NULL; }
|
||||
| object_decl_list
|
||||
{ $$.slots = NULL; $$.decls = $1; }
|
||||
| slot_assign_list object_decl_list
|
||||
{ $$.slots = $1; $$.decls = $2; }
|
||||
;
|
||||
|
||||
object_decl_list
|
||||
: object_decl semicolon
|
||||
{ $$ = $1; }
|
||||
| object_decl_list object_decl semicolon
|
||||
{ $1->append($2); $$ = $1; }
|
||||
;
|
||||
|
||||
stmt_block
|
||||
: rwBEGIN statement_list rwEND
|
||||
{ $$ = $2; }
|
||||
| stmt
|
||||
{ $$ = $1; }
|
||||
;
|
||||
|
||||
switch_stmt
|
||||
: rwSWITCH expr case_block rwEND
|
||||
{ $$ = $3; $3->propagateSwitchExpr($2, false); }
|
||||
| rwSWITCHSTR expr case_block rwEND
|
||||
{ $$ = $3; $3->propagateSwitchExpr($2, true); }
|
||||
;
|
||||
|
||||
case_block
|
||||
: rwCASE case_expr ':' statement_list
|
||||
{ $$ = IfStmtNode::alloc($1, $2, $4, NULL, false); }
|
||||
| rwCASE case_expr ':' statement_list rwDEFAULT ':' statement_list
|
||||
{ $$ = IfStmtNode::alloc($1, $2, $4, $7, false); }
|
||||
| rwCASE case_expr ':' statement_list case_block
|
||||
{ $$ = IfStmtNode::alloc($1, $2, $4, $5, true); }
|
||||
;
|
||||
|
||||
case_expr
|
||||
: expr
|
||||
{ $$ = $1;}
|
||||
| case_expr rwCASEOR expr
|
||||
{ ($1)->append($3); $$=$1; }
|
||||
;
|
||||
|
||||
if_stmt
|
||||
: rwIF expr rwTHEN statement_list rwEND
|
||||
{ $$ = IfStmtNode::alloc($1, $2, $4, NULL, false); }
|
||||
| rwIF expr rwTHEN statement_list rwELSE statement_list rwEND
|
||||
{ $$ = IfStmtNode::alloc($1, $2, $4, $6, false); }
|
||||
;
|
||||
|
||||
while_stmt
|
||||
: rwWHILE expr rwBEGIN statement_list rwEND
|
||||
{ $$ = LoopStmtNode::alloc($1, nil, $2, nil, $4, false); }
|
||||
;
|
||||
|
||||
for_stmt
|
||||
: rwFOR expr ',' expr ',' expr rwBEGIN statement_list rwEND
|
||||
{ $$ = LoopStmtNode::alloc($1, $2, $4, $6, $8, false); }
|
||||
;
|
||||
|
||||
expression_stmt
|
||||
: stmt_expr
|
||||
{ $$ = $1; }
|
||||
;
|
||||
|
||||
expr
|
||||
: stmt_expr
|
||||
{ $$ = $1; }
|
||||
| '(' expr ')'
|
||||
{ $$ = $2; }
|
||||
| expr '^' expr
|
||||
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
|
||||
| expr '%' expr
|
||||
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
|
||||
| expr '&' expr
|
||||
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
|
||||
| expr '|' expr
|
||||
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
|
||||
| expr '+' expr
|
||||
{ $$ = FloatBinaryExprNode::alloc($2, $1, $3); }
|
||||
| expr '-' expr
|
||||
{ $$ = FloatBinaryExprNode::alloc($2, $1, $3); }
|
||||
| expr '*' expr
|
||||
{ $$ = FloatBinaryExprNode::alloc($2, $1, $3); }
|
||||
| expr '/' expr
|
||||
{ $$ = FloatBinaryExprNode::alloc($2, $1, $3); }
|
||||
| '-' expr %prec UNARY
|
||||
{ $$ = FloatUnaryExprNode::alloc($1, $2); }
|
||||
| '*' expr %prec UNARY
|
||||
{ $$ = TTagDerefNode::alloc($2); }
|
||||
| TTAG
|
||||
{ $$ = TTagExprNode::alloc($1); }
|
||||
| expr '?' expr ':' expr
|
||||
{ $$ = ConditionalExprNode::alloc($1, $3, $5); }
|
||||
| expr '<' expr
|
||||
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
|
||||
| expr '>' expr
|
||||
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
|
||||
| expr opGE expr
|
||||
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
|
||||
| expr opLE expr
|
||||
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
|
||||
| expr opEQ expr
|
||||
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
|
||||
| expr opNE expr
|
||||
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
|
||||
| expr opOR expr
|
||||
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
|
||||
| expr opSHL expr
|
||||
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
|
||||
| expr opSHR expr
|
||||
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
|
||||
| expr opAND expr
|
||||
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
|
||||
| expr opSTREQ expr
|
||||
{ $$ = StreqExprNode::alloc($1, $3, true); }
|
||||
| expr opSTRNE expr
|
||||
{ $$ = StreqExprNode::alloc($1, $3, false); }
|
||||
| expr '@' expr
|
||||
{ $$ = StrcatExprNode::alloc($1, $3, $2); }
|
||||
| '!' expr
|
||||
{ $$ = IntUnaryExprNode::alloc($1, $2); }
|
||||
| '~' expr
|
||||
{ $$ = IntUnaryExprNode::alloc($1, $2); }
|
||||
| TAGATOM
|
||||
{ $$ = StrConstNode::alloc($1, true); }
|
||||
| FLTCONST
|
||||
{ $$ = FloatNode::alloc($1); }
|
||||
| INTCONST
|
||||
{ $$ = IntNode::alloc($1); }
|
||||
| rwBREAK
|
||||
{ $$ = ConstantNode::alloc(StringTable->insert("break")); }
|
||||
| slot_acc
|
||||
{ $$ = SlotAccessNode::alloc($1.object, $1.array, $1.slotName); }
|
||||
| IDENT
|
||||
{ $$ = ConstantNode::alloc($1); }
|
||||
| STRATOM
|
||||
{ $$ = StrConstNode::alloc($1, false); }
|
||||
| VAR
|
||||
{ $$ = (ExprNode*)VarNode::alloc($1, NULL); }
|
||||
| VAR '[' aidx_expr ']'
|
||||
{ $$ = (ExprNode*)VarNode::alloc($1, $3); }
|
||||
;
|
||||
|
||||
slot_acc
|
||||
: expr '.' IDENT
|
||||
{ $$.object = $1; $$.slotName = $3; $$.array = NULL; }
|
||||
| expr '.' IDENT '[' aidx_expr ']'
|
||||
{ $$.object = $1; $$.slotName = $3; $$.array = $5; }
|
||||
;
|
||||
|
||||
class_name_expr
|
||||
: IDENT
|
||||
{ $$ = ConstantNode::alloc($1); }
|
||||
| '(' expr ')'
|
||||
{ $$ = $2; }
|
||||
;
|
||||
|
||||
assign_op_struct
|
||||
: opPLUSPLUS
|
||||
{ $$.token = '+'; $$.expr = FloatNode::alloc(1); }
|
||||
| opMINUSMINUS
|
||||
{ $$.token = '-'; $$.expr = FloatNode::alloc(1); }
|
||||
| opPLASN expr
|
||||
{ $$.token = '+'; $$.expr = $2; }
|
||||
| opMIASN expr
|
||||
{ $$.token = '-'; $$.expr = $2; }
|
||||
| opMLASN expr
|
||||
{ $$.token = '*'; $$.expr = $2; }
|
||||
| opDVASN expr
|
||||
{ $$.token = '/'; $$.expr = $2; }
|
||||
| opMODASN expr
|
||||
{ $$.token = '%'; $$.expr = $2; }
|
||||
| opANDASN expr
|
||||
{ $$.token = '&'; $$.expr = $2; }
|
||||
| opXORASN expr
|
||||
{ $$.token = '^'; $$.expr = $2; }
|
||||
| opORASN expr
|
||||
{ $$.token = '|'; $$.expr = $2; }
|
||||
| opSLASN expr
|
||||
{ $$.token = opSHL; $$.expr = $2; }
|
||||
| opSRASN expr
|
||||
{ $$.token = opSHR; $$.expr = $2; }
|
||||
;
|
||||
|
||||
stmt_expr
|
||||
: funcall_expr
|
||||
{ $$ = $1; }
|
||||
| object_decl
|
||||
{ $$ = $1; }
|
||||
| VAR '=' expr
|
||||
{ $$ = AssignExprNode::alloc($1, NULL, $3); }
|
||||
| VAR '[' aidx_expr ']' '=' expr
|
||||
{ $$ = AssignExprNode::alloc($1, $3, $6); }
|
||||
| VAR assign_op_struct
|
||||
{ $$ = AssignOpExprNode::alloc($1, NULL, $2.expr, $2.token); }
|
||||
| VAR '[' aidx_expr ']' assign_op_struct
|
||||
{ $$ = AssignOpExprNode::alloc($1, $3, $5.expr, $5.token); }
|
||||
| slot_acc assign_op_struct
|
||||
{ $$ = SlotAssignOpNode::alloc($1.object, $1.slotName, $1.array, $2.token, $2.expr); }
|
||||
| slot_acc '=' expr
|
||||
{ $$ = SlotAssignNode::alloc($1.object, $1.array, $1.slotName, $3); }
|
||||
| slot_acc '=' '{' expr_list '}'
|
||||
{ $$ = SlotAssignNode::alloc($1.object, $1.array, $1.slotName, $4); }
|
||||
;
|
||||
|
||||
funcall_expr
|
||||
: IDENT '(' expr_list_decl ')'
|
||||
{ $$ = FuncCallExprNode::alloc($1, NULL, $3, false); }
|
||||
| IDENT opCOLONCOLON IDENT '(' expr_list_decl ')'
|
||||
{ $$ = FuncCallExprNode::alloc($3, $1, $5, false); }
|
||||
| expr '.' IDENT '(' expr_list_decl ')'
|
||||
{ $1->append($5); $$ = FuncCallExprNode::alloc($3, NULL, $1, true); }
|
||||
;
|
||||
|
||||
expr_list_decl
|
||||
:
|
||||
{ $$ = NULL; }
|
||||
| expr_list
|
||||
{ $$ = $1; }
|
||||
;
|
||||
|
||||
expr_list
|
||||
: expr
|
||||
{ $$ = $1; }
|
||||
| expr_list ',' expr
|
||||
{ ($1)->append($3); $$ = $1; }
|
||||
;
|
||||
|
||||
slot_assign_list
|
||||
: slot_assign
|
||||
{ $$ = $1; }
|
||||
| slot_assign_list slot_assign
|
||||
{ $1->append($2); $$ = $1; }
|
||||
;
|
||||
|
||||
slot_assign
|
||||
: IDENT '=' expr semicolon
|
||||
{ $$ = SlotAssignNode::alloc(NULL, NULL, $1, $3); }
|
||||
| rwDATABLOCK '=' expr semicolon
|
||||
{ $$ = SlotAssignNode::alloc(NULL, NULL, StringTable->insert("datablock"), $3); }
|
||||
| IDENT '[' aidx_expr ']' '=' expr semicolon
|
||||
{ $$ = SlotAssignNode::alloc(NULL, $3, $1, $6); }
|
||||
;
|
||||
|
||||
aidx_expr
|
||||
: expr
|
||||
{ $$ = $1; }
|
||||
| aidx_expr ',' expr
|
||||
{ $$ = CommaCatExprNode::alloc($1, $3); }
|
||||
;
|
||||
|
||||
semicolon:
|
||||
| semicolon ';'
|
||||
;
|
||||
%%
|
||||
|
2190
engine/console/BASscan.cc
Executable file
2190
engine/console/BASscan.cc
Executable file
File diff suppressed because it is too large
Load Diff
289
engine/console/BASscan.l
Executable file
289
engine/console/BASscan.l
Executable file
@ -0,0 +1,289 @@
|
||||
%{
|
||||
#define YYLMAX 4096
|
||||
|
||||
#include <stdio.h>
|
||||
#include "platform/platform.h"
|
||||
#include "core/stringTable.h"
|
||||
#include "console/console.h"
|
||||
#define _CMDGRAM_H_
|
||||
#include "console/compiler.h"
|
||||
#include "console/basgram.h"
|
||||
|
||||
using namespace Compiler;
|
||||
|
||||
#define YY_NEVER_INTERACTIVE 1
|
||||
|
||||
// Some basic parsing primitives...
|
||||
static int Sc_ScanString(int ret);
|
||||
static int Sc_ScanNum();
|
||||
static int Sc_ScanVar();
|
||||
static int Sc_ScanHex();
|
||||
|
||||
// Deal with debuggability of FLEX.
|
||||
#ifdef TORQUE_DEBUG
|
||||
#define FLEX_DEBUG 1
|
||||
#else
|
||||
#define FLEX_DEBUG 0
|
||||
#endif
|
||||
|
||||
//#undef input
|
||||
//#undef unput
|
||||
#undef BASgetc
|
||||
int BASgetc();
|
||||
|
||||
#define YY_INPUT(buf,result,max_size) \
|
||||
{ \
|
||||
int c = '*', n; \
|
||||
for ( n = 0; n < max_size && \
|
||||
(c = BASgetc()) != EOF && c != '\n'; ++n ) \
|
||||
buf[n] = (char) c; \
|
||||
if ( c == '\n' ) \
|
||||
buf[n++] = (char) c; \
|
||||
result = n; \
|
||||
}
|
||||
|
||||
static int lineIndex;
|
||||
|
||||
// Prototypes
|
||||
void BASSetScanBuffer(const char *sb, const char *fn);
|
||||
void BASerror(char * s, ...);
|
||||
|
||||
// Error reporting
|
||||
void CMDerror(char * s, ...);
|
||||
|
||||
// Reset the parser.
|
||||
void CMDrestart(FILE *in);
|
||||
|
||||
%}
|
||||
|
||||
DIGIT [0-9]
|
||||
INTEGER {DIGIT}+
|
||||
FLOAT ({INTEGER}\.{INTEGER})|({INTEGER}(\.{INTEGER})?[eE][+-]?{INTEGER})
|
||||
LETTER [A-Za-z_]
|
||||
FILECHAR [A-Za-z_\.]
|
||||
VARMID [:A-Za-z0-9_]
|
||||
IDTAIL [A-Za-z0-9_]
|
||||
VARTAIL {VARMID}*{IDTAIL}
|
||||
VAR [$%]{LETTER}{VARTAIL}*
|
||||
ID {LETTER}{IDTAIL}*
|
||||
ILID [$%]{DIGIT}+{LETTER}{VARTAIL}*
|
||||
FILENAME {FILECHAR}+
|
||||
SPACE [ \t\v\f]
|
||||
HEXDIGIT [a-fA-F0-9]
|
||||
|
||||
%%
|
||||
;
|
||||
{SPACE}+ { }
|
||||
"//"[^\n\r]* ;
|
||||
"rem"{SPACE}+[^\n\r]* ;
|
||||
[\r] ;
|
||||
[\n] { lineIndex++; }
|
||||
\"(\\.|[^\\"\n\r])*\" { return(Sc_ScanString(STRATOM)); }
|
||||
\'(\\.|[^\\'\n\r])*\' { return(Sc_ScanString(TAGATOM)); }
|
||||
"==" return(BASlval.i = opEQ);
|
||||
"!=" return(BASlval.i = opNE);
|
||||
">=" return(BASlval.i = opGE);
|
||||
"<=" return(BASlval.i = opLE);
|
||||
"&&" return(BASlval.i = opAND);
|
||||
"||" return(BASlval.i = opOR);
|
||||
"::" return(BASlval.i = opCOLONCOLON);
|
||||
"--" return(BASlval.i = opMINUSMINUS);
|
||||
"++" return(BASlval.i = opPLUSPLUS);
|
||||
"$=" return(BASlval.i = opSTREQ);
|
||||
"!$=" return(BASlval.i = opSTRNE);
|
||||
"<<" return(BASlval.i = opSHL);
|
||||
">>" return(BASlval.i = opSHR);
|
||||
"+=" return(BASlval.i = opPLASN);
|
||||
"-=" return(BASlval.i = opMIASN);
|
||||
"*=" return(BASlval.i = opMLASN);
|
||||
"/=" return(BASlval.i = opDVASN);
|
||||
"%=" return(BASlval.i = opMODASN);
|
||||
"&=" return(BASlval.i = opANDASN);
|
||||
"^=" return(BASlval.i = opXORASN);
|
||||
"|=" return(BASlval.i = opORASN);
|
||||
"<<=" return(BASlval.i = opSLASN);
|
||||
">>=" return(BASlval.i = opSRASN);
|
||||
"NL" {BASlval.i = '\n'; return '@'; }
|
||||
"TAB" {BASlval.i = '\t'; return '@'; }
|
||||
"SPC" {BASlval.i = ' '; return '@'; }
|
||||
"@" {BASlval.i = 0; return '@'; }
|
||||
"?" |
|
||||
"[" |
|
||||
"]" |
|
||||
"(" |
|
||||
")" |
|
||||
"+" |
|
||||
"-" |
|
||||
"*" |
|
||||
"/" |
|
||||
"<" |
|
||||
">" |
|
||||
"|" |
|
||||
"." |
|
||||
"!" |
|
||||
":" |
|
||||
";" |
|
||||
"{" |
|
||||
"}" |
|
||||
"," |
|
||||
"&" |
|
||||
"%" |
|
||||
"^" |
|
||||
"~" |
|
||||
"=" { return(BASlval.i = BAStext[0]); }
|
||||
"or" { BASlval.i = lineIndex; return(rwCASEOR); }
|
||||
"break" { BASlval.i = lineIndex; return(rwBREAK); }
|
||||
"return" { BASlval.i = lineIndex; return(rwRETURN); }
|
||||
"else" { BASlval.i = lineIndex; return(rwELSE); }
|
||||
"while" { BASlval.i = lineIndex; return(rwWHILE); }
|
||||
"if" { BASlval.i = lineIndex; return(rwIF); }
|
||||
"then" { BASlval.i = lineIndex; return(rwTHEN); }
|
||||
"do" { BASlval.i = lineIndex; return(rwBEGIN); }
|
||||
"begin" { BASlval.i = lineIndex; return(rwBEGIN); }
|
||||
"end" { BASlval.i = lineIndex; return(rwEND); }
|
||||
"for" { BASlval.i = lineIndex; return(rwFOR); }
|
||||
"cfor" { BASlval.i = lineIndex; return(rwCFOR); }
|
||||
"to" { BASlval.i = lineIndex; return(rwTO); }
|
||||
"step" { BASlval.i = lineIndex; return(rwSTEP); }
|
||||
"continue" { BASlval.i = lineIndex; return(rwCONTINUE); }
|
||||
"function" { BASlval.i = lineIndex; return(rwDEFINE); }
|
||||
"sub" { BASlval.i = lineIndex; return(rwDEFINE); }
|
||||
"new" { BASlval.i = lineIndex; return(rwDECLARE); }
|
||||
"datablock" { BASlval.i = lineIndex; return(rwDATABLOCK); }
|
||||
"case" { BASlval.i = lineIndex; return(rwCASE); }
|
||||
"switch$" { BASlval.i = lineIndex; return(rwSWITCHSTR); }
|
||||
"switch" { BASlval.i = lineIndex; return(rwSWITCH); }
|
||||
"default" { BASlval.i = lineIndex; return(rwDEFAULT); }
|
||||
"package" { BASlval.i = lineIndex; return(rwPACKAGE); }
|
||||
"true" { BASlval.i = 1; return INTCONST; }
|
||||
"false" { BASlval.i = 0; return INTCONST; }
|
||||
{VAR} return(Sc_ScanVar());
|
||||
{ID} { BAStext[BASleng] = 0; BASlval.s = StringTable->insert(BAStext); return(IDENT); }
|
||||
0[xX]{HEXDIGIT}+ return(Sc_ScanHex());
|
||||
{INTEGER} { BAStext[BASleng] = 0; BASlval.i = atoi(BAStext); return INTCONST; }
|
||||
{FLOAT} return Sc_ScanNum();
|
||||
{ILID} return(ILLEGAL_TOKEN);
|
||||
. return(ILLEGAL_TOKEN);
|
||||
%%
|
||||
|
||||
/*
|
||||
* Scan character constant.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Scan identifier.
|
||||
*/
|
||||
|
||||
static const char *scanBuffer;
|
||||
static const char *fileName;
|
||||
static int scanIndex;
|
||||
|
||||
const char * BASGetCurrentFile()
|
||||
{
|
||||
return fileName;
|
||||
}
|
||||
|
||||
int BASGetCurrentLine()
|
||||
{
|
||||
return lineIndex;
|
||||
}
|
||||
|
||||
void BASerror(char *, ...)
|
||||
{
|
||||
gSyntaxError = true;
|
||||
if(fileName)
|
||||
Con::errorf(ConsoleLogEntry::Script, "%s Line: %d - Syntax error.",
|
||||
fileName, lineIndex);
|
||||
else
|
||||
Con::errorf(ConsoleLogEntry::Script, "Syntax error in input.");
|
||||
}
|
||||
|
||||
void BASSetScanBuffer(const char *sb, const char *fn)
|
||||
{
|
||||
scanBuffer = sb;
|
||||
fileName = fn;
|
||||
scanIndex = 0;
|
||||
lineIndex = 1;
|
||||
}
|
||||
|
||||
int BASgetc()
|
||||
{
|
||||
int ret = scanBuffer[scanIndex];
|
||||
if(ret)
|
||||
scanIndex++;
|
||||
else
|
||||
ret = -1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int BASwrap()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int Sc_ScanVar()
|
||||
{
|
||||
BAStext[BASleng] = 0;
|
||||
BASlval.s = StringTable->insert(BAStext);
|
||||
return(VAR);
|
||||
}
|
||||
/*
|
||||
* Scan string constant.
|
||||
*/
|
||||
|
||||
// Defined in CS_CMD.l
|
||||
extern void expandEscape(char *dest, const char *src);
|
||||
extern bool collapseEscape(char *buf);
|
||||
|
||||
static int charConv(int in)
|
||||
{
|
||||
switch(in)
|
||||
{
|
||||
case 'r':
|
||||
return '\r';
|
||||
case 'n':
|
||||
return '\n';
|
||||
case 't':
|
||||
return '\t';
|
||||
default:
|
||||
return in;
|
||||
}
|
||||
}
|
||||
|
||||
static int getHexDigit(char c)
|
||||
{
|
||||
if(c >= '0' && c <= '9')
|
||||
return c - '0';
|
||||
if(c >= 'A' && c <= 'F')
|
||||
return c - 'A' + 10;
|
||||
if(c >= 'a' && c <= 'f')
|
||||
return c - 'a' + 10;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int Sc_ScanString(int ret)
|
||||
{
|
||||
BAStext[BASleng - 1] = 0;
|
||||
if(!collapseEscape(BAStext+1))
|
||||
return -1;
|
||||
BASlval.str = (char *) consoleAlloc(dStrlen(BAStext));
|
||||
dStrcpy(BASlval.str, BAStext + 1);
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
static int Sc_ScanNum()
|
||||
{
|
||||
BAStext[BASleng] = 0;
|
||||
BASlval.f = atof(BAStext);
|
||||
return(FLTCONST);
|
||||
}
|
||||
|
||||
static int Sc_ScanHex()
|
||||
{
|
||||
int val = 0;
|
||||
dSscanf(BAStext, "%x", &val);
|
||||
BASlval.i = val;
|
||||
return INTCONST;
|
||||
}
|
||||
|
2140
engine/console/CMDgram.cc
Executable file
2140
engine/console/CMDgram.cc
Executable file
File diff suppressed because it is too large
Load Diff
526
engine/console/CMDgram.y
Executable file
526
engine/console/CMDgram.y
Executable file
@ -0,0 +1,526 @@
|
||||
%{
|
||||
|
||||
// Make sure we don't get gram.h twice.
|
||||
#define _CMDGRAM_H_
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "console/console.h"
|
||||
#include "console/compiler.h"
|
||||
#include "console/consoleInternal.h"
|
||||
|
||||
#ifndef YYDEBUG
|
||||
#define YYDEBUG 0
|
||||
#endif
|
||||
|
||||
#define YYSSIZE 350
|
||||
|
||||
int outtext(char *fmt, ...);
|
||||
extern int serrors;
|
||||
|
||||
#define nil 0
|
||||
#undef YY_ARGS
|
||||
#define YY_ARGS(x) x
|
||||
|
||||
int CMDlex();
|
||||
void CMDerror(char *, ...);
|
||||
|
||||
#define alloca dMalloc
|
||||
|
||||
%}
|
||||
%{
|
||||
/* Reserved Word Definitions */
|
||||
%}
|
||||
%token <i> rwDEFINE rwENDDEF rwDECLARE
|
||||
%token <i> rwBREAK rwELSE rwCONTINUE rwGLOBAL
|
||||
%token <i> rwIF rwNIL rwRETURN rwWHILE rwDO
|
||||
%token <i> rwENDIF rwENDWHILE rwENDFOR rwDEFAULT
|
||||
%token <i> rwFOR rwDATABLOCK rwSWITCH rwCASE rwSWITCHSTR
|
||||
%token <i> rwCASEOR rwPACKAGE rwNAMESPACE rwCLASS
|
||||
%token ILLEGAL_TOKEN
|
||||
%{
|
||||
/* Constants and Identifier Definitions */
|
||||
%}
|
||||
%token <c> CHRCONST
|
||||
%token <i> INTCONST
|
||||
%token <s> TTAG
|
||||
%token <s> VAR
|
||||
%token <s> IDENT
|
||||
%token <str> STRATOM
|
||||
%token <str> TAGATOM
|
||||
%token <f> FLTCONST
|
||||
|
||||
%{
|
||||
/* Operator Definitions */
|
||||
%}
|
||||
%token <i> '+' '-' '*' '/' '<' '>' '=' '.' '|' '&' '%'
|
||||
%token <i> '(' ')' ',' ':' ';' '{' '}' '^' '~' '!' '@'
|
||||
%token <i> opMINUSMINUS opPLUSPLUS
|
||||
%token <i> STMT_SEP
|
||||
%token <i> opSHL opSHR opPLASN opMIASN opMLASN opDVASN opMODASN opANDASN
|
||||
%token <i> opXORASN opORASN opSLASN opSRASN opCAT
|
||||
%token <i> opEQ opNE opGE opLE opAND opOR opSTREQ
|
||||
%token <i> opCOLONCOLON
|
||||
|
||||
%union {
|
||||
char c;
|
||||
int i;
|
||||
const char * s;
|
||||
char * str;
|
||||
double f;
|
||||
StmtNode * stmt;
|
||||
ExprNode * expr;
|
||||
SlotAssignNode * slist;
|
||||
VarNode * var;
|
||||
SlotDecl slot;
|
||||
ObjectBlockDecl odcl;
|
||||
ObjectDeclNode * od;
|
||||
AssignDecl asn;
|
||||
IfStmtNode * ifnode;
|
||||
}
|
||||
|
||||
%type <s> parent_block
|
||||
%type <ifnode> case_block
|
||||
%type <stmt> switch_stmt
|
||||
%type <stmt> decl
|
||||
%type <stmt> decl_list
|
||||
%type <stmt> package_decl
|
||||
%type <stmt> fn_decl_stmt
|
||||
%type <stmt> fn_decl_list
|
||||
%type <stmt> statement_list
|
||||
%type <stmt> stmt
|
||||
%type <expr> expr_list
|
||||
%type <expr> expr_list_decl
|
||||
%type <expr> aidx_expr
|
||||
%type <expr> funcall_expr
|
||||
%type <expr> object_name
|
||||
%type <expr> object_args
|
||||
%type <expr> stmt_expr
|
||||
%type <expr> case_expr
|
||||
%type <expr> class_name_expr
|
||||
%type <stmt> if_stmt
|
||||
%type <stmt> while_stmt
|
||||
%type <stmt> for_stmt
|
||||
%type <stmt> stmt_block
|
||||
%type <stmt> datablock_decl
|
||||
%type <od> object_decl
|
||||
%type <od> object_decl_list
|
||||
%type <odcl> object_declare_block
|
||||
%type <expr> expr
|
||||
%type <slist> slot_assign_list
|
||||
%type <slist> slot_assign
|
||||
%type <slot> slot_acc
|
||||
%type <stmt> expression_stmt
|
||||
%type <var> var_list
|
||||
%type <var> var_list_decl
|
||||
%type <asn> assign_op_struct
|
||||
|
||||
%left '['
|
||||
%right opMODASN opANDASN opXORASN opPLASN opMIASN opMLASN opDVASN opMDASN opNDASN opNTASN opORASN opSLASN opSRASN '='
|
||||
%left '?' ':'
|
||||
%left opOR
|
||||
%left opAND
|
||||
%left '|'
|
||||
%left '^'
|
||||
%left '&'
|
||||
%left opEQ opNE
|
||||
%left '<' opLE '>' opGE
|
||||
%left '@' opCAT opSTREQ opSTRNE
|
||||
%left opSHL opSHR
|
||||
%left '+' '-'
|
||||
%left '*' '/' '%'
|
||||
%right '!' '~' opPLUSPLUS opMINUSMINUS UNARY
|
||||
%left '.'
|
||||
|
||||
%%
|
||||
|
||||
start
|
||||
: decl_list
|
||||
{ }
|
||||
;
|
||||
|
||||
decl_list
|
||||
:
|
||||
{ $$ = nil; }
|
||||
| decl_list decl
|
||||
{ if(!statementList) { statementList = $2; } else { statementList->append($2); } }
|
||||
;
|
||||
|
||||
decl
|
||||
: stmt
|
||||
{ $$ = $1; }
|
||||
| fn_decl_stmt
|
||||
{ $$ = $1; }
|
||||
| package_decl
|
||||
{ $$ = $1; }
|
||||
;
|
||||
|
||||
package_decl
|
||||
: rwPACKAGE IDENT '{' fn_decl_list '}' ';'
|
||||
{ $$ = $4; for(StmtNode *walk = ($4);walk;walk = walk->getNext() ) walk->setPackage($2); }
|
||||
;
|
||||
|
||||
fn_decl_list
|
||||
: fn_decl_stmt
|
||||
{ $$ = $1; }
|
||||
| fn_decl_list fn_decl_stmt
|
||||
{ $$ = $1; ($1)->append($2); }
|
||||
;
|
||||
|
||||
statement_list
|
||||
:
|
||||
{ $$ = nil; }
|
||||
| statement_list stmt
|
||||
{ if(!$1) { $$ = $2; } else { ($1)->append($2); $$ = $1; } }
|
||||
;
|
||||
|
||||
stmt
|
||||
: if_stmt
|
||||
| while_stmt
|
||||
| for_stmt
|
||||
| datablock_decl
|
||||
| switch_stmt
|
||||
| rwBREAK ';'
|
||||
{ $$ = BreakStmtNode::alloc(); }
|
||||
| rwCONTINUE ';'
|
||||
{ $$ = ContinueStmtNode::alloc(); }
|
||||
| rwRETURN ';'
|
||||
{ $$ = ReturnStmtNode::alloc(NULL); }
|
||||
| rwRETURN expr ';'
|
||||
{ $$ = ReturnStmtNode::alloc($2); }
|
||||
| expression_stmt ';'
|
||||
{ $$ = $1; }
|
||||
| TTAG '=' expr ';'
|
||||
{ $$ = TTagSetStmtNode::alloc($1, $3, NULL); }
|
||||
| TTAG '=' expr ',' expr ';'
|
||||
{ $$ = TTagSetStmtNode::alloc($1, $3, $5); }
|
||||
;
|
||||
|
||||
fn_decl_stmt
|
||||
: rwDEFINE IDENT '(' var_list_decl ')' '{' statement_list '}'
|
||||
{ $$ = FunctionDeclStmtNode::alloc($2, NULL, $4, $7); }
|
||||
| rwDEFINE IDENT opCOLONCOLON IDENT '(' var_list_decl ')' '{' statement_list '}'
|
||||
{ $$ = FunctionDeclStmtNode::alloc($4, $2, $6, $9); }
|
||||
;
|
||||
|
||||
var_list_decl
|
||||
:
|
||||
{ $$ = NULL; }
|
||||
| var_list
|
||||
{ $$ = $1; }
|
||||
;
|
||||
|
||||
var_list
|
||||
: VAR
|
||||
{ $$ = VarNode::alloc($1, NULL); }
|
||||
| var_list ',' VAR
|
||||
{ $$ = $1; ((StmtNode*)($1))->append((StmtNode*)VarNode::alloc($3, NULL)); }
|
||||
;
|
||||
|
||||
datablock_decl
|
||||
: rwDATABLOCK IDENT '(' IDENT parent_block ')' '{' slot_assign_list '}' ';'
|
||||
{ $$ = ObjectDeclNode::alloc(ConstantNode::alloc($2), ConstantNode::alloc($4), NULL, $5, $8, NULL, true); }
|
||||
;
|
||||
|
||||
object_decl
|
||||
: rwDECLARE class_name_expr '(' object_name parent_block object_args ')' '{' object_declare_block '}'
|
||||
{ $$ = ObjectDeclNode::alloc($2, $4, $6, $5, $9.slots, $9.decls, false); }
|
||||
| rwDECLARE class_name_expr '(' object_name parent_block object_args ')'
|
||||
{ $$ = ObjectDeclNode::alloc($2, $4, $6, $5, NULL, NULL, false); }
|
||||
;
|
||||
|
||||
parent_block
|
||||
:
|
||||
{ $$ = NULL; }
|
||||
| ':' IDENT
|
||||
{ $$ = $2; }
|
||||
;
|
||||
|
||||
object_name
|
||||
:
|
||||
{ $$ = StrConstNode::alloc("", false); }
|
||||
| expr
|
||||
{ $$ = $1; }
|
||||
;
|
||||
|
||||
object_args
|
||||
:
|
||||
{ $$ = NULL; }
|
||||
| ',' expr_list
|
||||
{ $$ = $2; }
|
||||
;
|
||||
|
||||
object_declare_block
|
||||
:
|
||||
{ $$.slots = NULL; $$.decls = NULL; }
|
||||
| slot_assign_list
|
||||
{ $$.slots = $1; $$.decls = NULL; }
|
||||
| object_decl_list
|
||||
{ $$.slots = NULL; $$.decls = $1; }
|
||||
| slot_assign_list object_decl_list
|
||||
{ $$.slots = $1; $$.decls = $2; }
|
||||
;
|
||||
|
||||
object_decl_list
|
||||
: object_decl ';'
|
||||
{ $$ = $1; }
|
||||
| object_decl_list object_decl ';'
|
||||
{ $1->append($2); $$ = $1; }
|
||||
;
|
||||
|
||||
stmt_block
|
||||
: '{' statement_list '}'
|
||||
{ $$ = $2; }
|
||||
| stmt
|
||||
{ $$ = $1; }
|
||||
;
|
||||
|
||||
switch_stmt
|
||||
: rwSWITCH '(' expr ')' '{' case_block '}'
|
||||
{ $$ = $6; $6->propagateSwitchExpr($3, false); }
|
||||
| rwSWITCHSTR '(' expr ')' '{' case_block '}'
|
||||
{ $$ = $6; $6->propagateSwitchExpr($3, true); }
|
||||
;
|
||||
|
||||
case_block
|
||||
: rwCASE case_expr ':' statement_list
|
||||
{ $$ = IfStmtNode::alloc($1, $2, $4, NULL, false); }
|
||||
| rwCASE case_expr ':' statement_list rwDEFAULT ':' statement_list
|
||||
{ $$ = IfStmtNode::alloc($1, $2, $4, $7, false); }
|
||||
| rwCASE case_expr ':' statement_list case_block
|
||||
{ $$ = IfStmtNode::alloc($1, $2, $4, $5, true); }
|
||||
;
|
||||
|
||||
case_expr
|
||||
: expr
|
||||
{ $$ = $1;}
|
||||
| case_expr rwCASEOR expr
|
||||
{ ($1)->append($3); $$=$1; }
|
||||
;
|
||||
|
||||
if_stmt
|
||||
: rwIF '(' expr ')' stmt_block
|
||||
{ $$ = IfStmtNode::alloc($1, $3, $5, NULL, false); }
|
||||
| rwIF '(' expr ')' stmt_block rwELSE stmt_block
|
||||
{ $$ = IfStmtNode::alloc($1, $3, $5, $7, false); }
|
||||
;
|
||||
|
||||
while_stmt
|
||||
: rwWHILE '(' expr ')' stmt_block
|
||||
{ $$ = LoopStmtNode::alloc($1, nil, $3, nil, $5, false); }
|
||||
| rwDO stmt_block rwWHILE '(' expr ')'
|
||||
{ $$ = LoopStmtNode::alloc($3, nil, $5, nil, $2, true); }
|
||||
;
|
||||
|
||||
for_stmt
|
||||
: rwFOR '(' expr ';' expr ';' expr ')' stmt_block
|
||||
{ $$ = LoopStmtNode::alloc($1, $3, $5, $7, $9, false); }
|
||||
| rwFOR '(' expr ';' expr ';' ')' stmt_block
|
||||
{ $$ = LoopStmtNode::alloc($1, $3, $5, NULL, $8, false); }
|
||||
| rwFOR '(' expr ';' ';' expr ')' stmt_block
|
||||
{ $$ = LoopStmtNode::alloc($1, $3, NULL, $6, $8, false); }
|
||||
| rwFOR '(' expr ';' ';' ')' stmt_block
|
||||
{ $$ = LoopStmtNode::alloc($1, $3, NULL, NULL, $7, false); }
|
||||
| rwFOR '(' ';' expr ';' expr ')' stmt_block
|
||||
{ $$ = LoopStmtNode::alloc($1, NULL, $4, $6, $8, false); }
|
||||
| rwFOR '(' ';' expr ';' ')' stmt_block
|
||||
{ $$ = LoopStmtNode::alloc($1, NULL, $4, NULL, $7, false); }
|
||||
| rwFOR '(' ';' ';' expr ')' stmt_block
|
||||
{ $$ = LoopStmtNode::alloc($1, NULL, NULL, $5, $7, false); }
|
||||
| rwFOR '(' ';' ';' ')' stmt_block
|
||||
{ $$ = LoopStmtNode::alloc($1, NULL, NULL, NULL, $6, false); }
|
||||
;
|
||||
|
||||
expression_stmt
|
||||
: stmt_expr
|
||||
{ $$ = $1; }
|
||||
;
|
||||
|
||||
expr
|
||||
: stmt_expr
|
||||
{ $$ = $1; }
|
||||
| '(' expr ')'
|
||||
{ $$ = $2; }
|
||||
| expr '^' expr
|
||||
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
|
||||
| expr '%' expr
|
||||
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
|
||||
| expr '&' expr
|
||||
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
|
||||
| expr '|' expr
|
||||
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
|
||||
| expr '+' expr
|
||||
{ $$ = FloatBinaryExprNode::alloc($2, $1, $3); }
|
||||
| expr '-' expr
|
||||
{ $$ = FloatBinaryExprNode::alloc($2, $1, $3); }
|
||||
| expr '*' expr
|
||||
{ $$ = FloatBinaryExprNode::alloc($2, $1, $3); }
|
||||
| expr '/' expr
|
||||
{ $$ = FloatBinaryExprNode::alloc($2, $1, $3); }
|
||||
| '-' expr %prec UNARY
|
||||
{ $$ = FloatUnaryExprNode::alloc($1, $2); }
|
||||
| '*' expr %prec UNARY
|
||||
{ $$ = TTagDerefNode::alloc($2); }
|
||||
| TTAG
|
||||
{ $$ = TTagExprNode::alloc($1); }
|
||||
| expr '?' expr ':' expr
|
||||
{ $$ = ConditionalExprNode::alloc($1, $3, $5); }
|
||||
| expr '<' expr
|
||||
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
|
||||
| expr '>' expr
|
||||
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
|
||||
| expr opGE expr
|
||||
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
|
||||
| expr opLE expr
|
||||
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
|
||||
| expr opEQ expr
|
||||
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
|
||||
| expr opNE expr
|
||||
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
|
||||
| expr opOR expr
|
||||
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
|
||||
| expr opSHL expr
|
||||
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
|
||||
| expr opSHR expr
|
||||
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
|
||||
| expr opAND expr
|
||||
{ $$ = IntBinaryExprNode::alloc($2, $1, $3); }
|
||||
| expr opSTREQ expr
|
||||
{ $$ = StreqExprNode::alloc($1, $3, true); }
|
||||
| expr opSTRNE expr
|
||||
{ $$ = StreqExprNode::alloc($1, $3, false); }
|
||||
| expr '@' expr
|
||||
{ $$ = StrcatExprNode::alloc($1, $3, $2); }
|
||||
| '!' expr
|
||||
{ $$ = IntUnaryExprNode::alloc($1, $2); }
|
||||
| '~' expr
|
||||
{ $$ = IntUnaryExprNode::alloc($1, $2); }
|
||||
| TAGATOM
|
||||
{ $$ = StrConstNode::alloc($1, true); }
|
||||
| FLTCONST
|
||||
{ $$ = FloatNode::alloc($1); }
|
||||
| INTCONST
|
||||
{ $$ = IntNode::alloc($1); }
|
||||
| rwBREAK
|
||||
{ $$ = ConstantNode::alloc(StringTable->insert("break")); }
|
||||
| slot_acc
|
||||
{ $$ = SlotAccessNode::alloc($1.object, $1.array, $1.slotName); }
|
||||
| IDENT
|
||||
{ $$ = ConstantNode::alloc($1); }
|
||||
| STRATOM
|
||||
{ $$ = StrConstNode::alloc($1, false); }
|
||||
| VAR
|
||||
{ $$ = (ExprNode*)VarNode::alloc($1, NULL); }
|
||||
| VAR '[' aidx_expr ']'
|
||||
{ $$ = (ExprNode*)VarNode::alloc($1, $3); }
|
||||
;
|
||||
|
||||
slot_acc
|
||||
: expr '.' IDENT
|
||||
{ $$.object = $1; $$.slotName = $3; $$.array = NULL; }
|
||||
| expr '.' IDENT '[' aidx_expr ']'
|
||||
{ $$.object = $1; $$.slotName = $3; $$.array = $5; }
|
||||
;
|
||||
|
||||
class_name_expr
|
||||
: IDENT
|
||||
{ $$ = ConstantNode::alloc($1); }
|
||||
| '(' expr ')'
|
||||
{ $$ = $2; }
|
||||
;
|
||||
|
||||
assign_op_struct
|
||||
: opPLUSPLUS
|
||||
{ $$.token = '+'; $$.expr = FloatNode::alloc(1); }
|
||||
| opMINUSMINUS
|
||||
{ $$.token = '-'; $$.expr = FloatNode::alloc(1); }
|
||||
| opPLASN expr
|
||||
{ $$.token = '+'; $$.expr = $2; }
|
||||
| opMIASN expr
|
||||
{ $$.token = '-'; $$.expr = $2; }
|
||||
| opMLASN expr
|
||||
{ $$.token = '*'; $$.expr = $2; }
|
||||
| opDVASN expr
|
||||
{ $$.token = '/'; $$.expr = $2; }
|
||||
| opMODASN expr
|
||||
{ $$.token = '%'; $$.expr = $2; }
|
||||
| opANDASN expr
|
||||
{ $$.token = '&'; $$.expr = $2; }
|
||||
| opXORASN expr
|
||||
{ $$.token = '^'; $$.expr = $2; }
|
||||
| opORASN expr
|
||||
{ $$.token = '|'; $$.expr = $2; }
|
||||
| opSLASN expr
|
||||
{ $$.token = opSHL; $$.expr = $2; }
|
||||
| opSRASN expr
|
||||
{ $$.token = opSHR; $$.expr = $2; }
|
||||
;
|
||||
|
||||
stmt_expr
|
||||
: funcall_expr
|
||||
{ $$ = $1; }
|
||||
| object_decl
|
||||
{ $$ = $1; }
|
||||
| VAR '=' expr
|
||||
{ $$ = AssignExprNode::alloc($1, NULL, $3); }
|
||||
| VAR '[' aidx_expr ']' '=' expr
|
||||
{ $$ = AssignExprNode::alloc($1, $3, $6); }
|
||||
| VAR assign_op_struct
|
||||
{ $$ = AssignOpExprNode::alloc($1, NULL, $2.expr, $2.token); }
|
||||
| VAR '[' aidx_expr ']' assign_op_struct
|
||||
{ $$ = AssignOpExprNode::alloc($1, $3, $5.expr, $5.token); }
|
||||
| slot_acc assign_op_struct
|
||||
{ $$ = SlotAssignOpNode::alloc($1.object, $1.slotName, $1.array, $2.token, $2.expr); }
|
||||
| slot_acc '=' expr
|
||||
{ $$ = SlotAssignNode::alloc($1.object, $1.array, $1.slotName, $3); }
|
||||
| slot_acc '=' '{' expr_list '}'
|
||||
{ $$ = SlotAssignNode::alloc($1.object, $1.array, $1.slotName, $4); }
|
||||
;
|
||||
|
||||
funcall_expr
|
||||
: IDENT '(' expr_list_decl ')'
|
||||
{ $$ = FuncCallExprNode::alloc($1, NULL, $3, false); }
|
||||
| IDENT opCOLONCOLON IDENT '(' expr_list_decl ')'
|
||||
{ $$ = FuncCallExprNode::alloc($3, $1, $5, false); }
|
||||
| expr '.' IDENT '(' expr_list_decl ')'
|
||||
{ $1->append($5); $$ = FuncCallExprNode::alloc($3, NULL, $1, true); }
|
||||
;
|
||||
|
||||
expr_list_decl
|
||||
:
|
||||
{ $$ = NULL; }
|
||||
| expr_list
|
||||
{ $$ = $1; }
|
||||
;
|
||||
|
||||
expr_list
|
||||
: expr
|
||||
{ $$ = $1; }
|
||||
| expr_list ',' expr
|
||||
{ ($1)->append($3); $$ = $1; }
|
||||
;
|
||||
|
||||
slot_assign_list
|
||||
: slot_assign
|
||||
{ $$ = $1; }
|
||||
| slot_assign_list slot_assign
|
||||
{ $1->append($2); $$ = $1; }
|
||||
;
|
||||
|
||||
slot_assign
|
||||
: IDENT '=' expr ';'
|
||||
{ $$ = SlotAssignNode::alloc(NULL, NULL, $1, $3); }
|
||||
| rwDATABLOCK '=' expr ';'
|
||||
{ $$ = SlotAssignNode::alloc(NULL, NULL, StringTable->insert("datablock"), $3); }
|
||||
| IDENT '[' aidx_expr ']' '=' expr ';'
|
||||
{ $$ = SlotAssignNode::alloc(NULL, $3, $1, $6); }
|
||||
;
|
||||
|
||||
aidx_expr
|
||||
: expr
|
||||
{ $$ = $1; }
|
||||
| aidx_expr ',' expr
|
||||
{ $$ = CommaCatExprNode::alloc($1, $3); }
|
||||
;
|
||||
%%
|
||||
|
2336
engine/console/CMDscan.cc
Executable file
2336
engine/console/CMDscan.cc
Executable file
File diff suppressed because it is too large
Load Diff
466
engine/console/CMDscan.l
Executable file
466
engine/console/CMDscan.l
Executable file
@ -0,0 +1,466 @@
|
||||
%{
|
||||
#define YYLMAX 4096
|
||||
|
||||
#include <stdio.h>
|
||||
#include "platform/platform.h"
|
||||
#include "core/stringTable.h"
|
||||
#include "console/console.h"
|
||||
#include "console/compiler.h"
|
||||
|
||||
using namespace Compiler;
|
||||
|
||||
#define YY_NEVER_INTERACTIVE 1
|
||||
|
||||
// Some basic parsing primitives...
|
||||
static int Sc_ScanString(int ret);
|
||||
static int Sc_ScanNum();
|
||||
static int Sc_ScanVar();
|
||||
static int Sc_ScanHex();
|
||||
|
||||
// Deal with debuggability of FLEX.
|
||||
#ifdef TORQUE_DEBUG
|
||||
#define FLEX_DEBUG 1
|
||||
#else
|
||||
#define FLEX_DEBUG 0
|
||||
#endif
|
||||
|
||||
// Install our own input code...
|
||||
#undef CMDgetc
|
||||
int CMDgetc();
|
||||
|
||||
// Hack to make windows lex happy.
|
||||
#ifndef isatty
|
||||
inline int isatty(int) { return 0; }
|
||||
#endif
|
||||
|
||||
// Wrap our getc, so that lex doesn't try to do its own buffering/file IO.
|
||||
#define YY_INPUT(buf,result,max_size) \
|
||||
{ \
|
||||
int c = '*', n; \
|
||||
for ( n = 0; n < max_size && \
|
||||
(c = CMDgetc()) != EOF && c != '\n'; ++n ) \
|
||||
buf[n] = (char) c; \
|
||||
if ( c == '\n' ) \
|
||||
buf[n++] = (char) c; \
|
||||
result = n; \
|
||||
}
|
||||
|
||||
// General helper stuff.
|
||||
static int lineIndex;
|
||||
|
||||
// File state
|
||||
void CMDSetScanBuffer(const char *sb, const char *fn);
|
||||
const char * CMDgetFileLine(int &lineNumber);
|
||||
|
||||
// Error reporting
|
||||
void CMDerror(char * s, ...);
|
||||
|
||||
// Reset the parser.
|
||||
void CMDrestart(FILE *in);
|
||||
|
||||
%}
|
||||
|
||||
DIGIT [0-9]
|
||||
INTEGER {DIGIT}+
|
||||
FLOAT ({INTEGER}\.{INTEGER})|({INTEGER}(\.{INTEGER})?[eE][+-]?{INTEGER})
|
||||
LETTER [A-Za-z_]
|
||||
FILECHAR [A-Za-z_\.]
|
||||
VARMID [:A-Za-z0-9_]
|
||||
IDTAIL [A-Za-z0-9_]
|
||||
VARTAIL {VARMID}*{IDTAIL}
|
||||
VAR [$%]{LETTER}{VARTAIL}*
|
||||
ID {LETTER}{IDTAIL}*
|
||||
ILID [$%]{DIGIT}+{LETTER}{VARTAIL}*
|
||||
FILENAME {FILECHAR}+
|
||||
SPACE [ \t\v\f]
|
||||
HEXDIGIT [a-fA-F0-9]
|
||||
|
||||
%%
|
||||
;
|
||||
{SPACE}+ { }
|
||||
"//"[^\n\r]* ;
|
||||
[\r] ;
|
||||
[\n] {lineIndex++;}
|
||||
\"(\\.|[^\\"\n\r])*\" { return(Sc_ScanString(STRATOM)); }
|
||||
\'(\\.|[^\\'\n\r])*\' { return(Sc_ScanString(TAGATOM)); }
|
||||
"==" return(CMDlval.i = opEQ);
|
||||
"!=" return(CMDlval.i = opNE);
|
||||
">=" return(CMDlval.i = opGE);
|
||||
"<=" return(CMDlval.i = opLE);
|
||||
"&&" return(CMDlval.i = opAND);
|
||||
"||" return(CMDlval.i = opOR);
|
||||
"::" return(CMDlval.i = opCOLONCOLON);
|
||||
"--" return(CMDlval.i = opMINUSMINUS);
|
||||
"++" return(CMDlval.i = opPLUSPLUS);
|
||||
"$=" return(CMDlval.i = opSTREQ);
|
||||
"!$=" return(CMDlval.i = opSTRNE);
|
||||
"<<" return(CMDlval.i = opSHL);
|
||||
">>" return(CMDlval.i = opSHR);
|
||||
"+=" return(CMDlval.i = opPLASN);
|
||||
"-=" return(CMDlval.i = opMIASN);
|
||||
"*=" return(CMDlval.i = opMLASN);
|
||||
"/=" return(CMDlval.i = opDVASN);
|
||||
"%=" return(CMDlval.i = opMODASN);
|
||||
"&=" return(CMDlval.i = opANDASN);
|
||||
"^=" return(CMDlval.i = opXORASN);
|
||||
"|=" return(CMDlval.i = opORASN);
|
||||
"<<=" return(CMDlval.i = opSLASN);
|
||||
">>=" return(CMDlval.i = opSRASN);
|
||||
"NL" {CMDlval.i = '\n'; return '@'; }
|
||||
"TAB" {CMDlval.i = '\t'; return '@'; }
|
||||
"SPC" {CMDlval.i = ' '; return '@'; }
|
||||
"@" {CMDlval.i = 0; return '@'; }
|
||||
"/*" {
|
||||
register int c;
|
||||
for ( ; ; )
|
||||
{
|
||||
while ( (c = CMDgetc()) != '*' && c != EOF )
|
||||
; /* eat up text of comment */
|
||||
|
||||
if ( c == '*' )
|
||||
{
|
||||
while ( (c = CMDgetc()) == '*' )
|
||||
;
|
||||
if ( c == '/' )
|
||||
break; /* found the end */
|
||||
}
|
||||
|
||||
if ( c == EOF )
|
||||
{
|
||||
CMDerror( "EOF in comment" );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
"?" |
|
||||
"[" |
|
||||
"]" |
|
||||
"(" |
|
||||
")" |
|
||||
"+" |
|
||||
"-" |
|
||||
"*" |
|
||||
"/" |
|
||||
"<" |
|
||||
">" |
|
||||
"|" |
|
||||
"." |
|
||||
"!" |
|
||||
":" |
|
||||
";" |
|
||||
"{" |
|
||||
"}" |
|
||||
"," |
|
||||
"&" |
|
||||
"%" |
|
||||
"^" |
|
||||
"~" |
|
||||
"=" { return(CMDlval.i = CMDtext[0]); }
|
||||
"or" { CMDlval.i = lineIndex; return(rwCASEOR); }
|
||||
"break" { CMDlval.i = lineIndex; return(rwBREAK); }
|
||||
"return" { CMDlval.i = lineIndex; return(rwRETURN); }
|
||||
"else" { CMDlval.i = lineIndex; return(rwELSE); }
|
||||
"while" { CMDlval.i = lineIndex; return(rwWHILE); }
|
||||
"do" { CMDlval.i = lineIndex; return(rwDO); }
|
||||
"if" { CMDlval.i = lineIndex; return(rwIF); }
|
||||
"for" { CMDlval.i = lineIndex; return(rwFOR); }
|
||||
"continue" { CMDlval.i = lineIndex; return(rwCONTINUE); }
|
||||
"function" { CMDlval.i = lineIndex; return(rwDEFINE); }
|
||||
"new" { CMDlval.i = lineIndex; return(rwDECLARE); }
|
||||
"datablock" { CMDlval.i = lineIndex; return(rwDATABLOCK); }
|
||||
"case" { CMDlval.i = lineIndex; return(rwCASE); }
|
||||
"switch$" { CMDlval.i = lineIndex; return(rwSWITCHSTR); }
|
||||
"switch" { CMDlval.i = lineIndex; return(rwSWITCH); }
|
||||
"default" { CMDlval.i = lineIndex; return(rwDEFAULT); }
|
||||
"package" { CMDlval.i = lineIndex; return(rwPACKAGE); }
|
||||
"namespace" { CMDlval.i = lineIndex; return(rwNAMESPACE); }
|
||||
"true" { CMDlval.i = 1; return INTCONST; }
|
||||
"false" { CMDlval.i = 0; return INTCONST; }
|
||||
{VAR} return(Sc_ScanVar());
|
||||
{ID} { CMDtext[CMDleng] = 0; CMDlval.s = StringTable->insert(CMDtext); return(IDENT); }
|
||||
0[xX]{HEXDIGIT}+ return(Sc_ScanHex());
|
||||
{INTEGER} { CMDtext[CMDleng] = 0; CMDlval.i = dAtoi(CMDtext); return INTCONST; }
|
||||
{FLOAT} return Sc_ScanNum();
|
||||
{ILID} return(ILLEGAL_TOKEN);
|
||||
. return(ILLEGAL_TOKEN);
|
||||
%%
|
||||
|
||||
static const char *scanBuffer;
|
||||
static const char *fileName;
|
||||
static int scanIndex;
|
||||
|
||||
const char * CMDGetCurrentFile()
|
||||
{
|
||||
return fileName;
|
||||
}
|
||||
|
||||
int CMDGetCurrentLine()
|
||||
{
|
||||
return lineIndex;
|
||||
}
|
||||
|
||||
void CMDerror(char *, ...)
|
||||
{
|
||||
gSyntaxError = true;
|
||||
if(fileName)
|
||||
Con::errorf(ConsoleLogEntry::Script, "%s Line: %d - Syntax error.",
|
||||
fileName, lineIndex);
|
||||
else
|
||||
Con::errorf(ConsoleLogEntry::Script, "Syntax error in input.");
|
||||
}
|
||||
|
||||
void CMDSetScanBuffer(const char *sb, const char *fn)
|
||||
{
|
||||
scanBuffer = sb;
|
||||
fileName = fn;
|
||||
scanIndex = 0;
|
||||
lineIndex = 1;
|
||||
}
|
||||
|
||||
int CMDgetc()
|
||||
{
|
||||
int ret = scanBuffer[scanIndex];
|
||||
if(ret)
|
||||
scanIndex++;
|
||||
else
|
||||
ret = -1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int CMDwrap()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int Sc_ScanVar()
|
||||
{
|
||||
// Truncate the temp buffer...
|
||||
CMDtext[CMDleng] = 0;
|
||||
|
||||
// Make it a stringtable string!
|
||||
CMDlval.s = StringTable->insert(CMDtext);
|
||||
return(VAR);
|
||||
}
|
||||
|
||||
static int charConv(int in)
|
||||
{
|
||||
switch(in)
|
||||
{
|
||||
case 'r':
|
||||
return '\r';
|
||||
case 'n':
|
||||
return '\n';
|
||||
case 't':
|
||||
return '\t';
|
||||
default:
|
||||
return in;
|
||||
}
|
||||
}
|
||||
|
||||
static int getHexDigit(char c)
|
||||
{
|
||||
if(c >= '0' && c <= '9')
|
||||
return c - '0';
|
||||
if(c >= 'A' && c <= 'F')
|
||||
return c - 'A' + 10;
|
||||
if(c >= 'a' && c <= 'f')
|
||||
return c - 'a' + 10;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int Sc_ScanString(int ret)
|
||||
{
|
||||
CMDtext[CMDleng - 1] = 0;
|
||||
if(!collapseEscape(CMDtext+1))
|
||||
return -1;
|
||||
CMDlval.str = (char *) consoleAlloc(dStrlen(CMDtext));
|
||||
dStrcpy(CMDlval.str, CMDtext + 1);
|
||||
return(ret);
|
||||
}
|
||||
|
||||
void expandEscape(char *dest, const char *src)
|
||||
{
|
||||
U8 c;
|
||||
while((c = (U8) *src++) != 0)
|
||||
{
|
||||
if(c == '\"')
|
||||
{
|
||||
*dest++ = '\\';
|
||||
*dest++ = '\"';
|
||||
}
|
||||
else if(c == '\\')
|
||||
{
|
||||
*dest++ = '\\';
|
||||
*dest++ = '\\';
|
||||
}
|
||||
else if(c == '\r')
|
||||
{
|
||||
*dest++ = '\\';
|
||||
*dest++ = 'r';
|
||||
}
|
||||
else if(c == '\n')
|
||||
{
|
||||
*dest++ = '\\';
|
||||
*dest++ = 'n';
|
||||
}
|
||||
else if(c == '\t')
|
||||
{
|
||||
*dest++ = '\\';
|
||||
*dest++ = 't';
|
||||
}
|
||||
else if(c == '\'')
|
||||
{
|
||||
*dest++ = '\\';
|
||||
*dest++ = '\'';
|
||||
}
|
||||
else if((c >= 1 && c <= 7) ||
|
||||
(c >= 11 && c <= 12) ||
|
||||
(c >= 14 && c <= 15))
|
||||
{
|
||||
/* Remap around: \b = 0x8, \t = 0x9, \n = 0xa, \r = 0xd */
|
||||
static U8 expandRemap[15] = { 0x0,
|
||||
0x0,
|
||||
0x1,
|
||||
0x2,
|
||||
0x3,
|
||||
0x4,
|
||||
0x5,
|
||||
0x6,
|
||||
0x0,
|
||||
0x0,
|
||||
0x0,
|
||||
0x7,
|
||||
0x8,
|
||||
0x0,
|
||||
0x9 };
|
||||
|
||||
*dest++ = '\\';
|
||||
*dest++ = 'c';
|
||||
if(c == 15)
|
||||
*dest++ = 'r';
|
||||
else if(c == 16)
|
||||
*dest++ = 'p';
|
||||
else if(c == 17)
|
||||
*dest++ = 'o';
|
||||
else
|
||||
*dest++ = expandRemap[c] + '0';
|
||||
}
|
||||
else if(c < 32)
|
||||
{
|
||||
*dest++ = '\\';
|
||||
*dest++ = 'x';
|
||||
S32 dig1 = c >> 4;
|
||||
S32 dig2 = c & 0xf;
|
||||
if(dig1 < 10)
|
||||
dig1 += '0';
|
||||
else
|
||||
dig1 += 'A' - 10;
|
||||
if(dig2 < 10)
|
||||
dig2 += '0';
|
||||
else
|
||||
dig2 += 'A' - 10;
|
||||
*dest++ = dig1;
|
||||
*dest++ = dig2;
|
||||
}
|
||||
else
|
||||
*dest++ = c;
|
||||
}
|
||||
*dest = '\0';
|
||||
}
|
||||
|
||||
bool collapseEscape(char *buf)
|
||||
{
|
||||
S32 len = dStrlen(buf) + 1;
|
||||
for(S32 i = 0; i < len;)
|
||||
{
|
||||
if(buf[i] == '\\')
|
||||
{
|
||||
if(buf[i+1] == 'x')
|
||||
{
|
||||
S32 dig1 = getHexDigit(buf[i+2]);
|
||||
if(dig1 == -1)
|
||||
return false;
|
||||
|
||||
S32 dig2 = getHexDigit(buf[i+3]);
|
||||
if(dig2 == -1)
|
||||
return false;
|
||||
buf[i] = dig1 * 16 + dig2;
|
||||
dMemmove(buf + i + 1, buf + i + 4, len - i - 3);
|
||||
len -= 3;
|
||||
i++;
|
||||
}
|
||||
else if(buf[i+1] == 'c')
|
||||
{
|
||||
/* Remap around: \b = 0x8, \t = 0x9, \n = 0xa, \r = 0xd */
|
||||
static U8 collapseRemap[10] = { 0x1,
|
||||
0x2,
|
||||
0x3,
|
||||
0x4,
|
||||
0x5,
|
||||
0x6,
|
||||
0x7,
|
||||
0xb,
|
||||
0xc,
|
||||
0xe };
|
||||
|
||||
if(buf[i+2] == 'r')
|
||||
buf[i] = 15;
|
||||
else if(buf[i+2] == 'p')
|
||||
buf[i] = 16;
|
||||
else if(buf[i+2] == 'o')
|
||||
buf[i] = 17;
|
||||
else
|
||||
{
|
||||
int dig1 = buf[i+2] - '0';
|
||||
if(dig1 < 0 || dig1 > 9)
|
||||
return false;
|
||||
buf[i] = collapseRemap[dig1];
|
||||
}
|
||||
// Make sure we don't put 0x1 at the beginning of the string.
|
||||
if ((buf[i] == 0x1) && (i == 0))
|
||||
{
|
||||
buf[i] = 0x2;
|
||||
buf[i+1] = 0x1;
|
||||
dMemmove(buf + i + 2, buf + i + 3, len - i - 1);
|
||||
len -= 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
dMemmove(buf + i + 1, buf + i + 3, len - i - 2);
|
||||
len -= 2;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
buf[i] = charConv(buf[i+1]);
|
||||
dMemmove(buf + i + 1, buf + i + 2, len - i - 1);
|
||||
len--;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
else
|
||||
i++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int Sc_ScanNum()
|
||||
{
|
||||
CMDtext[CMDleng] = 0;
|
||||
CMDlval.f = dAtof(CMDtext);
|
||||
return(FLTCONST);
|
||||
}
|
||||
|
||||
static int Sc_ScanHex()
|
||||
{
|
||||
S32 val = 0;
|
||||
dSscanf(CMDtext, "%x", &val);
|
||||
CMDlval.i = val;
|
||||
return INTCONST;
|
||||
}
|
||||
|
||||
void CMD_reset()
|
||||
{
|
||||
CMDrestart(NULL);
|
||||
}
|
461
engine/console/ast.h
Executable file
461
engine/console/ast.h
Executable file
@ -0,0 +1,461 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _AST_H_
|
||||
#define _AST_H_
|
||||
|
||||
class ExprEvalState;
|
||||
class Namespace;
|
||||
class SimObject;
|
||||
class SimGroup;
|
||||
|
||||
enum TypeReq {
|
||||
TypeReqNone,
|
||||
TypeReqUInt,
|
||||
TypeReqFloat,
|
||||
TypeReqString
|
||||
};
|
||||
|
||||
/// Representation of a node for the scripting language parser.
|
||||
///
|
||||
/// When the scripting language is evaluated, it is turned from a string representation,
|
||||
/// into a parse tree, thence into byte code, which is ultimately interpreted by the VM.
|
||||
///
|
||||
/// This is the base class for the nodes in the parse tree. There are a great many subclasses,
|
||||
/// each representing a different language construct.
|
||||
struct StmtNode
|
||||
{
|
||||
StmtNode *next; ///< Next entry in parse tree.
|
||||
|
||||
StmtNode();
|
||||
|
||||
/// @name next Accessors
|
||||
/// @{
|
||||
|
||||
///
|
||||
void append(StmtNode *next);
|
||||
StmtNode *getNext() { return next; }
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Debug Info
|
||||
/// @{
|
||||
|
||||
StringTableEntry dbgFileName; ///< Name of file this node is associated with.
|
||||
S32 dbgLineNumber; ///< Line number this node is associated with.
|
||||
/// @}
|
||||
|
||||
/// @name Breaking
|
||||
/// @{
|
||||
|
||||
void addBreakCount();
|
||||
void addBreakLine(U32 ip);
|
||||
/// @}
|
||||
|
||||
/// @name Compilation
|
||||
/// @{
|
||||
|
||||
virtual U32 precompileStmt(U32 loopCount) = 0;
|
||||
virtual U32 compileStmt(U32 *codeStream, U32 ip, U32 continuePoint, U32 breakPoint) = 0;
|
||||
virtual void setPackage(StringTableEntry packageName);
|
||||
/// @}
|
||||
};
|
||||
|
||||
struct BreakStmtNode : StmtNode
|
||||
{
|
||||
static BreakStmtNode *alloc();
|
||||
|
||||
U32 precompileStmt(U32 loopCount);
|
||||
U32 compileStmt(U32 *codeStream, U32 ip, U32 continuePoint, U32 breakPoint);
|
||||
};
|
||||
|
||||
struct ContinueStmtNode : StmtNode
|
||||
{
|
||||
static ContinueStmtNode *alloc();
|
||||
U32 precompileStmt(U32 loopCount);
|
||||
U32 compileStmt(U32 *codeStream, U32 ip, U32 continuePoint, U32 breakPoint);
|
||||
};
|
||||
|
||||
/// A mathematical expression.
|
||||
struct ExprNode : StmtNode
|
||||
{
|
||||
U32 precompileStmt(U32 loopCount);
|
||||
U32 compileStmt(U32 *codeStream, U32 ip, U32 continuePoint, U32 breakPoint);
|
||||
|
||||
virtual U32 precompile(TypeReq type) = 0;
|
||||
virtual U32 compile(U32 *codeStream, U32 ip, TypeReq type) = 0;
|
||||
virtual TypeReq getPreferredType() = 0;
|
||||
};
|
||||
|
||||
struct ReturnStmtNode : StmtNode
|
||||
{
|
||||
ExprNode *expr;
|
||||
|
||||
static ReturnStmtNode *alloc(ExprNode *expr);
|
||||
U32 precompileStmt(U32 loopCount);
|
||||
U32 compileStmt(U32 *codeStream, U32 ip, U32 continuePoint, U32 breakPoint);
|
||||
};
|
||||
|
||||
struct IfStmtNode : StmtNode
|
||||
{
|
||||
ExprNode *testExpr;
|
||||
StmtNode *ifBlock, *elseBlock;
|
||||
U32 endifOffset;
|
||||
U32 elseOffset;
|
||||
bool integer;
|
||||
bool propagate;
|
||||
|
||||
static IfStmtNode *alloc(S32 lineNumber, ExprNode *testExpr, StmtNode *ifBlock, StmtNode *elseBlock, bool propagateThrough);
|
||||
void propagateSwitchExpr(ExprNode *left, bool string);
|
||||
ExprNode *getSwitchOR(ExprNode *left, ExprNode *list, bool string);
|
||||
U32 precompileStmt(U32 loopCount);
|
||||
U32 compileStmt(U32 *codeStream, U32 ip, U32 continuePoint, U32 breakPoint);
|
||||
};
|
||||
|
||||
struct LoopStmtNode : StmtNode
|
||||
{
|
||||
ExprNode *testExpr;
|
||||
ExprNode *initExpr;
|
||||
ExprNode *endLoopExpr;
|
||||
StmtNode *loopBlock;
|
||||
bool isDoLoop;
|
||||
U32 breakOffset;
|
||||
U32 continueOffset;
|
||||
U32 loopBlockStartOffset;
|
||||
bool integer;
|
||||
|
||||
static LoopStmtNode *alloc(S32 lineNumber, ExprNode *testExpr, ExprNode *initExpr, ExprNode *endLoopExpr, StmtNode *loopBlock, bool isDoLoop);
|
||||
U32 precompileStmt(U32 loopCount);
|
||||
U32 compileStmt(U32 *codeStream, U32 ip, U32 continuePoint, U32 breakPoint);
|
||||
};
|
||||
|
||||
/// A binary mathematical expression (ie, left op right).
|
||||
struct BinaryExprNode : ExprNode
|
||||
{
|
||||
S32 op;
|
||||
ExprNode *left;
|
||||
ExprNode *right;
|
||||
};
|
||||
|
||||
struct FloatBinaryExprNode : BinaryExprNode
|
||||
{
|
||||
static FloatBinaryExprNode *alloc(S32 op, ExprNode *left, ExprNode *right);
|
||||
U32 precompile(TypeReq type);
|
||||
U32 compile(U32 *codeStream, U32 ip, TypeReq type);
|
||||
TypeReq getPreferredType();
|
||||
};
|
||||
|
||||
struct ConditionalExprNode : ExprNode
|
||||
{
|
||||
ExprNode *testExpr;
|
||||
ExprNode *trueExpr;
|
||||
ExprNode *falseExpr;
|
||||
bool integer;
|
||||
static ConditionalExprNode *alloc(ExprNode *testExpr, ExprNode *trueExpr, ExprNode *falseExpr);
|
||||
virtual U32 precompile(TypeReq type);
|
||||
virtual U32 compile(U32 *codeStream, U32 ip, TypeReq type);
|
||||
virtual TypeReq getPreferredType();
|
||||
};
|
||||
|
||||
struct IntBinaryExprNode : BinaryExprNode
|
||||
{
|
||||
TypeReq subType;
|
||||
U32 operand;
|
||||
|
||||
static IntBinaryExprNode *alloc(S32 op, ExprNode *left, ExprNode *right);
|
||||
|
||||
void getSubTypeOperand();
|
||||
U32 precompile(TypeReq type);
|
||||
U32 compile(U32 *codeStream, U32 ip, TypeReq type);
|
||||
TypeReq getPreferredType();
|
||||
};
|
||||
|
||||
struct StreqExprNode : BinaryExprNode
|
||||
{
|
||||
bool eq;
|
||||
static StreqExprNode *alloc(ExprNode *left, ExprNode *right, bool eq);
|
||||
U32 precompile(TypeReq type);
|
||||
U32 compile(U32 *codeStream, U32 ip, TypeReq type);
|
||||
TypeReq getPreferredType();
|
||||
};
|
||||
|
||||
struct StrcatExprNode : BinaryExprNode
|
||||
{
|
||||
int appendChar;
|
||||
static StrcatExprNode *alloc(ExprNode *left, ExprNode *right, int appendChar);
|
||||
U32 precompile(TypeReq type);
|
||||
U32 compile(U32 *codeStream, U32 ip, TypeReq type);
|
||||
TypeReq getPreferredType();
|
||||
};
|
||||
|
||||
struct CommaCatExprNode : BinaryExprNode
|
||||
{
|
||||
static CommaCatExprNode *alloc(ExprNode *left, ExprNode *right);
|
||||
|
||||
U32 precompile(TypeReq type);
|
||||
U32 compile(U32 *codeStream, U32 ip, TypeReq type);
|
||||
TypeReq getPreferredType();
|
||||
};
|
||||
|
||||
struct IntUnaryExprNode : ExprNode
|
||||
{
|
||||
S32 op;
|
||||
ExprNode *expr;
|
||||
bool integer;
|
||||
|
||||
static IntUnaryExprNode *alloc(S32 op, ExprNode *expr);
|
||||
U32 precompile(TypeReq type);
|
||||
U32 compile(U32 *codeStream, U32 ip, TypeReq type);
|
||||
TypeReq getPreferredType();
|
||||
};
|
||||
|
||||
struct FloatUnaryExprNode : ExprNode
|
||||
{
|
||||
S32 op;
|
||||
ExprNode *expr;
|
||||
|
||||
static FloatUnaryExprNode *alloc(S32 op, ExprNode *expr);
|
||||
U32 precompile(TypeReq type);
|
||||
U32 compile(U32 *codeStream, U32 ip, TypeReq type);
|
||||
TypeReq getPreferredType();
|
||||
};
|
||||
|
||||
struct VarNode : ExprNode
|
||||
{
|
||||
StringTableEntry varName;
|
||||
ExprNode *arrayIndex;
|
||||
|
||||
static VarNode *alloc(StringTableEntry varName, ExprNode *arrayIndex);
|
||||
U32 precompile(TypeReq type);
|
||||
U32 compile(U32 *codeStream, U32 ip, TypeReq type);
|
||||
TypeReq getPreferredType();
|
||||
};
|
||||
|
||||
struct IntNode : ExprNode
|
||||
{
|
||||
S32 value;
|
||||
U32 index; // if it's converted to float/string
|
||||
|
||||
static IntNode *alloc(S32 value);
|
||||
U32 precompile(TypeReq type);
|
||||
U32 compile(U32 *codeStream, U32 ip, TypeReq type);
|
||||
TypeReq getPreferredType();
|
||||
};
|
||||
|
||||
struct FloatNode : ExprNode
|
||||
{
|
||||
F64 value;
|
||||
U32 index;
|
||||
|
||||
static FloatNode *alloc(F64 value);
|
||||
U32 precompile(TypeReq type);
|
||||
U32 compile(U32 *codeStream, U32 ip, TypeReq type);
|
||||
TypeReq getPreferredType();
|
||||
};
|
||||
|
||||
struct StrConstNode : ExprNode
|
||||
{
|
||||
char *str;
|
||||
F64 fVal;
|
||||
U32 index;
|
||||
bool tag;
|
||||
|
||||
static StrConstNode *alloc(char *str, bool tag);
|
||||
U32 precompile(TypeReq type);
|
||||
U32 compile(U32 *codeStream, U32 ip, TypeReq type);
|
||||
TypeReq getPreferredType();
|
||||
};
|
||||
|
||||
struct ConstantNode : ExprNode
|
||||
{
|
||||
StringTableEntry value;
|
||||
F64 fVal;
|
||||
U32 index;
|
||||
|
||||
static ConstantNode *alloc(StringTableEntry value);
|
||||
U32 precompile(TypeReq type);
|
||||
U32 compile(U32 *codeStream, U32 ip, TypeReq type);
|
||||
TypeReq getPreferredType();
|
||||
};
|
||||
|
||||
struct AssignExprNode : ExprNode
|
||||
{
|
||||
StringTableEntry varName;
|
||||
ExprNode *expr;
|
||||
ExprNode *arrayIndex;
|
||||
TypeReq subType;
|
||||
|
||||
static AssignExprNode *alloc(StringTableEntry varName, ExprNode *arrayIndex, ExprNode *expr);
|
||||
U32 precompile(TypeReq type);
|
||||
U32 compile(U32 *codeStream, U32 ip, TypeReq type);
|
||||
TypeReq getPreferredType();
|
||||
};
|
||||
|
||||
struct AssignDecl
|
||||
{
|
||||
S32 token;
|
||||
ExprNode *expr;
|
||||
bool integer;
|
||||
};
|
||||
|
||||
struct AssignOpExprNode : ExprNode
|
||||
{
|
||||
StringTableEntry varName;
|
||||
ExprNode *expr;
|
||||
ExprNode *arrayIndex;
|
||||
S32 op;
|
||||
U32 operand;
|
||||
TypeReq subType;
|
||||
|
||||
static AssignOpExprNode *alloc(StringTableEntry varName, ExprNode *arrayIndex, ExprNode *expr, S32 op);
|
||||
U32 precompile(TypeReq type);
|
||||
U32 compile(U32 *codeStream, U32 ip, TypeReq type);
|
||||
TypeReq getPreferredType();
|
||||
};
|
||||
|
||||
struct TTagSetStmtNode : StmtNode
|
||||
{
|
||||
StringTableEntry tag;
|
||||
ExprNode *valueExpr;
|
||||
ExprNode *stringExpr;
|
||||
|
||||
static TTagSetStmtNode *alloc(StringTableEntry tag, ExprNode *valueExpr, ExprNode *stringExpr);
|
||||
U32 precompileStmt(U32 loopCount);
|
||||
U32 compileStmt(U32 *codeStream, U32 ip, U32 continuePoint, U32 breakPoint);
|
||||
};
|
||||
|
||||
struct TTagDerefNode : ExprNode
|
||||
{
|
||||
ExprNode *expr;
|
||||
|
||||
static TTagDerefNode *alloc(ExprNode *expr);
|
||||
U32 precompile(TypeReq type);
|
||||
U32 compile(U32 *codeStream, U32 ip, TypeReq type);
|
||||
TypeReq getPreferredType();
|
||||
};
|
||||
|
||||
struct TTagExprNode : ExprNode
|
||||
{
|
||||
StringTableEntry tag;
|
||||
|
||||
static TTagExprNode *alloc(StringTableEntry tag);
|
||||
U32 precompile(TypeReq type);
|
||||
U32 compile(U32 *codeStream, U32 ip, TypeReq type);
|
||||
TypeReq getPreferredType();
|
||||
};
|
||||
|
||||
struct FuncCallExprNode : ExprNode
|
||||
{
|
||||
StringTableEntry funcName;
|
||||
StringTableEntry nameSpace;
|
||||
ExprNode *args;
|
||||
U32 callType;
|
||||
enum {
|
||||
FunctionCall,
|
||||
MethodCall,
|
||||
ParentCall
|
||||
};
|
||||
|
||||
static FuncCallExprNode *alloc(StringTableEntry funcName, StringTableEntry nameSpace, ExprNode *args, bool dot);
|
||||
U32 precompile(TypeReq type);
|
||||
U32 compile(U32 *codeStream, U32 ip, TypeReq type);
|
||||
TypeReq getPreferredType();
|
||||
};
|
||||
|
||||
struct SlotDecl
|
||||
{
|
||||
ExprNode *object;
|
||||
StringTableEntry slotName;
|
||||
ExprNode *array;
|
||||
};
|
||||
|
||||
struct SlotAccessNode : ExprNode
|
||||
{
|
||||
ExprNode *objectExpr, *arrayExpr;
|
||||
StringTableEntry slotName;
|
||||
|
||||
static SlotAccessNode *alloc(ExprNode *objectExpr, ExprNode *arrayExpr, StringTableEntry slotName);
|
||||
U32 precompile(TypeReq type);
|
||||
U32 compile(U32 *codeStream, U32 ip, TypeReq type);
|
||||
TypeReq getPreferredType();
|
||||
};
|
||||
|
||||
struct SlotAssignNode : ExprNode
|
||||
{
|
||||
ExprNode *objectExpr, *arrayExpr;
|
||||
StringTableEntry slotName;
|
||||
ExprNode *valueExpr;
|
||||
|
||||
static SlotAssignNode *alloc(ExprNode *objectExpr, ExprNode *arrayExpr, StringTableEntry slotName, ExprNode *valueExpr);
|
||||
U32 precompile(TypeReq type);
|
||||
U32 compile(U32 *codeStream, U32 ip, TypeReq type);
|
||||
TypeReq getPreferredType();
|
||||
};
|
||||
|
||||
struct SlotAssignOpNode : ExprNode
|
||||
{
|
||||
ExprNode *objectExpr, *arrayExpr;
|
||||
StringTableEntry slotName;
|
||||
S32 op;
|
||||
ExprNode *valueExpr;
|
||||
U32 operand;
|
||||
TypeReq subType;
|
||||
|
||||
static SlotAssignOpNode *alloc(ExprNode *objectExpr, StringTableEntry slotName, ExprNode *arrayExpr, S32 op, ExprNode *valueExpr);
|
||||
U32 precompile(TypeReq type);
|
||||
U32 compile(U32 *codeStream, U32 ip, TypeReq type);
|
||||
TypeReq getPreferredType();
|
||||
};
|
||||
|
||||
struct ObjectDeclNode : ExprNode
|
||||
{
|
||||
ExprNode *classNameExpr;
|
||||
StringTableEntry parentObject;
|
||||
ExprNode *objectNameExpr;
|
||||
ExprNode *argList;
|
||||
SlotAssignNode *slotDecls;
|
||||
ObjectDeclNode *subObjects;
|
||||
bool structDecl;
|
||||
U32 failOffset;
|
||||
|
||||
static ObjectDeclNode *alloc(ExprNode *classNameExpr, ExprNode *objectNameExpr, ExprNode *argList, StringTableEntry parentObject, SlotAssignNode *slotDecls, ObjectDeclNode *subObjects, bool structDecl);
|
||||
U32 precompile(TypeReq type);
|
||||
U32 precompileSubObject(bool);
|
||||
U32 compile(U32 *codeStream, U32 ip, TypeReq type);
|
||||
U32 compileSubObject(U32 *codeStream, U32 ip, bool);
|
||||
TypeReq getPreferredType();
|
||||
};
|
||||
|
||||
struct ObjectBlockDecl
|
||||
{
|
||||
SlotAssignNode *slots;
|
||||
ObjectDeclNode *decls;
|
||||
};
|
||||
|
||||
struct FunctionDeclStmtNode : StmtNode
|
||||
{
|
||||
StringTableEntry fnName;
|
||||
VarNode *args;
|
||||
StmtNode *stmts;
|
||||
StringTableEntry nameSpace;
|
||||
StringTableEntry package;
|
||||
U32 endOffset;
|
||||
U32 argc;
|
||||
|
||||
static FunctionDeclStmtNode *alloc(StringTableEntry fnName, StringTableEntry nameSpace, VarNode *args, StmtNode *stmts);
|
||||
U32 precompileStmt(U32 loopCount);
|
||||
U32 compileStmt(U32 *codeStream, U32 ip, U32 continuePoint, U32 breakPoint);
|
||||
void setPackage(StringTableEntry packageName);
|
||||
};
|
||||
|
||||
extern StmtNode *statementList;
|
||||
extern void createFunction(const char *fnName, VarNode *args, StmtNode *statements);
|
||||
extern ExprEvalState gEvalState;
|
||||
extern bool lookupFunction(const char *fnName, VarNode **args, StmtNode **statements);
|
||||
typedef const char *(*cfunc)(S32 argc, char **argv);
|
||||
extern bool lookupCFunction(const char *fnName, cfunc *f);
|
||||
|
||||
|
||||
#endif
|
335
engine/console/astAlloc.cc
Executable file
335
engine/console/astAlloc.cc
Executable file
@ -0,0 +1,335 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "console/console.h"
|
||||
#include "console/compiler.h"
|
||||
#include "console/consoleInternal.h"
|
||||
|
||||
using namespace Compiler;
|
||||
|
||||
/// @file
|
||||
///
|
||||
/// TorqueScript AST node allocators.
|
||||
///
|
||||
/// These static methods exist to allocate new AST node for the compiler. They
|
||||
/// all allocate memory from the consoleAllocator for efficiency, and often take
|
||||
/// arguments relating to the state of the nodes. They are called from gram.y
|
||||
/// (really gram.c) as the lexer analyzes the script code.
|
||||
|
||||
//------------------------------------------------------------
|
||||
|
||||
BreakStmtNode *BreakStmtNode::alloc()
|
||||
{
|
||||
BreakStmtNode *ret = (BreakStmtNode *) consoleAlloc(sizeof(BreakStmtNode));
|
||||
constructInPlace(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ContinueStmtNode *ContinueStmtNode::alloc()
|
||||
{
|
||||
ContinueStmtNode *ret = (ContinueStmtNode *) consoleAlloc(sizeof(ContinueStmtNode));
|
||||
constructInPlace(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ReturnStmtNode *ReturnStmtNode::alloc(ExprNode *expr)
|
||||
{
|
||||
ReturnStmtNode *ret = (ReturnStmtNode *) consoleAlloc(sizeof(ReturnStmtNode));
|
||||
constructInPlace(ret);
|
||||
ret->expr = expr;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
IfStmtNode *IfStmtNode::alloc(S32 lineNumber, ExprNode *testExpr, StmtNode *ifBlock, StmtNode *elseBlock, bool propagate)
|
||||
{
|
||||
IfStmtNode *ret = (IfStmtNode *) consoleAlloc(sizeof(IfStmtNode));
|
||||
constructInPlace(ret);
|
||||
ret->dbgLineNumber = lineNumber;
|
||||
|
||||
ret->testExpr = testExpr;
|
||||
ret->ifBlock = ifBlock;
|
||||
ret->elseBlock = elseBlock;
|
||||
ret->propagate = propagate;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
LoopStmtNode *LoopStmtNode::alloc(S32 lineNumber, ExprNode *initExpr, ExprNode *testExpr, ExprNode *endLoopExpr, StmtNode *loopBlock, bool isDoLoop)
|
||||
{
|
||||
LoopStmtNode *ret = (LoopStmtNode *) consoleAlloc(sizeof(LoopStmtNode));
|
||||
constructInPlace(ret);
|
||||
ret->dbgLineNumber = lineNumber;
|
||||
ret->testExpr = testExpr;
|
||||
ret->initExpr = initExpr;
|
||||
ret->endLoopExpr = endLoopExpr;
|
||||
ret->loopBlock = loopBlock;
|
||||
ret->isDoLoop = isDoLoop;
|
||||
|
||||
// Deal with setting some dummy constant nodes if we weren't provided with
|
||||
// info... This allows us to play nice with missing parts of for(;;) for
|
||||
// instance.
|
||||
if(!ret->testExpr) ret->testExpr = IntNode::alloc(1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
FloatBinaryExprNode *FloatBinaryExprNode::alloc(S32 op, ExprNode *left, ExprNode *right)
|
||||
{
|
||||
FloatBinaryExprNode *ret = (FloatBinaryExprNode *) consoleAlloc(sizeof(FloatBinaryExprNode));
|
||||
constructInPlace(ret);
|
||||
|
||||
ret->op = op;
|
||||
ret->left = left;
|
||||
ret->right = right;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
IntBinaryExprNode *IntBinaryExprNode::alloc(S32 op, ExprNode *left, ExprNode *right)
|
||||
{
|
||||
IntBinaryExprNode *ret = (IntBinaryExprNode *) consoleAlloc(sizeof(IntBinaryExprNode));
|
||||
constructInPlace(ret);
|
||||
|
||||
ret->op = op;
|
||||
ret->left = left;
|
||||
ret->right = right;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
StreqExprNode *StreqExprNode::alloc(ExprNode *left, ExprNode *right, bool eq)
|
||||
{
|
||||
StreqExprNode *ret = (StreqExprNode *) consoleAlloc(sizeof(StreqExprNode));
|
||||
constructInPlace(ret);
|
||||
ret->left = left;
|
||||
ret->right = right;
|
||||
ret->eq = eq;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
StrcatExprNode *StrcatExprNode::alloc(ExprNode *left, ExprNode *right, int appendChar)
|
||||
{
|
||||
StrcatExprNode *ret = (StrcatExprNode *) consoleAlloc(sizeof(StrcatExprNode));
|
||||
constructInPlace(ret);
|
||||
ret->left = left;
|
||||
ret->right = right;
|
||||
ret->appendChar = appendChar;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
CommaCatExprNode *CommaCatExprNode::alloc(ExprNode *left, ExprNode *right)
|
||||
{
|
||||
CommaCatExprNode *ret = (CommaCatExprNode *) consoleAlloc(sizeof(CommaCatExprNode));
|
||||
constructInPlace(ret);
|
||||
ret->left = left;
|
||||
ret->right = right;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
IntUnaryExprNode *IntUnaryExprNode::alloc(S32 op, ExprNode *expr)
|
||||
{
|
||||
IntUnaryExprNode *ret = (IntUnaryExprNode *) consoleAlloc(sizeof(IntUnaryExprNode));
|
||||
constructInPlace(ret);
|
||||
ret->op = op;
|
||||
ret->expr = expr;
|
||||
return ret;
|
||||
}
|
||||
|
||||
FloatUnaryExprNode *FloatUnaryExprNode::alloc(S32 op, ExprNode *expr)
|
||||
{
|
||||
FloatUnaryExprNode *ret = (FloatUnaryExprNode *) consoleAlloc(sizeof(FloatUnaryExprNode));
|
||||
constructInPlace(ret);
|
||||
ret->op = op;
|
||||
ret->expr = expr;
|
||||
return ret;
|
||||
}
|
||||
|
||||
VarNode *VarNode::alloc(StringTableEntry varName, ExprNode *arrayIndex)
|
||||
{
|
||||
VarNode *ret = (VarNode *) consoleAlloc(sizeof(VarNode));
|
||||
constructInPlace(ret);
|
||||
ret->varName = varName;
|
||||
ret->arrayIndex = arrayIndex;
|
||||
return ret;
|
||||
}
|
||||
|
||||
IntNode *IntNode::alloc(S32 value)
|
||||
{
|
||||
IntNode *ret = (IntNode *) consoleAlloc(sizeof(IntNode));
|
||||
constructInPlace(ret);
|
||||
ret->value = value;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ConditionalExprNode *ConditionalExprNode::alloc(ExprNode *testExpr, ExprNode *trueExpr, ExprNode *falseExpr)
|
||||
{
|
||||
ConditionalExprNode *ret = (ConditionalExprNode *) consoleAlloc(sizeof(ConditionalExprNode));
|
||||
constructInPlace(ret);
|
||||
ret->testExpr = testExpr;
|
||||
ret->trueExpr = trueExpr;
|
||||
ret->falseExpr = falseExpr;
|
||||
ret->integer = false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
FloatNode *FloatNode::alloc(F64 value)
|
||||
{
|
||||
FloatNode *ret = (FloatNode *) consoleAlloc(sizeof(FloatNode));
|
||||
constructInPlace(ret);
|
||||
ret->value = value;
|
||||
return ret;
|
||||
}
|
||||
|
||||
StrConstNode *StrConstNode::alloc(char *str, bool tag)
|
||||
{
|
||||
StrConstNode *ret = (StrConstNode *) consoleAlloc(sizeof(StrConstNode));
|
||||
constructInPlace(ret);
|
||||
ret->str = (char *) consoleAlloc(dStrlen(str) + 1);
|
||||
ret->tag = tag;
|
||||
dStrcpy(ret->str, str);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ConstantNode *ConstantNode::alloc(StringTableEntry value)
|
||||
{
|
||||
ConstantNode *ret = (ConstantNode *) consoleAlloc(sizeof(ConstantNode));
|
||||
constructInPlace(ret);
|
||||
ret->value = value;
|
||||
return ret;
|
||||
}
|
||||
|
||||
AssignExprNode *AssignExprNode::alloc(StringTableEntry varName, ExprNode *arrayIndex, ExprNode *expr)
|
||||
{
|
||||
AssignExprNode *ret = (AssignExprNode *) consoleAlloc(sizeof(AssignExprNode));
|
||||
constructInPlace(ret);
|
||||
ret->varName = varName;
|
||||
ret->expr = expr;
|
||||
ret->arrayIndex = arrayIndex;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
AssignOpExprNode *AssignOpExprNode::alloc(StringTableEntry varName, ExprNode *arrayIndex, ExprNode *expr, S32 op)
|
||||
{
|
||||
AssignOpExprNode *ret = (AssignOpExprNode *) consoleAlloc(sizeof(AssignOpExprNode));
|
||||
constructInPlace(ret);
|
||||
ret->varName = varName;
|
||||
ret->expr = expr;
|
||||
ret->arrayIndex = arrayIndex;
|
||||
ret->op = op;
|
||||
return ret;
|
||||
}
|
||||
|
||||
TTagSetStmtNode *TTagSetStmtNode::alloc(StringTableEntry tag, ExprNode *valueExpr, ExprNode *stringExpr)
|
||||
{
|
||||
TTagSetStmtNode *ret = (TTagSetStmtNode *) consoleAlloc(sizeof(TTagSetStmtNode));
|
||||
constructInPlace(ret);
|
||||
ret->tag = tag;
|
||||
ret->valueExpr = valueExpr;
|
||||
ret->stringExpr = stringExpr;
|
||||
return ret;
|
||||
}
|
||||
|
||||
TTagDerefNode *TTagDerefNode::alloc(ExprNode *expr)
|
||||
{
|
||||
TTagDerefNode *ret = (TTagDerefNode *) consoleAlloc(sizeof(TTagDerefNode));
|
||||
constructInPlace(ret);
|
||||
ret->expr = expr;
|
||||
return ret;
|
||||
}
|
||||
|
||||
TTagExprNode *TTagExprNode::alloc(StringTableEntry tag)
|
||||
{
|
||||
TTagExprNode *ret = (TTagExprNode *) consoleAlloc(sizeof(TTagExprNode));
|
||||
constructInPlace(ret);
|
||||
ret->tag = tag;
|
||||
return ret;
|
||||
}
|
||||
|
||||
FuncCallExprNode *FuncCallExprNode::alloc(StringTableEntry funcName, StringTableEntry nameSpace, ExprNode *args, bool dot)
|
||||
{
|
||||
FuncCallExprNode *ret = (FuncCallExprNode *) consoleAlloc(sizeof(FuncCallExprNode));
|
||||
constructInPlace(ret);
|
||||
ret->funcName = funcName;
|
||||
ret->nameSpace = nameSpace;
|
||||
ret->args = args;
|
||||
if(dot)
|
||||
ret->callType = MethodCall;
|
||||
else
|
||||
{
|
||||
if(nameSpace && !dStricmp(nameSpace, "Parent"))
|
||||
ret->callType = ParentCall;
|
||||
else
|
||||
ret->callType = FunctionCall;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
SlotAccessNode *SlotAccessNode::alloc(ExprNode *objectExpr, ExprNode *arrayExpr, StringTableEntry slotName)
|
||||
{
|
||||
SlotAccessNode *ret = (SlotAccessNode *) consoleAlloc(sizeof(SlotAccessNode));
|
||||
constructInPlace(ret);
|
||||
ret->objectExpr = objectExpr;
|
||||
ret->arrayExpr = arrayExpr;
|
||||
ret->slotName = slotName;
|
||||
return ret;
|
||||
}
|
||||
|
||||
SlotAssignNode *SlotAssignNode::alloc(ExprNode *objectExpr, ExprNode *arrayExpr, StringTableEntry slotName, ExprNode *valueExpr)
|
||||
{
|
||||
SlotAssignNode *ret = (SlotAssignNode *) consoleAlloc(sizeof(SlotAssignNode));
|
||||
constructInPlace(ret);
|
||||
ret->objectExpr = objectExpr;
|
||||
ret->arrayExpr = arrayExpr;
|
||||
ret->slotName = slotName;
|
||||
ret->valueExpr = valueExpr;
|
||||
return ret;
|
||||
}
|
||||
|
||||
SlotAssignOpNode *SlotAssignOpNode::alloc(ExprNode *objectExpr, StringTableEntry slotName, ExprNode *arrayExpr, S32 op, ExprNode *valueExpr)
|
||||
{
|
||||
SlotAssignOpNode *ret = (SlotAssignOpNode *) consoleAlloc(sizeof(SlotAssignOpNode));
|
||||
constructInPlace(ret);
|
||||
ret->objectExpr = objectExpr;
|
||||
ret->arrayExpr = arrayExpr;
|
||||
ret->slotName = slotName;
|
||||
ret->op = op;
|
||||
ret->valueExpr = valueExpr;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ObjectDeclNode *ObjectDeclNode::alloc(ExprNode *classNameExpr, ExprNode *objectNameExpr, ExprNode *argList, StringTableEntry parentObject, SlotAssignNode *slotDecls, ObjectDeclNode *subObjects, bool structDecl)
|
||||
{
|
||||
ObjectDeclNode *ret = (ObjectDeclNode *) consoleAlloc(sizeof(ObjectDeclNode));
|
||||
constructInPlace(ret);
|
||||
ret->classNameExpr = classNameExpr;
|
||||
ret->objectNameExpr = objectNameExpr;
|
||||
ret->argList = argList;
|
||||
ret->slotDecls = slotDecls;
|
||||
ret->subObjects = subObjects;
|
||||
ret->structDecl = structDecl;
|
||||
if(parentObject)
|
||||
ret->parentObject = parentObject;
|
||||
else
|
||||
ret->parentObject = StringTable->insert("");
|
||||
return ret;
|
||||
}
|
||||
|
||||
FunctionDeclStmtNode *FunctionDeclStmtNode::alloc(StringTableEntry fnName, StringTableEntry nameSpace, VarNode *args, StmtNode *stmts)
|
||||
{
|
||||
FunctionDeclStmtNode *ret = (FunctionDeclStmtNode *) consoleAlloc(sizeof(FunctionDeclStmtNode));
|
||||
constructInPlace(ret);
|
||||
ret->fnName = fnName;
|
||||
ret->args = args;
|
||||
ret->stmts = stmts;
|
||||
ret->nameSpace = nameSpace;
|
||||
ret->package = NULL;
|
||||
return ret;
|
||||
}
|
1680
engine/console/astNodes.cc
Executable file
1680
engine/console/astNodes.cc
Executable file
File diff suppressed because it is too large
Load Diff
85
engine/console/basgram.h
Executable file
85
engine/console/basgram.h
Executable file
@ -0,0 +1,85 @@
|
||||
typedef union {
|
||||
char c;
|
||||
int i;
|
||||
const char *s;
|
||||
char *str;
|
||||
double f;
|
||||
StmtNode *stmt;
|
||||
ExprNode *expr;
|
||||
SlotAssignNode *slist;
|
||||
VarNode *var;
|
||||
SlotDecl slot;
|
||||
ObjectBlockDecl odcl;
|
||||
ObjectDeclNode *od;
|
||||
AssignDecl asn;
|
||||
IfStmtNode *ifnode;
|
||||
} YYSTYPE;
|
||||
#define rwDEFINE 258
|
||||
#define rwENDDEF 259
|
||||
#define rwDECLARE 260
|
||||
#define rwBREAK 261
|
||||
#define rwELSE 262
|
||||
#define rwCONTINUE 263
|
||||
#define rwGLOBAL 264
|
||||
#define rwIF 265
|
||||
#define rwNIL 266
|
||||
#define rwRETURN 267
|
||||
#define rwWHILE 268
|
||||
#define rwENDIF 269
|
||||
#define rwENDWHILE 270
|
||||
#define rwENDFOR 271
|
||||
#define rwDEFAULT 272
|
||||
#define rwFOR 273
|
||||
#define rwDATABLOCK 274
|
||||
#define rwSWITCH 275
|
||||
#define rwCASE 276
|
||||
#define rwSWITCHSTR 277
|
||||
#define rwCASEOR 278
|
||||
#define rwPACKAGE 279
|
||||
#define ILLEGAL_TOKEN 280
|
||||
#define CHRCONST 281
|
||||
#define INTCONST 282
|
||||
#define TTAG 283
|
||||
#define VAR 284
|
||||
#define IDENT 285
|
||||
#define STRATOM 286
|
||||
#define TAGATOM 287
|
||||
#define FLTCONST 288
|
||||
#define opMINUSMINUS 289
|
||||
#define opPLUSPLUS 290
|
||||
#define STMT_SEP 291
|
||||
#define opSHL 292
|
||||
#define opSHR 293
|
||||
#define opPLASN 294
|
||||
#define opMIASN 295
|
||||
#define opMLASN 296
|
||||
#define opDVASN 297
|
||||
#define opMODASN 298
|
||||
#define opANDASN 299
|
||||
#define opXORASN 300
|
||||
#define opORASN 301
|
||||
#define opSLASN 302
|
||||
#define opSRASN 303
|
||||
#define opCAT 304
|
||||
#define opEQ 305
|
||||
#define opNE 306
|
||||
#define opGE 307
|
||||
#define opLE 308
|
||||
#define opAND 309
|
||||
#define opOR 310
|
||||
#define opSTREQ 311
|
||||
#define opCOLONCOLON 312
|
||||
#define opMDASN 313
|
||||
#define opNDASN 314
|
||||
#define opNTASN 315
|
||||
#define opSTRNE 316
|
||||
#define UNARY 317
|
||||
#define rwTHEN 318
|
||||
#define rwEND 319
|
||||
#define rwBEGIN 320
|
||||
#define rwCFOR 321
|
||||
#define rwTO 322
|
||||
#define rwSTEP 323
|
||||
|
||||
|
||||
extern YYSTYPE BASlval;
|
6
engine/console/bison.bat
Executable file
6
engine/console/bison.bat
Executable file
@ -0,0 +1,6 @@
|
||||
echo Changing to %4 ...
|
||||
cd %4
|
||||
echo Generating %2 and %3 with prefix %1.
|
||||
..\..\bin\bison\bison.exe -o %2 %3 --defines -p %1
|
||||
echo Renaming %2 to %5 .
|
||||
move /Y %2 %5
|
686
engine/console/bison.simple
Executable file
686
engine/console/bison.simple
Executable file
@ -0,0 +1,686 @@
|
||||
/* -*-C-*- Note some compilers choke on comments on `#line' lines. */
|
||||
#line 3 "bison.simple"
|
||||
|
||||
/* Skeleton output parser for bison,
|
||||
Copyright (C) 1984, 1989, 1990 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2, or (at your option)
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||||
|
||||
/* As a special exception, when this file is copied by Bison into a
|
||||
Bison output file, you may use that output file without restriction.
|
||||
This special exception was added by the Free Software Foundation
|
||||
in version 1.24 of Bison. */
|
||||
|
||||
#ifndef alloca
|
||||
#ifdef __GNUC__
|
||||
#define alloca __builtin_alloca
|
||||
#else /* not GNU C. */
|
||||
#if (!defined (__STDC__) && defined (sparc)) || defined (__sparc__) || defined (__sparc) || defined (__sgi)
|
||||
#include <alloca.h>
|
||||
#else /* not sparc */
|
||||
#if defined (MSDOS) && !defined (__TURBOC__)
|
||||
#include <malloc.h>
|
||||
#else /* not MSDOS, or __TURBOC__ */
|
||||
#if defined(_AIX)
|
||||
#include <malloc.h>
|
||||
#pragma alloca
|
||||
#else /* not MSDOS, __TURBOC__, or _AIX */
|
||||
#ifdef __hpux
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
void *alloca (unsigned int);
|
||||
};
|
||||
#else /* not __cplusplus */
|
||||
void *alloca ();
|
||||
#endif /* not __cplusplus */
|
||||
#endif /* __hpux */
|
||||
#endif /* not _AIX */
|
||||
#endif /* not MSDOS, or __TURBOC__ */
|
||||
#endif /* not sparc. */
|
||||
#endif /* not GNU C. */
|
||||
#endif /* alloca not defined. */
|
||||
|
||||
/* This is the parser code that is written into each bison parser
|
||||
when the %semantic_parser declaration is not specified in the grammar.
|
||||
It was written by Richard Stallman by simplifying the hairy parser
|
||||
used when %semantic_parser is specified. */
|
||||
|
||||
/* Note: there must be only one dollar sign in this file.
|
||||
It is replaced by the list of actions, each action
|
||||
as one case of the switch. */
|
||||
|
||||
#define yyerrok (yyerrstatus = 0)
|
||||
#define yyclearin (yychar = YYEMPTY)
|
||||
#define YYEMPTY -2
|
||||
#define YYEOF 0
|
||||
#define YYACCEPT return(0)
|
||||
#define YYABORT return(1)
|
||||
#define YYERROR goto yyerrlab1
|
||||
/* Like YYERROR except do call yyerror.
|
||||
This remains here temporarily to ease the
|
||||
transition to the new meaning of YYERROR, for GCC.
|
||||
Once GCC version 2 has supplanted version 1, this can go. */
|
||||
#define YYFAIL goto yyerrlab
|
||||
#define YYRECOVERING() (!!yyerrstatus)
|
||||
#define YYBACKUP(token, value) \
|
||||
do \
|
||||
if (yychar == YYEMPTY && yylen == 1) \
|
||||
{ yychar = (token), yylval = (value); \
|
||||
yychar1 = YYTRANSLATE (yychar); \
|
||||
YYPOPSTACK; \
|
||||
goto yybackup; \
|
||||
} \
|
||||
else \
|
||||
{ yyerror ("syntax error: cannot back up"); YYERROR; } \
|
||||
while (0)
|
||||
|
||||
#define YYTERROR 1
|
||||
#define YYERRCODE 256
|
||||
|
||||
#ifndef YYPURE
|
||||
#define YYLEX yylex()
|
||||
#endif
|
||||
|
||||
#ifdef YYPURE
|
||||
#ifdef YYLSP_NEEDED
|
||||
#ifdef YYLEX_PARAM
|
||||
#define YYLEX yylex(&yylval, &yylloc, YYLEX_PARAM)
|
||||
#else
|
||||
#define YYLEX yylex(&yylval, &yylloc)
|
||||
#endif
|
||||
#else /* not YYLSP_NEEDED */
|
||||
#ifdef YYLEX_PARAM
|
||||
#define YYLEX yylex(&yylval, YYLEX_PARAM)
|
||||
#else
|
||||
#define YYLEX yylex(&yylval)
|
||||
#endif
|
||||
#endif /* not YYLSP_NEEDED */
|
||||
#endif
|
||||
|
||||
/* If nonreentrant, generate the variables here */
|
||||
|
||||
#ifndef YYPURE
|
||||
|
||||
int yychar; /* the lookahead symbol */
|
||||
YYSTYPE yylval; /* the semantic value of the */
|
||||
/* lookahead symbol */
|
||||
|
||||
#ifdef YYLSP_NEEDED
|
||||
YYLTYPE yylloc; /* location data for the lookahead */
|
||||
/* symbol */
|
||||
#endif
|
||||
|
||||
int yynerrs; /* number of parse errors so far */
|
||||
#endif /* not YYPURE */
|
||||
|
||||
#if YYDEBUG != 0
|
||||
int yydebug; /* nonzero means print parse trace */
|
||||
/* Since this is uninitialized, it does not stop multiple parsers
|
||||
from coexisting. */
|
||||
#endif
|
||||
|
||||
/* YYINITDEPTH indicates the initial size of the parser's stacks */
|
||||
|
||||
#ifndef YYINITDEPTH
|
||||
#define YYINITDEPTH 200
|
||||
#endif
|
||||
|
||||
/* YYMAXDEPTH is the maximum size the stacks can grow to
|
||||
(effective only if the built-in stack extension method is used). */
|
||||
|
||||
#if YYMAXDEPTH == 0
|
||||
#undef YYMAXDEPTH
|
||||
#endif
|
||||
|
||||
#ifndef YYMAXDEPTH
|
||||
#define YYMAXDEPTH 10000
|
||||
#endif
|
||||
|
||||
/* Prevent warning if -Wstrict-prototypes. */
|
||||
#ifdef __GNUC__
|
||||
int yyparse (void);
|
||||
#endif
|
||||
|
||||
#if __GNUC__ > 1 /* GNU C and GNU C++ define this. */
|
||||
#define __yy_memcpy(FROM,TO,COUNT) __builtin_memcpy(TO,FROM,COUNT)
|
||||
#else /* not GNU C or C++ */
|
||||
#ifndef __cplusplus
|
||||
|
||||
/* This is the most reliable way to avoid incompatibilities
|
||||
in available built-in functions on various systems. */
|
||||
static void
|
||||
__yy_memcpy (from, to, count)
|
||||
char *from;
|
||||
char *to;
|
||||
int count;
|
||||
{
|
||||
register char *f = from;
|
||||
register char *t = to;
|
||||
register int i = count;
|
||||
|
||||
while (i-- > 0)
|
||||
*t++ = *f++;
|
||||
}
|
||||
|
||||
#else /* __cplusplus */
|
||||
|
||||
/* This is the most reliable way to avoid incompatibilities
|
||||
in available built-in functions on various systems. */
|
||||
static void
|
||||
__yy_memcpy (char *from, char *to, int count)
|
||||
{
|
||||
register char *f = from;
|
||||
register char *t = to;
|
||||
register int i = count;
|
||||
|
||||
while (i-- > 0)
|
||||
*t++ = *f++;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#line 192 "bison.simple"
|
||||
|
||||
/* The user can define YYPARSE_PARAM as the name of an argument to be passed
|
||||
into yyparse. The argument should have type void *.
|
||||
It should actually point to an object.
|
||||
Grammar actions can access the variable by casting it
|
||||
to the proper pointer type. */
|
||||
|
||||
#ifdef YYPARSE_PARAM
|
||||
#define YYPARSE_PARAM_DECL void *YYPARSE_PARAM;
|
||||
#else
|
||||
#define YYPARSE_PARAM
|
||||
#define YYPARSE_PARAM_DECL
|
||||
#endif
|
||||
|
||||
int
|
||||
yyparse(YYPARSE_PARAM)
|
||||
YYPARSE_PARAM_DECL
|
||||
{
|
||||
register int yystate;
|
||||
register int yyn;
|
||||
register short *yyssp;
|
||||
register YYSTYPE *yyvsp;
|
||||
int yyerrstatus; /* number of tokens to shift before error messages enabled */
|
||||
int yychar1 = 0; /* lookahead token as an internal (translated) token number */
|
||||
|
||||
short yyssa[YYINITDEPTH]; /* the state stack */
|
||||
YYSTYPE yyvsa[YYINITDEPTH]; /* the semantic value stack */
|
||||
|
||||
short *yyss = yyssa; /* refer to the stacks thru separate pointers */
|
||||
YYSTYPE *yyvs = yyvsa; /* to allow yyoverflow to reallocate them elsewhere */
|
||||
|
||||
#ifdef YYLSP_NEEDED
|
||||
YYLTYPE yylsa[YYINITDEPTH]; /* the location stack */
|
||||
YYLTYPE *yyls = yylsa;
|
||||
YYLTYPE *yylsp;
|
||||
|
||||
#define YYPOPSTACK (yyvsp--, yyssp--, yylsp--)
|
||||
#else
|
||||
#define YYPOPSTACK (yyvsp--, yyssp--)
|
||||
#endif
|
||||
|
||||
int yystacksize = YYINITDEPTH;
|
||||
|
||||
#ifdef YYPURE
|
||||
int yychar;
|
||||
YYSTYPE yylval;
|
||||
int yynerrs;
|
||||
#ifdef YYLSP_NEEDED
|
||||
YYLTYPE yylloc;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
YYSTYPE yyval; /* the variable used to return */
|
||||
/* semantic values from the action */
|
||||
/* routines */
|
||||
|
||||
int yylen;
|
||||
|
||||
#if YYDEBUG != 0
|
||||
if (yydebug)
|
||||
fprintf(stderr, "Starting parse\n");
|
||||
#endif
|
||||
|
||||
yystate = 0;
|
||||
yyerrstatus = 0;
|
||||
yynerrs = 0;
|
||||
yychar = YYEMPTY; /* Cause a token to be read. */
|
||||
|
||||
/* Initialize stack pointers.
|
||||
Waste one element of value and location stack
|
||||
so that they stay on the same level as the state stack.
|
||||
The wasted elements are never initialized. */
|
||||
|
||||
yyssp = yyss - 1;
|
||||
yyvsp = yyvs;
|
||||
#ifdef YYLSP_NEEDED
|
||||
yylsp = yyls;
|
||||
#endif
|
||||
|
||||
/* Push a new state, which is found in yystate . */
|
||||
/* In all cases, when you get here, the value and location stacks
|
||||
have just been pushed. so pushing a state here evens the stacks. */
|
||||
yynewstate:
|
||||
|
||||
*++yyssp = yystate;
|
||||
|
||||
if (yyssp >= yyss + yystacksize - 1)
|
||||
{
|
||||
/* Give user a chance to reallocate the stack */
|
||||
/* Use copies of these so that the &'s don't force the real ones into memory. */
|
||||
YYSTYPE *yyvs1 = yyvs;
|
||||
short *yyss1 = yyss;
|
||||
#ifdef YYLSP_NEEDED
|
||||
YYLTYPE *yyls1 = yyls;
|
||||
#endif
|
||||
|
||||
/* Get the current used size of the three stacks, in elements. */
|
||||
int size = yyssp - yyss + 1;
|
||||
|
||||
#ifdef yyoverflow
|
||||
/* Each stack pointer address is followed by the size of
|
||||
the data in use in that stack, in bytes. */
|
||||
#ifdef YYLSP_NEEDED
|
||||
/* This used to be a conditional around just the two extra args,
|
||||
but that might be undefined if yyoverflow is a macro. */
|
||||
yyoverflow("parser stack overflow",
|
||||
&yyss1, size * sizeof (*yyssp),
|
||||
&yyvs1, size * sizeof (*yyvsp),
|
||||
&yyls1, size * sizeof (*yylsp),
|
||||
&yystacksize);
|
||||
#else
|
||||
yyoverflow("parser stack overflow",
|
||||
&yyss1, size * sizeof (*yyssp),
|
||||
&yyvs1, size * sizeof (*yyvsp),
|
||||
&yystacksize);
|
||||
#endif
|
||||
|
||||
yyss = yyss1; yyvs = yyvs1;
|
||||
#ifdef YYLSP_NEEDED
|
||||
yyls = yyls1;
|
||||
#endif
|
||||
#else /* no yyoverflow */
|
||||
/* Extend the stack our own way. */
|
||||
if (yystacksize >= YYMAXDEPTH)
|
||||
{
|
||||
yyerror("parser stack overflow");
|
||||
return 2;
|
||||
}
|
||||
yystacksize *= 2;
|
||||
if (yystacksize > YYMAXDEPTH)
|
||||
yystacksize = YYMAXDEPTH;
|
||||
yyss = (short *) alloca (yystacksize * sizeof (*yyssp));
|
||||
__yy_memcpy ((char *)yyss1, (char *)yyss, size * sizeof (*yyssp));
|
||||
yyvs = (YYSTYPE *) alloca (yystacksize * sizeof (*yyvsp));
|
||||
__yy_memcpy ((char *)yyvs1, (char *)yyvs, size * sizeof (*yyvsp));
|
||||
#ifdef YYLSP_NEEDED
|
||||
yyls = (YYLTYPE *) alloca (yystacksize * sizeof (*yylsp));
|
||||
__yy_memcpy ((char *)yyls1, (char *)yyls, size * sizeof (*yylsp));
|
||||
#endif
|
||||
#endif /* no yyoverflow */
|
||||
|
||||
yyssp = yyss + size - 1;
|
||||
yyvsp = yyvs + size - 1;
|
||||
#ifdef YYLSP_NEEDED
|
||||
yylsp = yyls + size - 1;
|
||||
#endif
|
||||
|
||||
#if YYDEBUG != 0
|
||||
if (yydebug)
|
||||
fprintf(stderr, "Stack size increased to %d\n", yystacksize);
|
||||
#endif
|
||||
|
||||
if (yyssp >= yyss + yystacksize - 1)
|
||||
YYABORT;
|
||||
}
|
||||
|
||||
#if YYDEBUG != 0
|
||||
if (yydebug)
|
||||
fprintf(stderr, "Entering state %d\n", yystate);
|
||||
#endif
|
||||
|
||||
goto yybackup;
|
||||
yybackup:
|
||||
|
||||
/* Do appropriate processing given the current state. */
|
||||
/* Read a lookahead token if we need one and don't already have one. */
|
||||
/* yyresume: */
|
||||
|
||||
/* First try to decide what to do without reference to lookahead token. */
|
||||
|
||||
yyn = yypact[yystate];
|
||||
if (yyn == YYFLAG)
|
||||
goto yydefault;
|
||||
|
||||
/* Not known => get a lookahead token if don't already have one. */
|
||||
|
||||
/* yychar is either YYEMPTY or YYEOF
|
||||
or a valid token in external form. */
|
||||
|
||||
if (yychar == YYEMPTY)
|
||||
{
|
||||
#if YYDEBUG != 0
|
||||
if (yydebug)
|
||||
fprintf(stderr, "Reading a token: ");
|
||||
#endif
|
||||
yychar = YYLEX;
|
||||
}
|
||||
|
||||
/* Convert token to internal form (in yychar1) for indexing tables with */
|
||||
|
||||
if (yychar <= 0) /* This means end of input. */
|
||||
{
|
||||
yychar1 = 0;
|
||||
yychar = YYEOF; /* Don't call YYLEX any more */
|
||||
|
||||
#if YYDEBUG != 0
|
||||
if (yydebug)
|
||||
fprintf(stderr, "Now at end of input.\n");
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
yychar1 = YYTRANSLATE(yychar);
|
||||
|
||||
#if YYDEBUG != 0
|
||||
if (yydebug)
|
||||
{
|
||||
fprintf (stderr, "Next token is %d (%s", yychar, yytname[yychar1]);
|
||||
/* Give the individual parser a way to print the precise meaning
|
||||
of a token, for further debugging info. */
|
||||
#ifdef YYPRINT
|
||||
YYPRINT (stderr, yychar, yylval);
|
||||
#endif
|
||||
fprintf (stderr, ")\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
yyn += yychar1;
|
||||
if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != yychar1)
|
||||
goto yydefault;
|
||||
|
||||
yyn = yytable[yyn];
|
||||
|
||||
/* yyn is what to do for this token type in this state.
|
||||
Negative => reduce, -yyn is rule number.
|
||||
Positive => shift, yyn is new state.
|
||||
New state is final state => don't bother to shift,
|
||||
just return success.
|
||||
0, or most negative number => error. */
|
||||
|
||||
if (yyn < 0)
|
||||
{
|
||||
if (yyn == YYFLAG)
|
||||
goto yyerrlab;
|
||||
yyn = -yyn;
|
||||
goto yyreduce;
|
||||
}
|
||||
else if (yyn == 0)
|
||||
goto yyerrlab;
|
||||
|
||||
if (yyn == YYFINAL)
|
||||
YYACCEPT;
|
||||
|
||||
/* Shift the lookahead token. */
|
||||
|
||||
#if YYDEBUG != 0
|
||||
if (yydebug)
|
||||
fprintf(stderr, "Shifting token %d (%s), ", yychar, yytname[yychar1]);
|
||||
#endif
|
||||
|
||||
/* Discard the token being shifted unless it is eof. */
|
||||
if (yychar != YYEOF)
|
||||
yychar = YYEMPTY;
|
||||
|
||||
*++yyvsp = yylval;
|
||||
#ifdef YYLSP_NEEDED
|
||||
*++yylsp = yylloc;
|
||||
#endif
|
||||
|
||||
/* count tokens shifted since error; after three, turn off error status. */
|
||||
if (yyerrstatus) yyerrstatus--;
|
||||
|
||||
yystate = yyn;
|
||||
goto yynewstate;
|
||||
|
||||
/* Do the default action for the current state. */
|
||||
yydefault:
|
||||
|
||||
yyn = yydefact[yystate];
|
||||
if (yyn == 0)
|
||||
goto yyerrlab;
|
||||
|
||||
/* Do a reduction. yyn is the number of a rule to reduce with. */
|
||||
yyreduce:
|
||||
yylen = yyr2[yyn];
|
||||
if (yylen > 0)
|
||||
yyval = yyvsp[1-yylen]; /* implement default value of the action */
|
||||
|
||||
#if YYDEBUG != 0
|
||||
if (yydebug)
|
||||
{
|
||||
int i;
|
||||
|
||||
fprintf (stderr, "Reducing via rule %d (line %d), ",
|
||||
yyn, yyrline[yyn]);
|
||||
|
||||
/* Print the symbols being reduced, and their result. */
|
||||
for (i = yyprhs[yyn]; yyrhs[i] > 0; i++)
|
||||
fprintf (stderr, "%s ", yytname[yyrhs[i]]);
|
||||
fprintf (stderr, " -> %s\n", yytname[yyr1[yyn]]);
|
||||
}
|
||||
#endif
|
||||
|
||||
$ /* the action file gets copied in in place of this dollarsign */
|
||||
#line 487 "bison.simple"
|
||||
|
||||
yyvsp -= yylen;
|
||||
yyssp -= yylen;
|
||||
#ifdef YYLSP_NEEDED
|
||||
yylsp -= yylen;
|
||||
#endif
|
||||
|
||||
#if YYDEBUG != 0
|
||||
if (yydebug)
|
||||
{
|
||||
short *ssp1 = yyss - 1;
|
||||
fprintf (stderr, "state stack now");
|
||||
while (ssp1 != yyssp)
|
||||
fprintf (stderr, " %d", *++ssp1);
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
*++yyvsp = yyval;
|
||||
|
||||
#ifdef YYLSP_NEEDED
|
||||
yylsp++;
|
||||
if (yylen == 0)
|
||||
{
|
||||
yylsp->first_line = yylloc.first_line;
|
||||
yylsp->first_column = yylloc.first_column;
|
||||
yylsp->last_line = (yylsp-1)->last_line;
|
||||
yylsp->last_column = (yylsp-1)->last_column;
|
||||
yylsp->text = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
yylsp->last_line = (yylsp+yylen-1)->last_line;
|
||||
yylsp->last_column = (yylsp+yylen-1)->last_column;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Now "shift" the result of the reduction.
|
||||
Determine what state that goes to,
|
||||
based on the state we popped back to
|
||||
and the rule number reduced by. */
|
||||
|
||||
yyn = yyr1[yyn];
|
||||
|
||||
yystate = yypgoto[yyn - YYNTBASE] + *yyssp;
|
||||
if (yystate >= 0 && yystate <= YYLAST && yycheck[yystate] == *yyssp)
|
||||
yystate = yytable[yystate];
|
||||
else
|
||||
yystate = yydefgoto[yyn - YYNTBASE];
|
||||
|
||||
goto yynewstate;
|
||||
|
||||
yyerrlab: /* here on detecting error */
|
||||
|
||||
if (! yyerrstatus)
|
||||
/* If not already recovering from an error, report this error. */
|
||||
{
|
||||
++yynerrs;
|
||||
|
||||
#ifdef YYERROR_VERBOSE
|
||||
yyn = yypact[yystate];
|
||||
|
||||
if (yyn > YYFLAG && yyn < YYLAST)
|
||||
{
|
||||
int size = 0;
|
||||
char *msg;
|
||||
int x, count;
|
||||
|
||||
count = 0;
|
||||
/* Start X at -yyn if nec to avoid negative indexes in yycheck. */
|
||||
for (x = (yyn < 0 ? -yyn : 0);
|
||||
x < (sizeof(yytname) / sizeof(char *)); x++)
|
||||
if (yycheck[x + yyn] == x)
|
||||
size += strlen(yytname[x]) + 15, count++;
|
||||
msg = (char *) malloc(size + 15);
|
||||
if (msg != 0)
|
||||
{
|
||||
strcpy(msg, "parse error");
|
||||
|
||||
if (count < 5)
|
||||
{
|
||||
count = 0;
|
||||
for (x = (yyn < 0 ? -yyn : 0);
|
||||
x < (sizeof(yytname) / sizeof(char *)); x++)
|
||||
if (yycheck[x + yyn] == x)
|
||||
{
|
||||
strcat(msg, count == 0 ? ", expecting `" : " or `");
|
||||
strcat(msg, yytname[x]);
|
||||
strcat(msg, "'");
|
||||
count++;
|
||||
}
|
||||
}
|
||||
yyerror(msg);
|
||||
free(msg);
|
||||
}
|
||||
else
|
||||
yyerror ("parse error; also virtual memory exceeded");
|
||||
}
|
||||
else
|
||||
#endif /* YYERROR_VERBOSE */
|
||||
yyerror("parse error");
|
||||
}
|
||||
|
||||
goto yyerrlab1;
|
||||
yyerrlab1: /* here on error raised explicitly by an action */
|
||||
|
||||
if (yyerrstatus == 3)
|
||||
{
|
||||
/* if just tried and failed to reuse lookahead token after an error, discard it. */
|
||||
|
||||
/* return failure if at end of input */
|
||||
if (yychar == YYEOF)
|
||||
YYABORT;
|
||||
|
||||
#if YYDEBUG != 0
|
||||
if (yydebug)
|
||||
fprintf(stderr, "Discarding token %d (%s).\n", yychar, yytname[yychar1]);
|
||||
#endif
|
||||
|
||||
yychar = YYEMPTY;
|
||||
}
|
||||
|
||||
/* Else will try to reuse lookahead token
|
||||
after shifting the error token. */
|
||||
|
||||
yyerrstatus = 3; /* Each real token shifted decrements this */
|
||||
|
||||
goto yyerrhandle;
|
||||
|
||||
yyerrdefault: /* current state does not do anything special for the error token. */
|
||||
|
||||
#if 0
|
||||
/* This is wrong; only states that explicitly want error tokens
|
||||
should shift them. */
|
||||
yyn = yydefact[yystate]; /* If its default is to accept any token, ok. Otherwise pop it.*/
|
||||
if (yyn) goto yydefault;
|
||||
#endif
|
||||
|
||||
yyerrpop: /* pop the current state because it cannot handle the error token */
|
||||
|
||||
if (yyssp == yyss) YYABORT;
|
||||
yyvsp--;
|
||||
yystate = *--yyssp;
|
||||
#ifdef YYLSP_NEEDED
|
||||
yylsp--;
|
||||
#endif
|
||||
|
||||
#if YYDEBUG != 0
|
||||
if (yydebug)
|
||||
{
|
||||
short *ssp1 = yyss - 1;
|
||||
fprintf (stderr, "Error: state stack now");
|
||||
while (ssp1 != yyssp)
|
||||
fprintf (stderr, " %d", *++ssp1);
|
||||
fprintf (stderr, "\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
yyerrhandle:
|
||||
|
||||
yyn = yypact[yystate];
|
||||
if (yyn == YYFLAG)
|
||||
goto yyerrdefault;
|
||||
|
||||
yyn += YYTERROR;
|
||||
if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != YYTERROR)
|
||||
goto yyerrdefault;
|
||||
|
||||
yyn = yytable[yyn];
|
||||
if (yyn < 0)
|
||||
{
|
||||
if (yyn == YYFLAG)
|
||||
goto yyerrpop;
|
||||
yyn = -yyn;
|
||||
goto yyreduce;
|
||||
}
|
||||
else if (yyn == 0)
|
||||
goto yyerrpop;
|
||||
|
||||
if (yyn == YYFINAL)
|
||||
YYACCEPT;
|
||||
|
||||
#if YYDEBUG != 0
|
||||
if (yydebug)
|
||||
fprintf(stderr, "Shifting error token, ");
|
||||
#endif
|
||||
|
||||
*++yyvsp = yylval;
|
||||
#ifdef YYLSP_NEEDED
|
||||
*++yylsp = yylloc;
|
||||
#endif
|
||||
|
||||
yystate = yyn;
|
||||
goto yynewstate;
|
||||
}
|
82
engine/console/cmdgram.h
Executable file
82
engine/console/cmdgram.h
Executable file
@ -0,0 +1,82 @@
|
||||
typedef union {
|
||||
char c;
|
||||
int i;
|
||||
const char * s;
|
||||
char * str;
|
||||
double f;
|
||||
StmtNode * stmt;
|
||||
ExprNode * expr;
|
||||
SlotAssignNode * slist;
|
||||
VarNode * var;
|
||||
SlotDecl slot;
|
||||
ObjectBlockDecl odcl;
|
||||
ObjectDeclNode * od;
|
||||
AssignDecl asn;
|
||||
IfStmtNode * ifnode;
|
||||
} YYSTYPE;
|
||||
#define rwDEFINE 258
|
||||
#define rwENDDEF 259
|
||||
#define rwDECLARE 260
|
||||
#define rwBREAK 261
|
||||
#define rwELSE 262
|
||||
#define rwCONTINUE 263
|
||||
#define rwGLOBAL 264
|
||||
#define rwIF 265
|
||||
#define rwNIL 266
|
||||
#define rwRETURN 267
|
||||
#define rwWHILE 268
|
||||
#define rwDO 269
|
||||
#define rwENDIF 270
|
||||
#define rwENDWHILE 271
|
||||
#define rwENDFOR 272
|
||||
#define rwDEFAULT 273
|
||||
#define rwFOR 274
|
||||
#define rwDATABLOCK 275
|
||||
#define rwSWITCH 276
|
||||
#define rwCASE 277
|
||||
#define rwSWITCHSTR 278
|
||||
#define rwCASEOR 279
|
||||
#define rwPACKAGE 280
|
||||
#define rwNAMESPACE 281
|
||||
#define rwCLASS 282
|
||||
#define ILLEGAL_TOKEN 283
|
||||
#define CHRCONST 284
|
||||
#define INTCONST 285
|
||||
#define TTAG 286
|
||||
#define VAR 287
|
||||
#define IDENT 288
|
||||
#define STRATOM 289
|
||||
#define TAGATOM 290
|
||||
#define FLTCONST 291
|
||||
#define opMINUSMINUS 292
|
||||
#define opPLUSPLUS 293
|
||||
#define STMT_SEP 294
|
||||
#define opSHL 295
|
||||
#define opSHR 296
|
||||
#define opPLASN 297
|
||||
#define opMIASN 298
|
||||
#define opMLASN 299
|
||||
#define opDVASN 300
|
||||
#define opMODASN 301
|
||||
#define opANDASN 302
|
||||
#define opXORASN 303
|
||||
#define opORASN 304
|
||||
#define opSLASN 305
|
||||
#define opSRASN 306
|
||||
#define opCAT 307
|
||||
#define opEQ 308
|
||||
#define opNE 309
|
||||
#define opGE 310
|
||||
#define opLE 311
|
||||
#define opAND 312
|
||||
#define opOR 313
|
||||
#define opSTREQ 314
|
||||
#define opCOLONCOLON 315
|
||||
#define opMDASN 316
|
||||
#define opNDASN 317
|
||||
#define opNTASN 318
|
||||
#define opSTRNE 319
|
||||
#define UNARY 320
|
||||
|
||||
|
||||
extern YYSTYPE CMDlval;
|
559
engine/console/codeBlock.cc
Executable file
559
engine/console/codeBlock.cc
Executable file
@ -0,0 +1,559 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "console/console.h"
|
||||
#include "console/compiler.h"
|
||||
#include "console/codeBlock.h"
|
||||
#include "console/telnetDebugger.h"
|
||||
#include "core/resManager.h"
|
||||
|
||||
using namespace Compiler;
|
||||
|
||||
bool CodeBlock::smInFunction = false;
|
||||
U32 CodeBlock::smBreakLineCount = 0;
|
||||
CodeBlock * CodeBlock::smCodeBlockList = NULL;
|
||||
CodeBlock * CodeBlock::smCurrentCodeBlock = NULL;
|
||||
ConsoleParser *CodeBlock::smCurrentParser = NULL;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
CodeBlock::CodeBlock()
|
||||
{
|
||||
globalStrings = NULL;
|
||||
functionStrings = NULL;
|
||||
globalFloats = NULL;
|
||||
functionFloats = NULL;
|
||||
lineBreakPairs = NULL;
|
||||
breakList = NULL;
|
||||
breakListSize = 0;
|
||||
|
||||
refCount = 0;
|
||||
code = NULL;
|
||||
name = NULL;
|
||||
mRoot = StringTable->insert("");
|
||||
}
|
||||
|
||||
CodeBlock::~CodeBlock()
|
||||
{
|
||||
// Make sure we aren't lingering in the current code block...
|
||||
AssertFatal(smCurrentCodeBlock != this, "CodeBlock::~CodeBlock - Caught lingering in smCurrentCodeBlock!")
|
||||
|
||||
if(name)
|
||||
removeFromCodeList();
|
||||
delete[] const_cast<char*>(globalStrings);
|
||||
delete[] const_cast<char*>(functionStrings);
|
||||
delete[] globalFloats;
|
||||
delete[] functionFloats;
|
||||
delete[] code;
|
||||
delete[] breakList;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
StringTableEntry CodeBlock::getCurrentCodeBlockName()
|
||||
{
|
||||
if (CodeBlock::getCurrentBlock())
|
||||
return CodeBlock::getCurrentBlock()->name;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
CodeBlock *CodeBlock::find(StringTableEntry name)
|
||||
{
|
||||
for(CodeBlock *walk = CodeBlock::getCodeBlockList(); walk; walk = walk->nextFile)
|
||||
if(walk->name == name)
|
||||
return walk;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
void CodeBlock::addToCodeList()
|
||||
{
|
||||
// remove any code blocks with my name
|
||||
for(CodeBlock **walk = &smCodeBlockList; *walk;walk = &((*walk)->nextFile))
|
||||
{
|
||||
if((*walk)->name == name)
|
||||
{
|
||||
*walk = (*walk)->nextFile;
|
||||
break;
|
||||
}
|
||||
}
|
||||
nextFile = smCodeBlockList;
|
||||
smCodeBlockList = this;
|
||||
}
|
||||
|
||||
void CodeBlock::clearAllBreaks()
|
||||
{
|
||||
if(!lineBreakPairs)
|
||||
return;
|
||||
for(U32 i = 0; i < lineBreakPairCount; i++)
|
||||
{
|
||||
U32 *p = lineBreakPairs + i * 2;
|
||||
code[p[1]] = p[0] & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
void CodeBlock::clearBreakpoint(U32 lineNumber)
|
||||
{
|
||||
if(!lineBreakPairs)
|
||||
return;
|
||||
for(U32 i = 0; i < lineBreakPairCount; i++)
|
||||
{
|
||||
U32 *p = lineBreakPairs + i * 2;
|
||||
if((p[0] >> 8) == lineNumber)
|
||||
{
|
||||
code[p[1]] = p[0] & 0xFF;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CodeBlock::setAllBreaks()
|
||||
{
|
||||
if(!lineBreakPairs)
|
||||
return;
|
||||
for(U32 i = 0; i < lineBreakPairCount; i++)
|
||||
{
|
||||
U32 *p = lineBreakPairs + i * 2;
|
||||
code[p[1]] = OP_BREAK;
|
||||
}
|
||||
}
|
||||
|
||||
bool CodeBlock::setBreakpoint(U32 lineNumber)
|
||||
{
|
||||
if(!lineBreakPairs)
|
||||
return false;
|
||||
|
||||
for(U32 i = 0; i < lineBreakPairCount; i++)
|
||||
{
|
||||
U32 *p = lineBreakPairs + i * 2;
|
||||
if((p[0] >> 8) == lineNumber)
|
||||
{
|
||||
code[p[1]] = OP_BREAK;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
U32 CodeBlock::findFirstBreakLine(U32 lineNumber)
|
||||
{
|
||||
if(!lineBreakPairs)
|
||||
return 0;
|
||||
|
||||
for(U32 i = 0; i < lineBreakPairCount; i++)
|
||||
{
|
||||
U32 *p = lineBreakPairs + i * 2;
|
||||
U32 line = (p[0] >> 8);
|
||||
|
||||
if( lineNumber <= line )
|
||||
return line;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct LinePair
|
||||
{
|
||||
U32 instLine;
|
||||
U32 ip;
|
||||
};
|
||||
|
||||
void CodeBlock::findBreakLine(U32 ip, U32 &line, U32 &instruction)
|
||||
{
|
||||
U32 min = 0;
|
||||
U32 max = lineBreakPairCount - 1;
|
||||
LinePair *p = (LinePair *) lineBreakPairs;
|
||||
|
||||
U32 found;
|
||||
if(!lineBreakPairCount || p[min].ip > ip || p[max].ip < ip)
|
||||
{
|
||||
line = 0;
|
||||
instruction = OP_INVALID;
|
||||
return;
|
||||
}
|
||||
else if(p[min].ip == ip)
|
||||
found = min;
|
||||
else if(p[max].ip == ip)
|
||||
found = max;
|
||||
else
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
if(min == max - 1)
|
||||
{
|
||||
found = min;
|
||||
break;
|
||||
}
|
||||
U32 mid = (min + max) >> 1;
|
||||
if(p[mid].ip == ip)
|
||||
{
|
||||
found = mid;
|
||||
break;
|
||||
}
|
||||
else if(p[mid].ip > ip)
|
||||
max = mid;
|
||||
else
|
||||
min = mid;
|
||||
}
|
||||
}
|
||||
instruction = p[found].instLine & 0xFF;
|
||||
line = p[found].instLine >> 8;
|
||||
}
|
||||
|
||||
const char *CodeBlock::getFileLine(U32 ip)
|
||||
{
|
||||
static char nameBuffer[256];
|
||||
U32 line, inst;
|
||||
findBreakLine(ip, line, inst);
|
||||
|
||||
dSprintf(nameBuffer, sizeof(nameBuffer), "%s (%d)", name ? name : "<input>", line);
|
||||
return nameBuffer;
|
||||
}
|
||||
|
||||
void CodeBlock::removeFromCodeList()
|
||||
{
|
||||
for(CodeBlock **walk = &smCodeBlockList; *walk; walk = &((*walk)->nextFile))
|
||||
{
|
||||
if(*walk == this)
|
||||
{
|
||||
*walk = nextFile;
|
||||
|
||||
// clear out all breakpoints
|
||||
clearAllBreaks();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CodeBlock::calcBreakList()
|
||||
{
|
||||
U32 size = 0;
|
||||
S32 line = -1;
|
||||
U32 seqCount = 0;
|
||||
U32 i;
|
||||
for(i = 0; i < lineBreakPairCount; i++)
|
||||
{
|
||||
U32 lineNumber = lineBreakPairs[i * 2];
|
||||
if(lineNumber == U32(line + 1))
|
||||
seqCount++;
|
||||
else
|
||||
{
|
||||
if(seqCount)
|
||||
size++;
|
||||
size++;
|
||||
seqCount = 1;
|
||||
}
|
||||
line = lineNumber;
|
||||
}
|
||||
if(seqCount)
|
||||
size++;
|
||||
|
||||
breakList = new U32[size];
|
||||
breakListSize = size;
|
||||
line = -1;
|
||||
seqCount = 0;
|
||||
size = 0;
|
||||
|
||||
for(i = 0; i < lineBreakPairCount; i++)
|
||||
{
|
||||
U32 lineNumber = lineBreakPairs[i * 2];
|
||||
|
||||
if(lineNumber == U32(line + 1))
|
||||
seqCount++;
|
||||
else
|
||||
{
|
||||
if(seqCount)
|
||||
breakList[size++] = seqCount;
|
||||
breakList[size++] = lineNumber - getMax(0, line) - 1;
|
||||
seqCount = 1;
|
||||
}
|
||||
|
||||
line = lineNumber;
|
||||
}
|
||||
|
||||
if(seqCount)
|
||||
breakList[size++] = seqCount;
|
||||
|
||||
for(i = 0; i < lineBreakPairCount; i++)
|
||||
{
|
||||
U32 *p = lineBreakPairs + i * 2;
|
||||
p[0] = (p[0] << 8) | code[p[1]];
|
||||
}
|
||||
|
||||
// Let the telnet debugger know that this code
|
||||
// block has been loaded and that it can add break
|
||||
// points it has for it.
|
||||
if ( TelDebugger )
|
||||
TelDebugger->addAllBreakpoints( this );
|
||||
}
|
||||
|
||||
bool CodeBlock::read(StringTableEntry fileName, Stream &st)
|
||||
{
|
||||
name = fileName;
|
||||
|
||||
//
|
||||
if (name)
|
||||
{
|
||||
if (const char *slash = dStrchr(this->name, '/'))
|
||||
{
|
||||
char root[512];
|
||||
dStrncpy(root, this->name, slash-this->name);
|
||||
root[slash-this->name] = 0;
|
||||
mRoot = StringTable->insert(root);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
addToCodeList();
|
||||
|
||||
U32 globalSize,size,i;
|
||||
st.read(&size);
|
||||
if(size)
|
||||
{
|
||||
globalSize = size;
|
||||
globalStrings = new char[size];
|
||||
st.read(size, globalStrings);
|
||||
}
|
||||
st.read(&size);
|
||||
if(size)
|
||||
{
|
||||
functionStrings = new char[size];
|
||||
st.read(size, functionStrings);
|
||||
}
|
||||
st.read(&size);
|
||||
if(size)
|
||||
{
|
||||
globalFloats = new F64[size];
|
||||
for(U32 i = 0; i < size; i++)
|
||||
st.read(&globalFloats[i]);
|
||||
}
|
||||
st.read(&size);
|
||||
if(size)
|
||||
{
|
||||
functionFloats = new F64[size];
|
||||
for(U32 i = 0; i < size; i++)
|
||||
st.read(&functionFloats[i]);
|
||||
}
|
||||
U32 codeSize;
|
||||
st.read(&codeSize);
|
||||
st.read(&lineBreakPairCount);
|
||||
|
||||
U32 totSize = codeSize + lineBreakPairCount * 2;
|
||||
code = new U32[totSize];
|
||||
|
||||
for(i = 0; i < codeSize; i++)
|
||||
{
|
||||
U8 b;
|
||||
st.read(&b);
|
||||
if(b == 0xFF)
|
||||
st.read(&code[i]);
|
||||
else
|
||||
code[i] = b;
|
||||
}
|
||||
|
||||
for(i = codeSize; i < totSize; i++)
|
||||
st.read(&code[i]);
|
||||
|
||||
lineBreakPairs = code + codeSize;
|
||||
|
||||
// StringTable-ize our identifiers.
|
||||
U32 identCount;
|
||||
st.read(&identCount);
|
||||
while(identCount--)
|
||||
{
|
||||
U32 offset;
|
||||
st.read(&offset);
|
||||
StringTableEntry ste;
|
||||
if(offset < globalSize)
|
||||
ste = StringTable->insert(globalStrings + offset);
|
||||
else
|
||||
ste = StringTable->insert("");
|
||||
U32 count;
|
||||
st.read(&count);
|
||||
while(count--)
|
||||
{
|
||||
U32 ip;
|
||||
st.read(&ip);
|
||||
code[ip] = *((U32 *) &ste);
|
||||
}
|
||||
}
|
||||
|
||||
if(lineBreakPairCount)
|
||||
calcBreakList();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CodeBlock::compile(const char *codeFileName, StringTableEntry fileName, const char *script)
|
||||
{
|
||||
gSyntaxError = false;
|
||||
|
||||
consoleAllocReset();
|
||||
|
||||
STEtoU32 = compileSTEtoU32;
|
||||
|
||||
statementList = NULL;
|
||||
|
||||
// Set up the parser.
|
||||
smCurrentParser = getParserForFile(fileName);
|
||||
AssertISV(smCurrentParser, avar("CodeBlock::compile - no parser available for '%s'!", fileName));
|
||||
|
||||
// Now do some parsing.
|
||||
smCurrentParser->setScanBuffer(script, fileName);
|
||||
smCurrentParser->restart(NULL);
|
||||
smCurrentParser->parse();
|
||||
|
||||
if(gSyntaxError)
|
||||
{
|
||||
consoleAllocReset();
|
||||
return false;
|
||||
}
|
||||
|
||||
FileStream st;
|
||||
if(!ResourceManager->openFileForWrite(st, codeFileName))
|
||||
return false;
|
||||
st.write(U32(Con::DSOVersion));
|
||||
|
||||
// Reset all our value tables...
|
||||
resetTables();
|
||||
|
||||
smInFunction = false;
|
||||
smBreakLineCount = 0;
|
||||
setBreakCodeBlock(this);
|
||||
|
||||
if(statementList)
|
||||
codeSize = precompileBlock(statementList, 0) + 1;
|
||||
else
|
||||
codeSize = 1;
|
||||
|
||||
lineBreakPairCount = smBreakLineCount;
|
||||
code = new U32[codeSize + smBreakLineCount * 2];
|
||||
lineBreakPairs = code + codeSize;
|
||||
|
||||
// Write string table data...
|
||||
getGlobalStringTable().write(st);
|
||||
getFunctionStringTable().write(st);
|
||||
|
||||
// Write float table data...
|
||||
getGlobalFloatTable().write(st);
|
||||
getFunctionFloatTable().write(st);
|
||||
|
||||
smBreakLineCount = 0;
|
||||
U32 lastIp;
|
||||
if(statementList)
|
||||
lastIp = compileBlock(statementList, code, 0, 0, 0);
|
||||
else
|
||||
lastIp = 0;
|
||||
|
||||
if(lastIp != codeSize - 1)
|
||||
Con::errorf(ConsoleLogEntry::General, "CodeBlock::compile - precompile size mismatch, a precompile/compile function pair is probably mismatched.");
|
||||
|
||||
code[lastIp++] = OP_RETURN;
|
||||
U32 totSize = codeSize + smBreakLineCount * 2;
|
||||
st.write(codeSize);
|
||||
st.write(lineBreakPairCount);
|
||||
|
||||
// Write out our bytecode, doing a bit of compression for low numbers.
|
||||
U32 i;
|
||||
for(i = 0; i < codeSize; i++)
|
||||
{
|
||||
if(code[i] < 0xFF)
|
||||
st.write(U8(code[i]));
|
||||
else
|
||||
{
|
||||
st.write(U8(0xFF));
|
||||
st.write(code[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Write the break info...
|
||||
for(i = codeSize; i < totSize; i++)
|
||||
st.write(code[i]);
|
||||
|
||||
getIdentTable().write(st);
|
||||
|
||||
consoleAllocReset();
|
||||
st.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const char *CodeBlock::compileExec(StringTableEntry fileName, const char *string, bool noCalls, int setFrame)
|
||||
{
|
||||
STEtoU32 = evalSTEtoU32;
|
||||
consoleAllocReset();
|
||||
|
||||
name = fileName;
|
||||
|
||||
if(name)
|
||||
addToCodeList();
|
||||
|
||||
statementList = NULL;
|
||||
|
||||
// Set up the parser.
|
||||
smCurrentParser = getParserForFile(fileName);
|
||||
AssertISV(smCurrentParser, avar("CodeBlock::compile - no parser available for '%s'!", fileName));
|
||||
|
||||
// Now do some parsing.
|
||||
smCurrentParser->setScanBuffer(string, fileName);
|
||||
smCurrentParser->restart(NULL);
|
||||
smCurrentParser->parse();
|
||||
|
||||
if(!statementList)
|
||||
{
|
||||
delete this;
|
||||
return "";
|
||||
}
|
||||
|
||||
resetTables();
|
||||
|
||||
smInFunction = false;
|
||||
smBreakLineCount = 0;
|
||||
setBreakCodeBlock(this);
|
||||
|
||||
codeSize = precompileBlock(statementList, 0) + 1;
|
||||
|
||||
lineBreakPairCount = smBreakLineCount;
|
||||
|
||||
globalStrings = getGlobalStringTable().build();
|
||||
functionStrings = getFunctionStringTable().build();
|
||||
globalFloats = getGlobalFloatTable().build();
|
||||
functionFloats = getFunctionFloatTable().build();
|
||||
|
||||
code = new U32[codeSize + lineBreakPairCount * 2];
|
||||
lineBreakPairs = code + codeSize;
|
||||
|
||||
smBreakLineCount = 0;
|
||||
U32 lastIp = compileBlock(statementList, code, 0, 0, 0);
|
||||
code[lastIp++] = OP_RETURN;
|
||||
|
||||
consoleAllocReset();
|
||||
|
||||
if(lineBreakPairCount && fileName)
|
||||
calcBreakList();
|
||||
|
||||
if(lastIp != codeSize)
|
||||
Con::warnf(ConsoleLogEntry::General, "precompile size mismatch");
|
||||
|
||||
return exec(0, fileName, NULL, 0, 0, noCalls, NULL, setFrame);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
void CodeBlock::incRefCount()
|
||||
{
|
||||
refCount++;
|
||||
}
|
||||
|
||||
void CodeBlock::decRefCount()
|
||||
{
|
||||
refCount--;
|
||||
if(!refCount)
|
||||
delete this;
|
||||
}
|
129
engine/console/codeBlock.h
Executable file
129
engine/console/codeBlock.h
Executable file
@ -0,0 +1,129 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _CODEBLOCK_H_
|
||||
#define _CODEBLOCK_H_
|
||||
|
||||
#include "console/compiler.h"
|
||||
#include "console/consoleParser.h"
|
||||
|
||||
class Stream;
|
||||
|
||||
/// Core TorqueScript code management class.
|
||||
///
|
||||
/// This class represents a block of code, usually mapped directly to a file.
|
||||
class CodeBlock
|
||||
{
|
||||
private:
|
||||
static CodeBlock* smCodeBlockList;
|
||||
static CodeBlock* smCurrentCodeBlock;
|
||||
|
||||
public:
|
||||
static U32 smBreakLineCount;
|
||||
static bool smInFunction;
|
||||
static Compiler::ConsoleParser * smCurrentParser;
|
||||
|
||||
static CodeBlock* getCurrentBlock()
|
||||
{
|
||||
return smCurrentCodeBlock;
|
||||
}
|
||||
|
||||
static CodeBlock *getCodeBlockList()
|
||||
{
|
||||
return smCodeBlockList;
|
||||
}
|
||||
|
||||
static StringTableEntry getCurrentCodeBlockName();
|
||||
static CodeBlock *find(StringTableEntry);
|
||||
|
||||
CodeBlock();
|
||||
~CodeBlock();
|
||||
|
||||
StringTableEntry name;
|
||||
|
||||
char *globalStrings;
|
||||
char *functionStrings;
|
||||
|
||||
F64 *globalFloats;
|
||||
F64 *functionFloats;
|
||||
|
||||
U32 codeSize;
|
||||
U32 *code;
|
||||
|
||||
U32 refCount;
|
||||
U32 lineBreakPairCount;
|
||||
U32 *lineBreakPairs;
|
||||
U32 breakListSize;
|
||||
U32 *breakList;
|
||||
CodeBlock *nextFile;
|
||||
StringTableEntry mRoot;
|
||||
|
||||
|
||||
void addToCodeList();
|
||||
void removeFromCodeList();
|
||||
void calcBreakList();
|
||||
void clearAllBreaks();
|
||||
void setAllBreaks();
|
||||
|
||||
/// Returns the first breakable line or 0 if none was found.
|
||||
/// @param lineNumber The one based line number.
|
||||
U32 findFirstBreakLine(U32 lineNumber);
|
||||
|
||||
void clearBreakpoint(U32 lineNumber);
|
||||
|
||||
/// Set a OP_BREAK instruction on a line. If a break
|
||||
/// is not possible on that line it returns false.
|
||||
/// @param lineNumber The one based line number.
|
||||
bool setBreakpoint(U32 lineNumber);
|
||||
|
||||
void findBreakLine(U32 ip, U32 &line, U32 &instruction);
|
||||
void getFunctionArgs(char buffer[1024], U32 offset);
|
||||
const char *getFileLine(U32 ip);
|
||||
|
||||
bool read(StringTableEntry fileName, Stream &st);
|
||||
bool compile(const char *dsoName, StringTableEntry fileName, const char *script);
|
||||
|
||||
void incRefCount();
|
||||
void decRefCount();
|
||||
|
||||
/// Compiles and executes a block of script storing the compiled code in this
|
||||
/// CodeBlock. If there is no filename breakpoints will not be generated and
|
||||
/// the CodeBlock will not be added to the linked list of loaded CodeBlocks.
|
||||
/// Note that if the script contains no executable statements the CodeBlock
|
||||
/// will delete itself on return an empty string. The return string is any
|
||||
/// result of the code executed, if any, or an empty string.
|
||||
///
|
||||
/// @param fileName The file name, including path and extension, for the
|
||||
/// block of code or an empty string.
|
||||
/// @param script The script code to compile and execute.
|
||||
/// @param noCalls Skips calling functions from the script.
|
||||
/// @param setFrame A zero based index of the stack frame to execute the code
|
||||
/// with, zero being the top of the stack. If the the index is
|
||||
/// -1 a new frame is created. If the index is out of range the
|
||||
/// top stack frame is used.
|
||||
const char *compileExec(StringTableEntry fileName, const char *script,
|
||||
bool noCalls, int setFrame = -1 );
|
||||
|
||||
/// Executes the existing code in the CodeBlock. The return string is any
|
||||
/// result of the code executed, if any, or an empty string.
|
||||
///
|
||||
/// @param offset The instruction offset to start executing from.
|
||||
/// @param fnName The name of the function to execute or null.
|
||||
/// @param ns The namespace of the function to execute or null.
|
||||
/// @param argc The number of parameters passed to the function or
|
||||
/// zero to execute code outside of a function.
|
||||
/// @param argv The function parameter list.
|
||||
/// @param noCalls Skips calling functions from the script.
|
||||
/// @param setFrame A zero based index of the stack frame to execute the code
|
||||
/// with, zero being the top of the stack. If the the index is
|
||||
/// -1 a new frame is created. If the index is out of range the
|
||||
/// top stack frame is used.
|
||||
/// @param packageName The code package name or null.
|
||||
const char *exec(U32 offset, const char *fnName, Namespace *ns, U32 argc,
|
||||
const char **argv, bool noCalls, StringTableEntry packageName,
|
||||
S32 setFrame = -1);
|
||||
};
|
||||
|
||||
#endif
|
1199
engine/console/compiledEval.cc
Executable file
1199
engine/console/compiledEval.cc
Executable file
File diff suppressed because it is too large
Load Diff
271
engine/console/compiler.cc
Executable file
271
engine/console/compiler.cc
Executable file
@ -0,0 +1,271 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "console/console.h"
|
||||
#include "console/telnetDebugger.h"
|
||||
#include "platform/event.h"
|
||||
|
||||
#include "console/ast.h"
|
||||
#include "core/tAlgorithm.h"
|
||||
#include "core/resManager.h"
|
||||
|
||||
#include "core/findMatch.h"
|
||||
#include "console/consoleInternal.h"
|
||||
#include "core/fileStream.h"
|
||||
#include "console/compiler.h"
|
||||
|
||||
#include "console/simBase.h"
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
|
||||
F64 consoleStringToNumber(const char *str, StringTableEntry file, U32 line)
|
||||
{
|
||||
F64 val = dAtof(str);
|
||||
if(val != 0)
|
||||
return val;
|
||||
else if(!dStricmp(str, "true"))
|
||||
return 1;
|
||||
else if(!dStricmp(str, "false"))
|
||||
return 0;
|
||||
else if(file)
|
||||
{
|
||||
Con::warnf(ConsoleLogEntry::General, "%s (%d): string always evaluates to 0.", file, line);
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------
|
||||
|
||||
CompilerStringTable *gCurrentStringTable, gGlobalStringTable, gFunctionStringTable;
|
||||
CompilerFloatTable *gCurrentFloatTable, gGlobalFloatTable, gFunctionFloatTable;
|
||||
DataChunker gConsoleAllocator;
|
||||
CompilerIdentTable gIdentTable;
|
||||
CodeBlock *gCurBreakBlock;
|
||||
|
||||
//------------------------------------------------------------
|
||||
|
||||
|
||||
CodeBlock *getBreakCodeBlock() { return gCurBreakBlock; }
|
||||
void setBreakCodeBlock(CodeBlock *cb) { gCurBreakBlock = cb; }
|
||||
|
||||
//------------------------------------------------------------
|
||||
|
||||
U32 evalSTEtoU32(StringTableEntry ste, U32)
|
||||
{
|
||||
return *((U32 *) &ste);
|
||||
}
|
||||
|
||||
U32 compileSTEtoU32(StringTableEntry ste, U32 ip)
|
||||
{
|
||||
if(ste)
|
||||
getIdentTable().add(ste, ip);
|
||||
return 0;
|
||||
}
|
||||
|
||||
U32 (*STEtoU32)(StringTableEntry ste, U32 ip) = evalSTEtoU32;
|
||||
|
||||
//------------------------------------------------------------
|
||||
|
||||
bool gSyntaxError = false;
|
||||
|
||||
//------------------------------------------------------------
|
||||
|
||||
CompilerStringTable *getCurrentStringTable() { return gCurrentStringTable; }
|
||||
CompilerStringTable &getGlobalStringTable() { return gGlobalStringTable; }
|
||||
CompilerStringTable &getFunctionStringTable() { return gFunctionStringTable; }
|
||||
|
||||
void setCurrentStringTable (CompilerStringTable* cst) { gCurrentStringTable = cst; }
|
||||
|
||||
CompilerFloatTable *getCurrentFloatTable() { return gCurrentFloatTable; }
|
||||
CompilerFloatTable &getGlobalFloatTable() { return gGlobalFloatTable; }
|
||||
CompilerFloatTable &getFunctionFloatTable() { return gFunctionFloatTable; }
|
||||
|
||||
void setCurrentFloatTable (CompilerFloatTable* cst) { gCurrentFloatTable = cst; }
|
||||
|
||||
CompilerIdentTable &getIdentTable() { return gIdentTable; }
|
||||
|
||||
void precompileIdent(StringTableEntry ident)
|
||||
{
|
||||
if(ident)
|
||||
gGlobalStringTable.add(ident);
|
||||
}
|
||||
|
||||
void resetTables()
|
||||
{
|
||||
setCurrentStringTable(&gGlobalStringTable);
|
||||
setCurrentFloatTable(&gGlobalFloatTable);
|
||||
getGlobalFloatTable().reset();
|
||||
getGlobalStringTable().reset();
|
||||
getFunctionFloatTable().reset();
|
||||
getFunctionStringTable().reset();
|
||||
getIdentTable().reset();
|
||||
}
|
||||
|
||||
void *consoleAlloc(U32 size) { return gConsoleAllocator.alloc(size); }
|
||||
void consoleAllocReset() { gConsoleAllocator.freeBlocks(); }
|
||||
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
using namespace Compiler;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
|
||||
U32 CompilerStringTable::add(const char *str, bool caseSens, bool tag)
|
||||
{
|
||||
// Is it already in?
|
||||
Entry **walk;
|
||||
for(walk = &list; *walk; walk = &((*walk)->next))
|
||||
{
|
||||
if((*walk)->tag != tag)
|
||||
continue;
|
||||
|
||||
if(caseSens)
|
||||
{
|
||||
if(!dStrcmp((*walk)->string, str))
|
||||
return (*walk)->start;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!dStricmp((*walk)->string, str))
|
||||
return (*walk)->start;
|
||||
}
|
||||
}
|
||||
|
||||
// Write it out.
|
||||
Entry *newStr = (Entry *) consoleAlloc(sizeof(Entry));
|
||||
*walk = newStr;
|
||||
newStr->next = NULL;
|
||||
newStr->start = totalLen;
|
||||
U32 len = dStrlen(str) + 1;
|
||||
if(tag && len < 7) // alloc space for the numeric tag 1 for tag, 5 for # and 1 for nul
|
||||
len = 7;
|
||||
totalLen += len;
|
||||
newStr->string = (char *) consoleAlloc(len);
|
||||
newStr->len = len;
|
||||
newStr->tag = tag;
|
||||
dStrcpy(newStr->string, str);
|
||||
return newStr->start;
|
||||
}
|
||||
|
||||
U32 CompilerStringTable::addIntString(U32 value)
|
||||
{
|
||||
dSprintf(buf, sizeof(buf), "%d", value);
|
||||
return add(buf);
|
||||
}
|
||||
|
||||
U32 CompilerStringTable::addFloatString(F64 value)
|
||||
{
|
||||
dSprintf(buf, sizeof(buf), "%g", value);
|
||||
return add(buf);
|
||||
}
|
||||
void CompilerStringTable::reset()
|
||||
{
|
||||
list = NULL;
|
||||
totalLen = 0;
|
||||
}
|
||||
char *CompilerStringTable::build()
|
||||
{
|
||||
char *ret = new char[totalLen];
|
||||
for(Entry *walk = list; walk; walk = walk->next)
|
||||
dStrcpy(ret + walk->start, walk->string);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void CompilerStringTable::write(Stream &st)
|
||||
{
|
||||
st.write(totalLen);
|
||||
for(Entry *walk = list; walk; walk = walk->next)
|
||||
st.write(walk->len, walk->string);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------
|
||||
|
||||
U32 CompilerFloatTable::add(F64 value)
|
||||
{
|
||||
Entry **walk;
|
||||
U32 i = 0;
|
||||
for(walk = &list; *walk; walk = &((*walk)->next), i++)
|
||||
if(value == (*walk)->val)
|
||||
return i;
|
||||
Entry *newFloat = (Entry *) consoleAlloc(sizeof(Entry));
|
||||
newFloat->val = value;
|
||||
newFloat->next = NULL;
|
||||
count++;
|
||||
*walk = newFloat;
|
||||
return count-1;
|
||||
}
|
||||
void CompilerFloatTable::reset()
|
||||
{
|
||||
list = NULL;
|
||||
count = 0;
|
||||
}
|
||||
F64 *CompilerFloatTable::build()
|
||||
{
|
||||
F64 *ret = new F64[count];
|
||||
U32 i = 0;
|
||||
for(Entry *walk = list; walk; walk = walk->next, i++)
|
||||
ret[i] = walk->val;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void CompilerFloatTable::write(Stream &st)
|
||||
{
|
||||
st.write(count);
|
||||
for(Entry *walk = list; walk; walk = walk->next)
|
||||
st.write(walk->val);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------
|
||||
|
||||
void CompilerIdentTable::reset()
|
||||
{
|
||||
list = NULL;
|
||||
}
|
||||
|
||||
void CompilerIdentTable::add(StringTableEntry ste, U32 ip)
|
||||
{
|
||||
U32 index = gGlobalStringTable.add(ste, false);
|
||||
Entry *newEntry = (Entry *) consoleAlloc(sizeof(Entry));
|
||||
newEntry->offset = index;
|
||||
newEntry->ip = ip;
|
||||
for(Entry *walk = list; walk; walk = walk->next)
|
||||
{
|
||||
if(walk->offset == index)
|
||||
{
|
||||
newEntry->nextIdent = walk->nextIdent;
|
||||
walk->nextIdent = newEntry;
|
||||
return;
|
||||
}
|
||||
}
|
||||
newEntry->next = list;
|
||||
list = newEntry;
|
||||
newEntry->nextIdent = NULL;
|
||||
}
|
||||
|
||||
void CompilerIdentTable::write(Stream &st)
|
||||
{
|
||||
U32 count = 0;
|
||||
Entry * walk;
|
||||
for(walk = list; walk; walk = walk->next)
|
||||
count++;
|
||||
st.write(count);
|
||||
for(walk = list; walk; walk = walk->next)
|
||||
{
|
||||
U32 ec = 0;
|
||||
Entry * el;
|
||||
for(el = walk; el; el = el->nextIdent)
|
||||
ec++;
|
||||
st.write(walk->offset);
|
||||
st.write(ec);
|
||||
for(el = walk; el; el = el->nextIdent)
|
||||
st.write(el->ip);
|
||||
}
|
||||
}
|
235
engine/console/compiler.h
Executable file
235
engine/console/compiler.h
Executable file
@ -0,0 +1,235 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef _COMPILER_H_
|
||||
#define _COMPILER_H_
|
||||
|
||||
class Stream;
|
||||
class DataChunker;
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "console/ast.h"
|
||||
#include "console/codeBlock.h"
|
||||
|
||||
// Autogenerated, so we should only ever include from once place - here.
|
||||
// (We can't stick include guards in it without patching bison.)
|
||||
#ifndef _CMDGRAM_H_
|
||||
#define _CMDGRAM_H_
|
||||
#include "console/cmdgram.h"
|
||||
#endif
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
/// The opcodes for the TorqueScript VM.
|
||||
enum CompiledInstructions
|
||||
{
|
||||
OP_FUNC_DECL,
|
||||
OP_CREATE_OBJECT,
|
||||
OP_ADD_OBJECT,
|
||||
OP_END_OBJECT,
|
||||
OP_JMPIFFNOT,
|
||||
OP_JMPIFNOT,
|
||||
OP_JMPIFF,
|
||||
OP_JMPIF,
|
||||
OP_JMPIFNOT_NP,
|
||||
OP_JMPIF_NP,
|
||||
OP_JMP,
|
||||
OP_RETURN,
|
||||
OP_CMPEQ,
|
||||
OP_CMPGR,
|
||||
OP_CMPGE,
|
||||
OP_CMPLT,
|
||||
OP_CMPLE,
|
||||
OP_CMPNE,
|
||||
OP_XOR,
|
||||
OP_MOD,
|
||||
OP_BITAND,
|
||||
OP_BITOR,
|
||||
OP_NOT,
|
||||
OP_NOTF,
|
||||
OP_ONESCOMPLEMENT,
|
||||
|
||||
OP_SHR,
|
||||
OP_SHL,
|
||||
OP_AND,
|
||||
OP_OR,
|
||||
|
||||
OP_ADD,
|
||||
OP_SUB,
|
||||
OP_MUL,
|
||||
OP_DIV,
|
||||
OP_NEG,
|
||||
|
||||
OP_SETCURVAR,
|
||||
OP_SETCURVAR_CREATE,
|
||||
OP_SETCURVAR_ARRAY,
|
||||
OP_SETCURVAR_ARRAY_CREATE,
|
||||
|
||||
OP_LOADVAR_UINT,
|
||||
OP_LOADVAR_FLT,
|
||||
OP_LOADVAR_STR,
|
||||
|
||||
OP_SAVEVAR_UINT,
|
||||
OP_SAVEVAR_FLT,
|
||||
OP_SAVEVAR_STR,
|
||||
|
||||
OP_SETCUROBJECT,
|
||||
OP_SETCUROBJECT_NEW,
|
||||
|
||||
OP_SETCURFIELD,
|
||||
OP_SETCURFIELD_ARRAY,
|
||||
|
||||
OP_LOADFIELD_UINT,
|
||||
OP_LOADFIELD_FLT,
|
||||
OP_LOADFIELD_STR,
|
||||
|
||||
OP_SAVEFIELD_UINT,
|
||||
OP_SAVEFIELD_FLT,
|
||||
OP_SAVEFIELD_STR,
|
||||
|
||||
OP_STR_TO_UINT,
|
||||
OP_STR_TO_FLT,
|
||||
OP_STR_TO_NONE,
|
||||
OP_FLT_TO_UINT,
|
||||
OP_FLT_TO_STR,
|
||||
OP_FLT_TO_NONE,
|
||||
OP_UINT_TO_FLT,
|
||||
OP_UINT_TO_STR,
|
||||
OP_UINT_TO_NONE,
|
||||
|
||||
OP_LOADIMMED_UINT,
|
||||
OP_LOADIMMED_FLT,
|
||||
OP_TAG_TO_STR,
|
||||
OP_LOADIMMED_STR,
|
||||
OP_LOADIMMED_IDENT,
|
||||
|
||||
OP_CALLFUNC_RESOLVE,
|
||||
OP_CALLFUNC,
|
||||
|
||||
OP_ADVANCE_STR,
|
||||
OP_ADVANCE_STR_APPENDCHAR,
|
||||
OP_ADVANCE_STR_COMMA,
|
||||
OP_ADVANCE_STR_NUL,
|
||||
OP_REWIND_STR,
|
||||
OP_TERMINATE_REWIND_STR,
|
||||
OP_COMPARE_STR,
|
||||
|
||||
OP_PUSH,
|
||||
OP_PUSH_FRAME,
|
||||
|
||||
OP_BREAK,
|
||||
|
||||
OP_INVALID
|
||||
};
|
||||
|
||||
//------------------------------------------------------------
|
||||
|
||||
F64 consoleStringToNumber(const char *str, StringTableEntry file = 0, U32 line = 0);
|
||||
U32 precompileBlock(StmtNode *block, U32 loopCount);
|
||||
U32 compileBlock(StmtNode *block, U32 *codeStream, U32 ip, U32 continuePoint, U32 breakPoint);
|
||||
|
||||
//------------------------------------------------------------
|
||||
|
||||
struct CompilerIdentTable
|
||||
{
|
||||
struct Entry
|
||||
{
|
||||
U32 offset;
|
||||
U32 ip;
|
||||
Entry *next;
|
||||
Entry *nextIdent;
|
||||
};
|
||||
Entry *list;
|
||||
void add(StringTableEntry ste, U32 ip);
|
||||
void reset();
|
||||
void write(Stream &st);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------
|
||||
|
||||
struct CompilerStringTable
|
||||
{
|
||||
U32 totalLen;
|
||||
struct Entry
|
||||
{
|
||||
char *string;
|
||||
U32 start;
|
||||
U32 len;
|
||||
bool tag;
|
||||
Entry *next;
|
||||
};
|
||||
Entry *list;
|
||||
|
||||
char buf[256];
|
||||
|
||||
U32 add(const char *str, bool caseSens = true, bool tag = false);
|
||||
U32 addIntString(U32 value);
|
||||
U32 addFloatString(F64 value);
|
||||
void reset();
|
||||
char *build();
|
||||
void write(Stream &st);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------
|
||||
|
||||
struct CompilerFloatTable
|
||||
{
|
||||
struct Entry
|
||||
{
|
||||
F64 val;
|
||||
Entry *next;
|
||||
};
|
||||
U32 count;
|
||||
Entry *list;
|
||||
|
||||
U32 add(F64 value);
|
||||
void reset();
|
||||
F64 *build();
|
||||
void write(Stream &st);
|
||||
};
|
||||
|
||||
//------------------------------------------------------------
|
||||
|
||||
inline StringTableEntry U32toSTE(U32 u)
|
||||
{
|
||||
return *((StringTableEntry *) &u);
|
||||
}
|
||||
|
||||
extern U32 (*STEtoU32)(StringTableEntry ste, U32 ip);
|
||||
|
||||
U32 evalSTEtoU32(StringTableEntry ste, U32);
|
||||
U32 compileSTEtoU32(StringTableEntry ste, U32 ip);
|
||||
|
||||
CompilerStringTable *getCurrentStringTable();
|
||||
CompilerStringTable &getGlobalStringTable();
|
||||
CompilerStringTable &getFunctionStringTable();
|
||||
|
||||
void setCurrentStringTable (CompilerStringTable* cst);
|
||||
|
||||
CompilerFloatTable *getCurrentFloatTable();
|
||||
CompilerFloatTable &getGlobalFloatTable();
|
||||
CompilerFloatTable &getFunctionFloatTable();
|
||||
|
||||
void setCurrentFloatTable (CompilerFloatTable* cst);
|
||||
|
||||
CompilerIdentTable &getIdentTable();
|
||||
|
||||
void precompileIdent(StringTableEntry ident);
|
||||
|
||||
CodeBlock *getBreakCodeBlock();
|
||||
void setBreakCodeBlock(CodeBlock *cb);
|
||||
|
||||
/// Helper function to reset the float, string, and ident tables to a base
|
||||
/// starting state.
|
||||
void resetTables();
|
||||
|
||||
void *consoleAlloc(U32 size);
|
||||
void consoleAllocReset();
|
||||
|
||||
extern bool gSyntaxError;
|
||||
};
|
||||
|
||||
#endif
|
1046
engine/console/console.cc
Executable file
1046
engine/console/console.cc
Executable file
File diff suppressed because it is too large
Load Diff
807
engine/console/console.h
Executable file
807
engine/console/console.h
Executable file
@ -0,0 +1,807 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _CONSOLE_H_
|
||||
#define _CONSOLE_H_
|
||||
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "platform/platform.h"
|
||||
#endif
|
||||
#ifndef _BITSET_H_
|
||||
#include "core/bitSet.h"
|
||||
#endif
|
||||
#include <stdarg.h>
|
||||
|
||||
class SimObject;
|
||||
struct EnumTable;
|
||||
class Namespace;
|
||||
|
||||
/// Indicates that warnings about undefined script variables should be displayed.
|
||||
///
|
||||
/// @note This is set and controlled by script.
|
||||
extern bool gWarnUndefinedScriptVariables;
|
||||
|
||||
enum StringTableConstants
|
||||
{
|
||||
StringTagPrefixByte = 0x01 ///< Magic value prefixed to tagged strings.
|
||||
};
|
||||
|
||||
/// Represents an entry in the log.
|
||||
struct ConsoleLogEntry
|
||||
{
|
||||
/// This field indicates the severity of the log entry.
|
||||
///
|
||||
/// Log entries are filtered and displayed differently based on
|
||||
/// their severity. Errors are highlighted red, while normal entries
|
||||
/// are displayed as normal text. Often times, the engine will be
|
||||
/// configured to hide all log entries except warnings or errors,
|
||||
/// or to perform a special notification when it encounters an error.
|
||||
enum Level
|
||||
{
|
||||
Normal = 0,
|
||||
Warning,
|
||||
Error,
|
||||
NUM_CLASS
|
||||
} mLevel;
|
||||
|
||||
/// Used to associate a log entry with a module.
|
||||
///
|
||||
/// Log entries can come from different sources; for instance,
|
||||
/// the scripting engine, or the network code. This allows the
|
||||
/// logging system to be aware of where different log entries
|
||||
/// originated from.
|
||||
enum Type
|
||||
{
|
||||
General = 0,
|
||||
Assert,
|
||||
Script,
|
||||
GUI,
|
||||
Network,
|
||||
NUM_TYPE
|
||||
} mType;
|
||||
|
||||
/// Indicates the actual log entry.
|
||||
///
|
||||
/// This contains a description of the event being logged.
|
||||
/// For instance, "unable to access file", or "player connected
|
||||
/// successfully", or nearly anything else you might imagine.
|
||||
///
|
||||
/// Typically, the description should contain a concise, descriptive
|
||||
/// string describing whatever is being logged. Whenever possible,
|
||||
/// include useful details like the name of the file being accessed,
|
||||
/// or the id of the player or GuiControl, so that if a log needs
|
||||
/// to be used to locate a bug, it can be done as painlessly as
|
||||
/// possible.
|
||||
const char *mString;
|
||||
};
|
||||
|
||||
/// Scripting engine representation of an enum.
|
||||
///
|
||||
/// This data structure is used by the scripting engine
|
||||
/// to expose enumerations to the scripting language. It
|
||||
/// acts to relate named constants to integer values, just
|
||||
/// like an enum in C++.
|
||||
struct EnumTable
|
||||
{
|
||||
/// Number of enumerated items in the table.
|
||||
S32 size;
|
||||
|
||||
/// This represents a specific item in the enumeration.
|
||||
struct Enums
|
||||
{
|
||||
S32 index; ///< Index label maps to.
|
||||
const char *label;///< Label for this index.
|
||||
};
|
||||
|
||||
Enums *table;
|
||||
|
||||
/// Constructor.
|
||||
///
|
||||
/// This sets up the EnumTable with predefined data.
|
||||
///
|
||||
/// @param sSize Size of the table.
|
||||
/// @param sTable Pointer to table of Enums.
|
||||
///
|
||||
/// @see gLiquidTypeTable
|
||||
/// @see gAlignTable
|
||||
EnumTable(S32 sSize, Enums *sTable)
|
||||
{ size = sSize; table = sTable; }
|
||||
};
|
||||
|
||||
typedef const char *StringTableEntry;
|
||||
|
||||
/// @defgroup console_callbacks Scripting Engine Callbacks
|
||||
///
|
||||
/// The scripting engine makes heavy use of callbacks to represent
|
||||
/// function exposed to the scripting language. StringCallback,
|
||||
/// IntCallback, FloatCallback, VoidCallback, and BoolCallback all
|
||||
/// represent exposed script functions returning different types.
|
||||
///
|
||||
/// ConsumerCallback is used with the function Con::addConsumer; functions
|
||||
/// registered with Con::addConsumer are called whenever something is outputted
|
||||
/// to the console. For instance, the TelnetConsole registers itself with the
|
||||
/// console so it can echo the console over the network.
|
||||
///
|
||||
/// @note Callbacks to the scripting language - for instance, onExit(), which is
|
||||
/// a script function called when the engine is shutting down - are handled
|
||||
/// using Con::executef() and kin.
|
||||
/// @{
|
||||
|
||||
///
|
||||
typedef const char * (*StringCallback)(SimObject *obj, S32 argc, const char *argv[]);
|
||||
typedef S32 (*IntCallback)(SimObject *obj, S32 argc, const char *argv[]);
|
||||
typedef F32 (*FloatCallback)(SimObject *obj, S32 argc, const char *argv[]);
|
||||
typedef void (*VoidCallback)(SimObject *obj, S32 argc, const char *argv[]); // We have it return a value so things don't break..
|
||||
typedef bool (*BoolCallback)(SimObject *obj, S32 argc, const char *argv[]);
|
||||
|
||||
typedef void (*ConsumerCallback)(ConsoleLogEntry::Level level, const char *consoleLine);
|
||||
/// @}
|
||||
|
||||
/// @defgroup console_types Scripting Engine Type Functions
|
||||
///
|
||||
/// @see Con::registerType
|
||||
/// @{
|
||||
typedef const char* (*GetDataFunction)(void *dptr, EnumTable *tbl, BitSet32 flag);
|
||||
typedef void (*SetDataFunction)(void *dptr, S32 argc, const char **argv, EnumTable *tbl, BitSet32 flag);
|
||||
/// @}
|
||||
|
||||
/// This namespace contains the core of the console functionality.
|
||||
///
|
||||
/// @section con_intro Introduction
|
||||
///
|
||||
/// The console is a key part of Torque's architecture. It allows direct run-time control
|
||||
/// of many aspects of the engine.
|
||||
///
|
||||
/// @nosubgrouping
|
||||
namespace Con
|
||||
{
|
||||
/// Various configuration constants.
|
||||
enum Constants
|
||||
{
|
||||
/// This is the version number associated with DSO files.
|
||||
///
|
||||
/// If you make any changes to the way the scripting language works
|
||||
/// (such as DSO format changes, adding/removing op-codes) that would
|
||||
/// break compatibility, then you should increment this.
|
||||
///
|
||||
/// If you make a really major change, increment it to the next multiple
|
||||
/// of ten.
|
||||
///
|
||||
/// 12/29/04 - BJG - 33->34 Removed some opcodes, part of namespace upgrade.
|
||||
/// 12/30/04 - BJG - 34->35 Reordered some things, further general shuffling.
|
||||
/// 11/03/05 - BJG - 35->36 Integrated new debugger code.
|
||||
DSOVersion = 36,
|
||||
|
||||
MaxLineLength = 512, ///< Maximum length of a line of console input.
|
||||
MaxDataTypes = 256 ///< Maximum number of registered data types.
|
||||
};
|
||||
|
||||
/// @name Control Functions
|
||||
///
|
||||
/// The console must be initialized and shutdown appropriately during the
|
||||
/// lifetime of the app. These functions are used to manage this behavior.
|
||||
///
|
||||
/// @note Torque deals with this aspect of console management, so you don't need
|
||||
/// to call these functions in normal usage of the engine.
|
||||
/// @{
|
||||
|
||||
/// Initializes the console.
|
||||
///
|
||||
/// This performs the following steps:
|
||||
/// - Calls Namespace::init() to initialize the scripting namespace hierarchy.
|
||||
/// - Calls ConsoleConstructor::setup() to initialize globally defined console
|
||||
/// methods and functions.
|
||||
/// - Registers some basic global script variables.
|
||||
/// - Calls AbstractClassRep::init() to initialize Torque's class database.
|
||||
/// - Registers some basic global script functions that couldn't usefully
|
||||
/// be defined anywhere else.
|
||||
void init();
|
||||
|
||||
/// Shuts down the console.
|
||||
///
|
||||
/// This performs the following steps:
|
||||
/// - Closes the console log file.
|
||||
/// - Calls Namespace::shutdown() to shut down the scripting namespace hierarchy.
|
||||
void shutdown();
|
||||
|
||||
/// Is the console active at this time?
|
||||
bool isActive();
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Console Consumers
|
||||
///
|
||||
/// The console distributes its output through Torque by using
|
||||
/// consumers. Every time a new line is printed to the console,
|
||||
/// all the ConsumerCallbacks registered using addConsumer are
|
||||
/// called, in order.
|
||||
///
|
||||
/// @note The GuiConsole control, which provides the on-screen
|
||||
/// in-game console, uses a different technique to render
|
||||
/// the console. It calls getLockLog() to lock the Vector
|
||||
/// of on-screen console entries, then it renders them as
|
||||
/// needed. While the Vector is locked, the console will
|
||||
/// not change the Vector. When the GuiConsole control is
|
||||
/// done with the console entries, it calls unlockLog()
|
||||
/// to tell the console that it is again safe to modify
|
||||
/// the Vector.
|
||||
///
|
||||
/// @see TelnetConsole
|
||||
/// @see TelnetDebugger
|
||||
/// @see WinConsole
|
||||
/// @see MacCarbConsole
|
||||
/// @see StdConsole
|
||||
/// @see ConsoleLogger
|
||||
///
|
||||
/// @{
|
||||
void addConsumer(ConsumerCallback cb);
|
||||
void removeConsumer(ConsumerCallback cb);
|
||||
/// @}
|
||||
|
||||
/// @name Miscellaneous
|
||||
/// @{
|
||||
|
||||
/// Remove color marking information from a string.
|
||||
///
|
||||
/// @note It does this in-place, so be careful! It may
|
||||
/// potentially blast data if you're not careful.
|
||||
/// When in doubt, make a copy of the string first.
|
||||
void stripColorChars(char* line);
|
||||
|
||||
/// Convert from a relative script path to an absolute script path.
|
||||
///
|
||||
/// This is used in (among other places) the exec() script function, which
|
||||
/// takes a parameter indicating a script file and executes it. Script paths
|
||||
/// can be one of:
|
||||
/// - <b>Absolute:</b> <i>fps/foo/bar.cs</i> Paths of this sort are passed
|
||||
/// through.
|
||||
/// - <b>Mod-relative:</b> <i>~/foo/bar.cs</i> Paths of this sort have their
|
||||
/// replaced with the name of the current mod.
|
||||
/// - <b>File-relative:</b> <i>./baz/blip.cs</i> Paths of this sort are
|
||||
/// calculated relative to the path of the current scripting file.
|
||||
///
|
||||
/// @note This function determines paths relative to the currently executing
|
||||
/// CodeBlock. Calling it outside of script execution will result in
|
||||
/// it directly copying src to filename, since it won't know to what the
|
||||
/// path is relative!
|
||||
///
|
||||
/// @param filename Pointer to string buffer to fill with absolute path.
|
||||
/// @param size Size of buffer pointed to by filename.
|
||||
/// @param src Original, possibly relative script path.
|
||||
bool expandScriptFilename(char *filename, U32 size, const char *src);
|
||||
|
||||
/// Returns true if fn is a global scripting function.
|
||||
///
|
||||
/// This looks in the global namespace. It also checks to see if fn
|
||||
/// is in the StringTable; if not, it returns false.
|
||||
bool isFunction(const char *fn);
|
||||
|
||||
/// This is the basis for tab completion in the console.
|
||||
///
|
||||
/// @note This is an internally used function. You probably don't
|
||||
/// care much about how this works.
|
||||
///
|
||||
/// This function does some basic parsing to try to ascertain the namespace in which
|
||||
/// we are attempting to do tab completion, then bumps control off to the appropriate
|
||||
/// tabComplete function, either in SimObject or Namespace.
|
||||
///
|
||||
/// @param inputBuffer Pointer to buffer containing starting data, or last result.
|
||||
/// @param cursorPos Location of cursor in this buffer. This is used to indicate
|
||||
/// what part of the string should be kept and what part should
|
||||
/// be advanced to the next match if any.
|
||||
/// @param maxResultLength Maximum amount of result data to put into inputBuffer. This
|
||||
/// is capped by MaxCompletionBufferSize.
|
||||
/// @param forwardTab Should we go forward to next match or backwards to previous
|
||||
/// match? True indicates forward.
|
||||
U32 tabComplete(char* inputBuffer, U32 cursorPos, U32 maxResultLength, bool forwardTab);
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
/// @name Variable Management
|
||||
/// @{
|
||||
|
||||
/// Add a console variable that references the value of a variable in C++ code.
|
||||
///
|
||||
/// If a value is assigned to the console variable the C++ variable is updated,
|
||||
/// and vice-versa.
|
||||
///
|
||||
/// @param name Global console variable name to create
|
||||
/// @param type The type of the C++ variable; see the ConsoleDynamicTypes enum for a complete list.
|
||||
/// @param pointer Pointer to the variable.
|
||||
/// @see ConsoleDynamicTypes
|
||||
bool addVariable(const char *name, S32 type, void *pointer);
|
||||
|
||||
/// Remove a console variable.
|
||||
///
|
||||
/// @param name Global console variable name to remove
|
||||
/// @return true if variable existed before removal.
|
||||
bool removeVariable(const char *name);
|
||||
|
||||
/// Assign a string value to a locally scoped console variable
|
||||
///
|
||||
/// @note The context of the variable is determined by gEvalState; that is,
|
||||
/// by the currently executing code.
|
||||
///
|
||||
/// @param name Local console variable name to set
|
||||
/// @param value String value to assign to name
|
||||
void setLocalVariable(const char *name, const char *value);
|
||||
|
||||
/// Retrieve the string value to a locally scoped console variable
|
||||
///
|
||||
/// @note The context of the variable is determined by gEvalState; that is,
|
||||
/// by the currently executing code.
|
||||
///
|
||||
/// @param name Local console variable name to get
|
||||
const char* getLocalVariable(const char* name);
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Global Variable Accessors
|
||||
/// @{
|
||||
/// Assign a string value to a global console variable
|
||||
/// @param name Global console variable name to set
|
||||
/// @param value String value to assign to this variable.
|
||||
void setVariable(const char *name, const char *value);
|
||||
|
||||
/// Retrieve the string value of a global console variable
|
||||
/// @param name Global Console variable name to query
|
||||
/// @return The string value of the variable or "" if the variable does not exist.
|
||||
const char* getVariable(const char* name);
|
||||
|
||||
/// Same as setVariable(), but for bools.
|
||||
void setBoolVariable (const char* name,bool var);
|
||||
|
||||
/// Same as getVariable(), but for bools.
|
||||
///
|
||||
/// @param name Name of the variable.
|
||||
/// @param def Default value to supply if no matching variable is found.
|
||||
bool getBoolVariable (const char* name,bool def = false);
|
||||
|
||||
/// Same as setVariable(), but for ints.
|
||||
void setIntVariable (const char* name,S32 var);
|
||||
|
||||
/// Same as getVariable(), but for ints.
|
||||
///
|
||||
/// @param name Name of the variable.
|
||||
/// @param def Default value to supply if no matching variable is found.
|
||||
S32 getIntVariable (const char* name,S32 def = 0);
|
||||
|
||||
/// Same as setVariable(), but for floats.
|
||||
void setFloatVariable(const char* name,F32 var);
|
||||
|
||||
/// Same as getVariable(), but for floats.
|
||||
///
|
||||
/// @param name Name of the variable.
|
||||
/// @param def Default value to supply if no matching variable is found.
|
||||
F32 getFloatVariable(const char* name,F32 def = .0f);
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Global Function Registration
|
||||
/// @{
|
||||
|
||||
/// Register a C++ function with the console making it a global function callable from the scripting engine.
|
||||
///
|
||||
/// @param name Name of the new function.
|
||||
/// @param cb Pointer to the function implementing the scripting call; a console callback function returning a specific type value.
|
||||
/// @param usage Documentation for this function. @ref console_autodoc
|
||||
/// @param minArgs Minimum number of arguments this function accepts
|
||||
/// @param maxArgs Maximum number of arguments this function accepts
|
||||
void addCommand(const char *name, StringCallback cb, const char *usage, S32 minArgs, S32 maxArgs);
|
||||
|
||||
void addCommand(const char *name, IntCallback cb, const char *usage, S32 minArgs, S32 maxArgs); ///< @copydoc addCommand(const char *, StringCallback, const char *, S32, S32)
|
||||
void addCommand(const char *name, FloatCallback cb, const char *usage, S32 minArgs, S32 maxArgs); ///< @copydoc addCommand(const char *, StringCallback, const char *, S32, S32)
|
||||
void addCommand(const char *name, VoidCallback cb, const char *usage, S32 minArgs, S32 maxArgs); ///< @copydoc addCommand(const char *, StringCallback, const char *, S32, S32)
|
||||
void addCommand(const char *name, BoolCallback cb, const char *usage, S32 minArgs, S32 maxArgs); ///< @copydoc addCommand(const char *, StringCallback, const char *, S32, S32)
|
||||
/// @}
|
||||
|
||||
/// @name Namespace Function Registration
|
||||
/// @{
|
||||
|
||||
/// Register a C++ function with the console making it callable
|
||||
/// as a method of the given namespace from the scripting engine.
|
||||
///
|
||||
/// @param nameSpace Name of the namespace to associate the new function with; this is usually the name of a class.
|
||||
/// @param name Name of the new function.
|
||||
/// @param cb Pointer to the function implementing the scripting call; a console callback function returning a specific type value.
|
||||
/// @param usage Documentation for this function. @ref console_autodoc
|
||||
/// @param minArgs Minimum number of arguments this function accepts
|
||||
/// @param maxArgs Maximum number of arguments this function accepts
|
||||
void addCommand(const char *nameSpace, const char *name,StringCallback cb, const char *usage, S32 minArgs, S32 maxArgs);
|
||||
void addCommand(const char *nameSpace, const char *name,IntCallback cb, const char *usage, S32 minArgs, S32 maxArgs); ///< @copydoc addCommand(const char*, const char *, StringCallback, const char *, S32, S32)
|
||||
void addCommand(const char *nameSpace, const char *name,FloatCallback cb, const char *usage, S32 minArgs, S32 maxArgs); ///< @copydoc addCommand(const char*, const char *, StringCallback, const char *, S32, S32)
|
||||
void addCommand(const char *nameSpace, const char *name,VoidCallback cb, const char *usage, S32 minArgs, S32 maxArgs); ///< @copydoc addCommand(const char*, const char *, StringCallback, const char *, S32, S32)
|
||||
void addCommand(const char *nameSpace, const char *name,BoolCallback cb, const char *usage, S32 minArgs, S32 maxArgs); ///< @copydoc addCommand(const char*, const char *, StringCallback, const char *, S32, S32)
|
||||
/// @}
|
||||
|
||||
/// @name Special Purpose Registration
|
||||
///
|
||||
/// These are special-purpose functions that exist to allow commands to be grouped, so
|
||||
/// that when we generate console docs, they can be more meaningfully presented.
|
||||
///
|
||||
/// @ref console_autodoc "Click here for more information about console docs and grouping."
|
||||
///
|
||||
/// @{
|
||||
|
||||
void markCommandGroup (const char * nsName, const char *name, const char* usage=NULL);
|
||||
void beginCommandGroup(const char * nsName, const char *name, const char* usage);
|
||||
void endCommandGroup (const char * nsName, const char *name);
|
||||
|
||||
/// @deprecated
|
||||
void addOverload (const char * nsName, const char *name, const char *altUsage);
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Console Output
|
||||
///
|
||||
/// These functions process the formatted string and pass it to all the ConsumerCallbacks that are
|
||||
/// currently registered. The console log file and the console window callbacks are installed by default.
|
||||
///
|
||||
/// @see addConsumer()
|
||||
/// @see removeConsumer()
|
||||
/// @{
|
||||
|
||||
/// @param _format A stdlib printf style formatted out put string
|
||||
/// @param ... Variables to be written
|
||||
void printf(const char *_format, ...);
|
||||
|
||||
/// @note The console window colors warning text as LIGHT GRAY.
|
||||
/// @param _format A stdlib printf style formatted out put string
|
||||
/// @param ... Variables to be written
|
||||
void warnf(const char *_format, ...);
|
||||
|
||||
/// @note The console window colors warning text as RED.
|
||||
/// @param _format A stdlib printf style formatted out put string
|
||||
/// @param ... Variables to be written
|
||||
void errorf(const char *_format, ...);
|
||||
|
||||
/// @note The console window colors warning text as LIGHT GRAY.
|
||||
/// @param type Allows you to associate the warning message with an internal module.
|
||||
/// @param _format A stdlib printf style formatted out put string
|
||||
/// @param ... Variables to be written
|
||||
/// @see Con::warnf()
|
||||
void warnf(ConsoleLogEntry::Type type, const char *_format, ...);
|
||||
|
||||
/// @note The console window colors warning text as RED.
|
||||
/// @param type Allows you to associate the warning message with an internal module.
|
||||
/// @param _format A stdlib printf style formatted out put string
|
||||
/// @param ... Variables to be written
|
||||
/// @see Con::errorf()
|
||||
void errorf(ConsoleLogEntry::Type type, const char *_format, ...);
|
||||
|
||||
/// @}
|
||||
|
||||
/// Returns true when called from the main thread, false otherwise
|
||||
bool isMainThread();
|
||||
|
||||
|
||||
/// @name Console Execution
|
||||
///
|
||||
/// These are functions relating to the execution of script code.
|
||||
///
|
||||
/// @{
|
||||
|
||||
/// Call a script function from C/C++ code.
|
||||
///
|
||||
/// @param argc Number of elements in the argv parameter
|
||||
/// @param argv A character string array containing the name of the function
|
||||
/// to call followed by the arguments to that function.
|
||||
/// @code
|
||||
/// // Call a Torque script function called mAbs, having one parameter.
|
||||
/// char* argv[] = {"abs", "-9"};
|
||||
/// char* result = execute(2, argv);
|
||||
/// @endcode
|
||||
const char *execute(S32 argc, const char* argv[]);
|
||||
|
||||
/// @see execute(S32 argc, const char* argv[])
|
||||
const char *executef(S32 argc, ...);
|
||||
|
||||
/// Call a Torque Script member function of a SimObject from C/C++ code.
|
||||
/// @param object Object on which to execute the method call.
|
||||
/// @param argc Number of elements in the argv parameter (must be >2, see argv)
|
||||
/// @param argv A character string array containing the name of the member function
|
||||
/// to call followed by an empty parameter (gets filled with object ID)
|
||||
/// followed by arguments to that function.
|
||||
/// @code
|
||||
/// // Call the method setMode() on an object, passing it one parameter.
|
||||
///
|
||||
/// char* argv[] = {"setMode", "", "2"};
|
||||
/// char* result = execute(mysimobject, 3, argv);
|
||||
/// @endcode
|
||||
const char *execute(SimObject *object, S32 argc, const char *argv[]);
|
||||
|
||||
/// @see execute(SimObject *, S32 argc, const char *argv[])
|
||||
const char *executef(SimObject *, S32 argc, ...);
|
||||
|
||||
/// Evaluate an arbitrary chunk of code.
|
||||
///
|
||||
/// @param string Buffer containing code to execute.
|
||||
/// @param echo Should we echo the string to the console?
|
||||
/// @param fileName Indicate what file this code is coming from; used in error reporting and such.
|
||||
const char *evaluate(const char* string, bool echo = false, const char *fileName = NULL);
|
||||
|
||||
/// Evaluate an arbitrary line of script.
|
||||
///
|
||||
/// This wraps dVsprintf(), so you can substitute parameters into the code being executed.
|
||||
const char *evaluatef(const char* string, ...);
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Console Function Implementation Helpers
|
||||
///
|
||||
/// The functions Con::getIntArg, Con::getFloatArg and Con::getArgBuffer(size) are used to
|
||||
/// allocate on the console stack string variables that will be passed into the next console
|
||||
// function called. This allows the console to avoid copying some data.
|
||||
///
|
||||
/// getReturnBuffer lets you allocate stack space to return data in.
|
||||
/// @{
|
||||
|
||||
///
|
||||
char *getReturnBuffer(U32 bufferSize);
|
||||
|
||||
char *getArgBuffer(U32 bufferSize);
|
||||
char *getFloatArg(F64 arg);
|
||||
char *getIntArg (S32 arg);
|
||||
/// @}
|
||||
|
||||
/// @name Namespaces
|
||||
/// @{
|
||||
|
||||
Namespace *lookupNamespace(const char *nsName);
|
||||
bool linkNamespaces(const char *parentName, const char *childName);
|
||||
|
||||
/// @note This should only be called from consoleObject.h
|
||||
bool classLinkNamespaces(Namespace *parent, Namespace *child);
|
||||
/// @}
|
||||
|
||||
/// @name Logging
|
||||
/// @{
|
||||
|
||||
void getLockLog(ConsoleLogEntry * &log, U32 &size);
|
||||
void unlockLog(void);
|
||||
void setLogMode(S32 mode);
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Dynamic Type System
|
||||
/// @{
|
||||
|
||||
///
|
||||
/* void registerType( const char *typeName, S32 type, S32 size, GetDataFunction gdf, SetDataFunction sdf, bool isDatablockType = false );
|
||||
void registerType( const char* typeName, S32 type, S32 size, bool isDatablockType = false );
|
||||
void registerTypeGet( S32 type, GetDataFunction gdf );
|
||||
void registerTypeSet( S32 type, SetDataFunction sdf );
|
||||
|
||||
const char *getTypeName(S32 type);
|
||||
bool isDatablockType( S32 type ); */
|
||||
|
||||
void setData(S32 type, void *dptr, S32 index, S32 argc, const char **argv, EnumTable *tbl = NULL, BitSet32 flag = 0);
|
||||
const char *getData(S32 type, void *dptr, S32 index, EnumTable *tbl = NULL, BitSet32 flag = 0);
|
||||
/// @}
|
||||
};
|
||||
|
||||
extern void expandEscape(char *dest, const char *src);
|
||||
extern bool collapseEscape(char *buf);
|
||||
extern S32 HashPointer(StringTableEntry ptr);
|
||||
|
||||
/// This is the backend for the ConsoleMethod()/ConsoleFunction() macros.
|
||||
///
|
||||
/// See the group ConsoleConstructor Innards for specifics on how this works.
|
||||
///
|
||||
/// @see @ref console_autodoc
|
||||
/// @nosubgrouping
|
||||
class ConsoleConstructor
|
||||
{
|
||||
public:
|
||||
/// @name Entry Type Fields
|
||||
///
|
||||
/// One of these is set based on the type of entry we want
|
||||
/// inserted in the console.
|
||||
///
|
||||
/// @ref console_autodoc
|
||||
/// @{
|
||||
StringCallback sc; ///< A function/method that returns a string.
|
||||
IntCallback ic; ///< A function/method that returns an int.
|
||||
FloatCallback fc; ///< A function/method that returns a float.
|
||||
VoidCallback vc; ///< A function/method that returns nothing.
|
||||
BoolCallback bc; ///< A function/method that returns a bool.
|
||||
bool group; ///< Indicates that this is a group marker.
|
||||
bool overload; ///< Indicates that this is an overload marker.
|
||||
/// @deprecated Unused.
|
||||
/// @}
|
||||
|
||||
/// Minimum/maximum number of arguments for the function.
|
||||
S32 mina, maxa;
|
||||
const char *usage; ///< Usage string.
|
||||
const char *funcName; ///< Function name.
|
||||
const char *className; ///< Class name.
|
||||
|
||||
/// @name ConsoleConstructer Innards
|
||||
///
|
||||
/// The ConsoleConstructor class is used as the backend for the ConsoleFunction() and
|
||||
/// ConsoleMethod() macros. The way it works takes advantage of several properties of
|
||||
/// C++.
|
||||
///
|
||||
/// The ConsoleFunction()/ConsoleMethod() macros wrap the declaration of a ConsoleConstructor.
|
||||
///
|
||||
/// @code
|
||||
/// // The definition of a ConsoleFunction using the macro
|
||||
/// ConsoleFunction(ExpandFilename, const char*, 2, 2, "(string filename)")
|
||||
/// {
|
||||
/// argc;
|
||||
/// char* ret = Con::getReturnBuffer( 1024 );
|
||||
/// Con::expandScriptFilename(ret, 1024, argv[1]);
|
||||
/// return ret;
|
||||
/// }
|
||||
///
|
||||
/// // Resulting code
|
||||
/// static const char* cExpandFilename(SimObject *, S32, const char **argv);
|
||||
/// static ConsoleConstructor
|
||||
/// gExpandFilenameobj(NULL,"ExpandFilename", cExpandFilename,
|
||||
/// "(string filename)", 2, 2);
|
||||
/// static const char* cExpandFilename(SimObject *, S32 argc, const char **argv)
|
||||
/// {
|
||||
/// argc;
|
||||
/// char* ret = Con::getReturnBuffer( 1024 );
|
||||
/// Con::expandScriptFilename(ret, 1024, argv[1]);
|
||||
/// return ret;
|
||||
/// }
|
||||
///
|
||||
/// // A similar thing happens when you do a ConsoleMethod.
|
||||
/// @endcode
|
||||
///
|
||||
/// As you can see, several global items are defined when you use the ConsoleFunction method.
|
||||
/// The macro constructs the name of these items from the parameters you passed it. Your
|
||||
/// implementation of the console function is is placed in a function with a name based on
|
||||
/// the actual name of the console funnction. In addition, a ConsoleConstructor is declared.
|
||||
///
|
||||
/// Because it is defined as a global, the constructor for the ConsoleConstructor is called
|
||||
/// before execution of main() is started. The constructor is called once for each global
|
||||
/// ConsoleConstructor variable, in the order in which they were defined (this property only holds true
|
||||
/// within file scope).
|
||||
///
|
||||
/// We have ConsoleConstructor create a linked list at constructor time, by storing a static
|
||||
/// pointer to the head of the list, and keeping a pointer to the next item in each instance
|
||||
/// of ConsoleConstructor. init() is a helper function in this process, automatically filling
|
||||
/// in commonly used fields and updating first and next as needed. In this way, a list of
|
||||
/// items to add to the console is assemble in memory, ready for use, before we start
|
||||
/// execution of the program proper.
|
||||
///
|
||||
/// In Con::init(), ConsoleConstructor::setup() is called to process this prepared list. Each
|
||||
/// item in the list is iterated over, and the appropriate Con namespace functions (usually
|
||||
/// Con::addCommand) are invoked to register the ConsoleFunctions and ConsoleMethods in
|
||||
/// the appropriate namespaces.
|
||||
///
|
||||
/// @see Namespace
|
||||
/// @see Con
|
||||
/// @{
|
||||
|
||||
ConsoleConstructor *next;
|
||||
static ConsoleConstructor *first;
|
||||
|
||||
void init(const char *cName, const char *fName, const char *usg, S32 minArgs, S32 maxArgs);
|
||||
static void setup();
|
||||
/// @}
|
||||
|
||||
/// @name Basic Console Constructors
|
||||
/// @{
|
||||
|
||||
ConsoleConstructor(const char *className, const char *funcName, StringCallback sfunc, const char* usage, S32 minArgs, S32 maxArgs);
|
||||
ConsoleConstructor(const char *className, const char *funcName, IntCallback ifunc, const char* usage, S32 minArgs, S32 maxArgs);
|
||||
ConsoleConstructor(const char *className, const char *funcName, FloatCallback ffunc, const char* usage, S32 minArgs, S32 maxArgs);
|
||||
ConsoleConstructor(const char *className, const char *funcName, VoidCallback vfunc, const char* usage, S32 minArgs, S32 maxArgs);
|
||||
ConsoleConstructor(const char *className, const char *funcName, BoolCallback bfunc, const char* usage, S32 minArgs, S32 maxArgs);
|
||||
/// @}
|
||||
|
||||
/// @name Magic Console Constructors
|
||||
///
|
||||
/// These perform various pieces of "magic" related to consoleDoc functionality.
|
||||
/// @ref console_autodoc
|
||||
/// @{
|
||||
|
||||
/// Indicates a group marker. (A doxygen illusion)
|
||||
///
|
||||
/// @see Con::markCommandGroup
|
||||
/// @ref console_autodoc
|
||||
ConsoleConstructor(const char *className, const char *groupName, const char* usage);
|
||||
|
||||
/// @}
|
||||
};
|
||||
|
||||
/// @name Global Console Definition Macros
|
||||
///
|
||||
/// @note If TORQUE_DEBUG is defined, then we gather documentation information, and
|
||||
/// do some extra sanity checks.
|
||||
///
|
||||
/// @see ConsoleConstructor
|
||||
/// @ref console_autodoc
|
||||
/// @{
|
||||
|
||||
// O hackery of hackeries
|
||||
#define conmethod_return_const return (const
|
||||
#define conmethod_return_S32 return (S32
|
||||
#define conmethod_return_F32 return (F32
|
||||
#define conmethod_nullify(val)
|
||||
#define conmethod_return_void conmethod_nullify(void
|
||||
#define conmethod_return_bool return (bool
|
||||
|
||||
#if !defined(TORQUE_SHIPPING)
|
||||
|
||||
// Console function macros
|
||||
# define ConsoleFunctionGroupBegin(groupName, usage) \
|
||||
static ConsoleConstructor gConsoleFunctionGroup##groupName##__GroupBegin(NULL,#groupName,usage);
|
||||
|
||||
# define ConsoleFunction(name,returnType,minArgs,maxArgs,usage1) \
|
||||
static returnType c##name(SimObject *, S32, const char **argv); \
|
||||
static ConsoleConstructor g##name##obj(NULL,#name,c##name,usage1,minArgs,maxArgs); \
|
||||
static returnType c##name(SimObject *, S32 argc, const char **argv)
|
||||
|
||||
# define ConsoleFunctionGroupEnd(groupName) \
|
||||
static ConsoleConstructor gConsoleFunctionGroup##groupName##__GroupEnd(NULL,#groupName,NULL);
|
||||
|
||||
// Console method macros
|
||||
# define ConsoleMethodGroupBegin(className, groupName, usage) \
|
||||
static ConsoleConstructor className##groupName##__GroupBegin(#className,#groupName,usage);
|
||||
|
||||
# define ConsoleMethod(className,name,returnType,minArgs,maxArgs,usage1) \
|
||||
static inline returnType c##className##name(className *, S32, const char **argv); \
|
||||
static returnType c##className##name##caster(SimObject *object, S32 argc, const char **argv) { \
|
||||
AssertFatal( dynamic_cast<className*>( object ), "Object passed to " #name " is not a " #className "!" ); \
|
||||
conmethod_return_##returnType ) c##className##name(static_cast<className*>(object),argc,argv); \
|
||||
}; \
|
||||
static ConsoleConstructor className##name##obj(#className,#name,c##className##name##caster,usage1,minArgs,maxArgs); \
|
||||
static inline returnType c##className##name(className *object, S32 argc, const char **argv)
|
||||
|
||||
# define ConsoleStaticMethod(className,name,returnType,minArgs,maxArgs,usage1) \
|
||||
static inline returnType c##className##name(S32, const char **); \
|
||||
static returnType c##className##name##caster(SimObject *object, S32 argc, const char **argv) { \
|
||||
conmethod_return_##returnType ) c##className##name(argc,argv); \
|
||||
}; \
|
||||
static ConsoleConstructor \
|
||||
className##name##obj(#className,#name,c##className##name##caster,usage1,minArgs,maxArgs); \
|
||||
static inline returnType c##className##name(S32 argc, const char **argv)
|
||||
|
||||
# define ConsoleMethodGroupEnd(className, groupName) \
|
||||
static ConsoleConstructor className##groupName##__GroupEnd(#className,#groupName,NULL);
|
||||
|
||||
#else
|
||||
|
||||
// These do nothing if we don't want doc information.
|
||||
# define ConsoleFunctionGroupBegin(groupName, usage)
|
||||
# define ConsoleFunctionGroupEnd(groupName)
|
||||
# define ConsoleMethodGroupBegin(className, groupName, usage)
|
||||
# define ConsoleMethodGroupEnd(className, groupName)
|
||||
|
||||
// These are identical to what's above, we just want to null out the usage strings.
|
||||
# define ConsoleFunction(name,returnType,minArgs,maxArgs,usage1) \
|
||||
static returnType c##name(SimObject *, S32, const char **); \
|
||||
static ConsoleConstructor g##name##obj(NULL,#name,c##name,"",minArgs,maxArgs);\
|
||||
static returnType c##name(SimObject *, S32 argc, const char **argv)
|
||||
|
||||
# define ConsoleMethod(className,name,returnType,minArgs,maxArgs,usage1) \
|
||||
static inline returnType c##className##name(className *, S32, const char **argv); \
|
||||
static returnType c##className##name##caster(SimObject *object, S32 argc, const char **argv) { \
|
||||
conmethod_return_##returnType ) c##className##name(static_cast<className*>(object),argc,argv); \
|
||||
}; \
|
||||
static ConsoleConstructor \
|
||||
className##name##obj(#className,#name,c##className##name##caster,"",minArgs,maxArgs); \
|
||||
static inline returnType c##className##name(className *object, S32 argc, const char **argv)
|
||||
|
||||
# define ConsoleStaticMethod(className,name,returnType,minArgs,maxArgs,usage1) \
|
||||
static inline returnType c##className##name(S32, const char **); \
|
||||
static returnType c##className##name##caster(SimObject *object, S32 argc, const char **argv) { \
|
||||
conmethod_return_##returnType ) c##className##name(argc,argv); \
|
||||
}; \
|
||||
static ConsoleConstructor \
|
||||
className##name##obj(#className,#name,c##className##name##caster,"",minArgs,maxArgs); \
|
||||
static inline returnType c##className##name(S32 argc, const char **argv)
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
/// @}
|
||||
|
||||
#endif
|
378
engine/console/consoleDoc.cc
Executable file
378
engine/console/consoleDoc.cc
Executable file
@ -0,0 +1,378 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "console/console.h"
|
||||
|
||||
#include "console/ast.h"
|
||||
#include "core/tAlgorithm.h"
|
||||
#include "core/resManager.h"
|
||||
|
||||
#include "core/findMatch.h"
|
||||
#include "console/consoleInternal.h"
|
||||
#include "console/consoleObject.h"
|
||||
#include "core/fileStream.h"
|
||||
#include "console/compiler.h"
|
||||
|
||||
//--- Information pertaining to this page... ------------------
|
||||
/// @file
|
||||
///
|
||||
/// For specifics on using the consoleDoc functionality, see @ref console_autodoc
|
||||
|
||||
ConsoleFunctionGroupBegin(ConsoleDoc, "Console self-documentation functions. These output psuedo C++ suitable for feeeding through Doxygen or another auto documentation tool.");
|
||||
|
||||
ConsoleFunction(dumpConsoleClasses, void, 1, 1, "() dumps all declared console classes to the console.")
|
||||
{
|
||||
Namespace::dumpClasses();
|
||||
}
|
||||
|
||||
ConsoleFunction(dumpConsoleFunctions, void, 1, 1, "() dumps all declared console functions to the console.")
|
||||
{
|
||||
Namespace::dumpFunctions();
|
||||
}
|
||||
|
||||
ConsoleFunctionGroupEnd(ConsoleDoc);
|
||||
|
||||
/// Helper table to convert type ids to human readable names.
|
||||
const char *typeNames[] =
|
||||
{
|
||||
"Script",
|
||||
"string",
|
||||
"int",
|
||||
"float",
|
||||
"void",
|
||||
"bool",
|
||||
"",
|
||||
"",
|
||||
"unknown_overload"
|
||||
};
|
||||
|
||||
void printClassHeader(const char * className, const char * superClassName, const bool stub)
|
||||
{
|
||||
if(stub)
|
||||
{
|
||||
Con::printf("/// Stub class");
|
||||
Con::printf("/// ");
|
||||
Con::printf("/// @note This is a stub class to ensure a proper class hierarchy. No ");
|
||||
Con::printf("/// information was available for this class.");
|
||||
}
|
||||
|
||||
// Print out appropriate class header
|
||||
if(superClassName)
|
||||
Con::printf("class %s : public %s {", className, superClassName ? superClassName : "");
|
||||
else if(!className)
|
||||
Con::printf("namespace Global {");
|
||||
else
|
||||
Con::printf("class %s {", className);
|
||||
|
||||
if(className)
|
||||
Con::printf(" public:");
|
||||
|
||||
}
|
||||
|
||||
void printClassMethod(const bool isVirtual, const char *retType, const char *methodName, const char* args, const char*usage)
|
||||
{
|
||||
if(usage && usage[0] != ';' && usage[0] != 0)
|
||||
Con::printf(" /*! %s */", usage);
|
||||
Con::printf(" %s%s %s(%s) {}", isVirtual ? "virtual " : "", retType, methodName, args);
|
||||
}
|
||||
|
||||
void printGroupStart(const char * aName, const char * aDocs)
|
||||
{
|
||||
Con::printf("");
|
||||
Con::printf(" /*! @name %s", aName);
|
||||
|
||||
if(aDocs)
|
||||
{
|
||||
Con::printf(" ");
|
||||
Con::printf(" %s", aDocs);
|
||||
}
|
||||
|
||||
Con::printf(" @{ */");
|
||||
}
|
||||
|
||||
void printClassMember(const bool isDeprec, const char * aType, const char * aName, const char * aDocs)
|
||||
{
|
||||
Con::printf(" /*!");
|
||||
|
||||
if(aDocs)
|
||||
{
|
||||
Con::printf(" %s", aDocs);
|
||||
Con::printf(" ");
|
||||
}
|
||||
|
||||
if(isDeprec)
|
||||
Con::printf(" @deprecated This member is deprecated, which means that its value is always undefined.");
|
||||
|
||||
Con::printf(" */");
|
||||
|
||||
Con::printf(" %s %s;", isDeprec ? "deprecated" : aType, aName);
|
||||
}
|
||||
|
||||
void printGroupEnd()
|
||||
{
|
||||
Con::printf(" /// @}");
|
||||
Con::printf("");
|
||||
}
|
||||
|
||||
void printClassFooter()
|
||||
{
|
||||
Con::printf("};");
|
||||
Con::printf("");
|
||||
}
|
||||
|
||||
void Namespace::printNamespaceEntries(Namespace * g)
|
||||
{
|
||||
static bool inGroup = false;
|
||||
|
||||
// Go through all the entries.
|
||||
// Iterate through the methods of the namespace...
|
||||
for(Entry *ewalk = g->mEntryList; ewalk; ewalk = ewalk->mNext)
|
||||
{
|
||||
char buffer[1024]; //< This will bite you in the butt someday.
|
||||
int eType = ewalk->mType;
|
||||
const char * funcName = ewalk->mFunctionName;
|
||||
|
||||
// If it's a function
|
||||
if(eType >= Entry::ScriptFunctionType || eType == Entry::OverloadMarker)
|
||||
{
|
||||
if(eType==Entry::OverloadMarker)
|
||||
{
|
||||
// Deal with crap from the OverloadMarker case.
|
||||
// It has no type information so we have to "correct" its type.
|
||||
|
||||
// Find the original
|
||||
eType = 8;
|
||||
for(Entry *eseek = g->mEntryList; eseek; eseek = eseek->mNext)
|
||||
{
|
||||
if(!dStrcmp(eseek->mFunctionName, ewalk->cb.mGroupName))
|
||||
{
|
||||
eType = eseek->mType;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// And correct the name
|
||||
funcName = ewalk->cb.mGroupName;
|
||||
}
|
||||
|
||||
// A quick note - if your usage field starts with a (, then it's auto-integrated into
|
||||
// the script docs! Use this HEAVILY!
|
||||
|
||||
// We add some heuristics here as well. If you're of the form:
|
||||
// *.methodName(*)
|
||||
// then we will also extract parameters.
|
||||
|
||||
const char *use = ewalk->mUsage ? ewalk->mUsage : "";
|
||||
const char *bgn = dStrchr(use, '(');
|
||||
const char *end = dStrchr(use, ')');
|
||||
const char *dot = dStrchr(use, '.');
|
||||
|
||||
if(use[0] == '(')
|
||||
{
|
||||
if(!end)
|
||||
end = use + 1;
|
||||
|
||||
use++;
|
||||
|
||||
U32 len = end - use;
|
||||
dStrncpy(buffer, use, len);
|
||||
buffer[len] = 0;
|
||||
|
||||
printClassMethod(true, typeNames[eType], funcName, buffer, end+1);
|
||||
|
||||
continue; // Skip to next one.
|
||||
}
|
||||
|
||||
// We check to see if they're giving a prototype.
|
||||
if(dot && bgn && end) // If there's two parentheses, and a dot...
|
||||
if(dot < bgn && bgn < end) // And they're in the order dot, bgn, end...
|
||||
{
|
||||
use++;
|
||||
U32 len = end - bgn - 1;
|
||||
dStrncpy(buffer, bgn+1, len);
|
||||
buffer[len] = 0;
|
||||
|
||||
// Then let's do the heuristic-trick
|
||||
printClassMethod(true, typeNames[eType], funcName, buffer, end+1);
|
||||
continue; // Get to next item.
|
||||
}
|
||||
|
||||
// Finally, see if they did it foo(*) style.
|
||||
char* func_pos = dStrstr(use, funcName);
|
||||
if((func_pos) && (func_pos < bgn) && (end > bgn))
|
||||
{
|
||||
U32 len = end - bgn - 1;
|
||||
dStrncpy(buffer, bgn+1, len);
|
||||
buffer[len] = 0;
|
||||
|
||||
printClassMethod(true, typeNames[eType], funcName, buffer, end+1);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Default...
|
||||
printClassMethod(true, typeNames[eType], funcName, "", ewalk->mUsage);
|
||||
}
|
||||
else if(ewalk->mType == Entry::GroupMarker)
|
||||
{
|
||||
if(!inGroup)
|
||||
printGroupStart(ewalk->cb.mGroupName, ewalk->mUsage);
|
||||
else
|
||||
printGroupEnd();
|
||||
|
||||
inGroup = !inGroup;
|
||||
}
|
||||
else if(ewalk->mFunctionOffset) // If it's a builtin function...
|
||||
{
|
||||
ewalk->mCode->getFunctionArgs(buffer, ewalk->mFunctionOffset);
|
||||
printClassMethod(false, typeNames[ewalk->mType], ewalk->mFunctionName, buffer, "");
|
||||
}
|
||||
else
|
||||
{
|
||||
Con::printf(" // got an unknown thing?? %d", ewalk->mType );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Namespace::dumpClasses()
|
||||
{
|
||||
Vector<Namespace *> vec;
|
||||
trashCache();
|
||||
|
||||
// We use mHashSequence to mark if we have traversed...
|
||||
// so mark all as zero to start.
|
||||
for(Namespace *walk = mNamespaceList; walk; walk = walk->mNext)
|
||||
walk->mHashSequence = 0;
|
||||
|
||||
for(Namespace *walk = mNamespaceList; walk; walk = walk->mNext)
|
||||
{
|
||||
Vector<Namespace *> stack;
|
||||
|
||||
// Get all the parents of this namespace... (and mark them as we go)
|
||||
Namespace *parentWalk = walk;
|
||||
while(parentWalk)
|
||||
{
|
||||
if(parentWalk->mHashSequence != 0)
|
||||
break;
|
||||
if(parentWalk->mPackage == 0)
|
||||
{
|
||||
parentWalk->mHashSequence = 1; // Mark as traversed.
|
||||
stack.push_back(parentWalk);
|
||||
}
|
||||
parentWalk = parentWalk->mParent;
|
||||
}
|
||||
|
||||
// Load stack into our results vector.
|
||||
while(stack.size())
|
||||
{
|
||||
vec.push_back(stack[stack.size() - 1]);
|
||||
stack.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
// Go through previously discovered classes
|
||||
U32 i;
|
||||
for(i = 0; i < vec.size(); i++)
|
||||
{
|
||||
const char *className = vec[i]->mName;
|
||||
const char *superClassName = vec[i]->mParent ? vec[i]->mParent->mName : NULL;
|
||||
|
||||
// Skip the global namespace, that gets dealt with in dumpFunctions
|
||||
if(!className) continue;
|
||||
|
||||
// If we hit a class with no members and no classRep, do clever filtering.
|
||||
if(vec[i]->mEntryList == NULL && vec[i]->mClassRep == NULL)
|
||||
{
|
||||
// Print out a short stub so we get a proper class hierarchy.
|
||||
if(superClassName) { // Filter hack; we don't want non-inheriting classes...
|
||||
printClassHeader(className,superClassName, true);
|
||||
printClassFooter();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Print the header for the class..
|
||||
printClassHeader(className, superClassName, false);
|
||||
|
||||
// Deal with entries.
|
||||
printNamespaceEntries(vec[i]);
|
||||
|
||||
// Deal with the classRep (to get members)...
|
||||
AbstractClassRep *rep = vec[i]->mClassRep;
|
||||
AbstractClassRep::FieldList emptyList;
|
||||
AbstractClassRep::FieldList *parentList = &emptyList;
|
||||
AbstractClassRep::FieldList *fieldList = &emptyList;
|
||||
|
||||
if(rep)
|
||||
{
|
||||
// Get information about the parent's fields...
|
||||
AbstractClassRep *parentRep = vec[i]->mParent ? vec[i]->mParent->mClassRep : NULL;
|
||||
if(parentRep)
|
||||
parentList = &(parentRep->mFieldList);
|
||||
|
||||
// Get information about our fields
|
||||
fieldList = &(rep->mFieldList);
|
||||
|
||||
// Go through all our fields...
|
||||
for(U32 j = 0; j < fieldList->size(); j++)
|
||||
{
|
||||
switch((*fieldList)[j].type)
|
||||
{
|
||||
case AbstractClassRep::StartGroupFieldType:
|
||||
printGroupStart((*fieldList)[j].pGroupname, (*fieldList)[j].pFieldDocs);
|
||||
break;
|
||||
case AbstractClassRep::EndGroupFieldType:
|
||||
printGroupEnd();
|
||||
break;
|
||||
default:
|
||||
case AbstractClassRep::DepricatedFieldType:
|
||||
{
|
||||
bool isDeprecated = ((*fieldList)[j].type == AbstractClassRep::DepricatedFieldType);
|
||||
|
||||
if(isDeprecated)
|
||||
{
|
||||
printClassMember(
|
||||
true,
|
||||
"<deprecated>",
|
||||
(*fieldList)[j].pFieldname,
|
||||
(*fieldList)[j].pFieldDocs
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
ConsoleBaseType *cbt = ConsoleBaseType::getType((*fieldList)[j].type);
|
||||
|
||||
printClassMember(
|
||||
false,
|
||||
cbt ? cbt->getTypeClassName() : "<unknown>",
|
||||
(*fieldList)[j].pFieldname,
|
||||
(*fieldList)[j].pFieldDocs
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close the class/namespace.
|
||||
printClassFooter();
|
||||
}
|
||||
}
|
||||
|
||||
void Namespace::dumpFunctions()
|
||||
{
|
||||
// Get the global namespace.
|
||||
Namespace* g = find(NULL); //->mParent;
|
||||
|
||||
printClassHeader(NULL,NULL, false);
|
||||
|
||||
while(g)
|
||||
{
|
||||
printNamespaceEntries(g);
|
||||
g = g->mParent;
|
||||
}
|
||||
|
||||
printClassFooter();
|
||||
}
|
176
engine/console/consoleDoc.h
Executable file
176
engine/console/consoleDoc.h
Executable file
@ -0,0 +1,176 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// This file exists solely to document consoleDoc.cc
|
||||
|
||||
/// @page console_autodoc Console Auto-Documentation
|
||||
///
|
||||
/// @see consoleDoc.cc
|
||||
///
|
||||
/// @section console_autodoc_using Using Console Auto-Documentation
|
||||
///
|
||||
/// There are on the order of three hundred functions exposed to the script language
|
||||
/// through the console. It is therefore extremely important that they be documented,
|
||||
/// but due to their number, it is difficult to maintain a seperate reference document.
|
||||
///
|
||||
/// Therefore, a simple documentation system has been built into the scripting engine. It
|
||||
/// was initially added by Mark Frohnmayer, and later enhanced by Ben Garney. The
|
||||
/// scripting engine supports grouping functions and methods, to help organize the
|
||||
/// several hundred functions, as well as associating a "usage string" with functions and
|
||||
/// groups.
|
||||
///
|
||||
/// @note The results of a console doc dump will vary depending on when you run it. If
|
||||
/// you run it, for example, while in the game menu, it won't output any data for
|
||||
/// the script-defined classes which are defined for gameplay. To get comprehensive
|
||||
/// documentation, you may need to write a special script that will get all your
|
||||
/// classes loaded into the scripting engine.
|
||||
///
|
||||
/// The console documentation system is designed to output a dump of the current state
|
||||
/// of the scripting engine in a format understandable by Doxygen. It does this by
|
||||
/// traversing the namespace/class hierarchy in memory at the time of the dump, and
|
||||
/// outputting psuedo-C++ code equivalent to this class hierarchy.
|
||||
///
|
||||
/// @subsection console_autodoc_using_script For the Scripter...
|
||||
///
|
||||
/// Currently, there is no way to associate usage strings or other documentation with script code
|
||||
/// like you can with C++ code.
|
||||
///
|
||||
/// You can get a list of all the methods and fields of an object from any object which inherits
|
||||
/// from SimObject (ie, every object), as well as the documentation on those objects by using the
|
||||
/// dump() method from the console:
|
||||
///
|
||||
/// @code
|
||||
/// ==>$foo = new SimObject();
|
||||
/// ==>$foo.dump();
|
||||
/// Member Fields:
|
||||
/// Tagged Fields:
|
||||
/// Methods:
|
||||
/// delete() - obj.delete()
|
||||
/// dump() - obj.dump()
|
||||
/// getClassName() - obj.getClassName()
|
||||
/// getGroup() - obj.getGroup()
|
||||
/// getId() - obj.getId()
|
||||
/// getName() - obj.getName()
|
||||
/// getType() - obj.getType()
|
||||
/// save() - obj.save(fileName, <selectedOnly>)
|
||||
/// schedule() - object.schedule(time, command, <arg1...argN>);
|
||||
/// setName() - obj.setName(newName)
|
||||
/// @endcode
|
||||
///
|
||||
/// In the Torque example app, there are two functions defined in common\\client\\scriptDoc.cs
|
||||
/// which automate the process of dumping the documentation. They make use of the ConsoleLogger
|
||||
/// object to output the documentation to a file, and look like this:
|
||||
///
|
||||
/// @note You may want to add this code, or code like it, to your project if you have
|
||||
/// rewritten the script code in common.
|
||||
///
|
||||
/// @code
|
||||
/// // Writes out all script functions to a file
|
||||
/// function writeOutFunctions() {
|
||||
/// new ConsoleLogger( logger, "scriptFunctions.txt", false );
|
||||
/// dumpConsoleFunctions();
|
||||
/// logger.delete();
|
||||
/// }
|
||||
///
|
||||
/// // Writes out all script classes to a file
|
||||
/// function writeOutClasses() {
|
||||
/// new ConsoleLogger( logger, "scriptClasses.txt", false );
|
||||
/// dumpConsoleClasses();
|
||||
/// logger.delete();
|
||||
/// }
|
||||
/// @endcode
|
||||
///
|
||||
/// @subsection console_autodoc_using_coder For the C++ Coder...
|
||||
///
|
||||
/// @note <b>It is of the utmost important that you keep your usage strings up to date!</b>
|
||||
/// Usage strings are the only way that a scripter has to know how to use the methods,
|
||||
/// functions, and variables you expose. Misleading, missing, or out of date documentation
|
||||
/// will make their lives much harder - and yours, too, because you'll have to keep
|
||||
/// explaining things to them! So make everyone's lives easier - keep your usage strings
|
||||
/// clear, concise, and up to date.
|
||||
///
|
||||
/// There are four types of items which can be documented using the autodocumentation system:
|
||||
/// - <b>Fields</b>, which are defined using the addField() calls. They are documented
|
||||
/// by passing a string to the usage parameter.
|
||||
/// - <b>Field groups</b>, which are defined using the beginGroup() and endGroup() calls.
|
||||
/// They are documented by passing a descriptive string to the usage parameter.
|
||||
/// - <b>Method groups</b>, which are defined using beginCommandGroup(), endCommandGroup(),
|
||||
/// ConsoleMethodGroupEnd(), ConsoleMethodGroupBegin(), ConsoleFunctionGroupEnd(), and
|
||||
/// ConsoleFunctionGroupBegin().
|
||||
/// - <b>Methods and functions</b>, which are defined using either SimObject::addCommand(),
|
||||
/// the ConsoleMethod() macro, or the ConsoleFunction() macro. Methods and functions are
|
||||
/// special in that the usage strings should be in a specific format, so
|
||||
/// that parameter information can be extracted from them and placed into the Doxygen
|
||||
/// output.
|
||||
///
|
||||
/// You can use standard Doxygen commands in your comments, to make the documentation clearer.
|
||||
/// Of particular use are \@returns, \@param, \@note, and \@deprecated.
|
||||
///
|
||||
/// <b>Examples using global definitions.</b>
|
||||
///
|
||||
/// @code
|
||||
/// // Example of using Doxygen commands.
|
||||
/// ConsoleFunction(alxGetWaveLen, S32, 2, 2, "(string filename)"
|
||||
/// "Get length of a wave file\n\n"
|
||||
/// "@param filename File to determine length of.\n"
|
||||
/// "@returns Length in milliseconds.")
|
||||
///
|
||||
/// // A function group...
|
||||
/// ConsoleFunctionGroupBegin(Example, "This is an example group! Notice that the name for the group"
|
||||
/// "must be a valid identifier, due to limitations in the C preprocessor.");
|
||||
///
|
||||
/// // ConsoleFunction definitions go here.
|
||||
///
|
||||
/// ConsoleFunctionGroupEnd(Example);
|
||||
///
|
||||
/// // You can do similar things with methods...
|
||||
/// ConsoleMethodGroupBegin(SimSet, UsefulFuncs, "Here are some useful functions involving a SimSet.");
|
||||
/// ConsoleMethod(SimSet, listObjects, void, 2, 2, "set.listObjects();")
|
||||
/// ConsoleMethodGroupEnd(SimSet, UsefulFuncs, "Here are some more useful functions involving a SimSet.");
|
||||
/// @endcode
|
||||
///
|
||||
/// <b>Examples using addField</b>
|
||||
///
|
||||
/// @note Using addCommand is strongly deprecated.
|
||||
///
|
||||
/// @code
|
||||
/// // Example of a field group.
|
||||
/// addGroup( "Logging", "Things relating to logging." );
|
||||
/// addField( "level", TypeEnum, Offset( mLevel, ConsoleLogger ), 1, &gLogLevelTable );
|
||||
/// endGroup( "Logging" );
|
||||
/// @endcode
|
||||
///
|
||||
/// @section console_autodoc_makingdocs How to Generate Console Docs
|
||||
///
|
||||
/// Console docs can be generated by running the dumpConsoleFunctions() and
|
||||
/// dumpConsoleClasses(), then running the output through Doxygen. There is an
|
||||
/// example Doxygen configuration file to do this in HEAD,
|
||||
/// at doc\\doxygen\\html\\script_doxygen.html.cfg. Doxygen will parse the psuedo-C++
|
||||
/// generated by the console doc code and produce a class hierarchy and documentation
|
||||
/// of the global namespace. You may need to tweak the paths in the configuration file
|
||||
/// slightly to reflect your individual setup.
|
||||
///
|
||||
/// @section console_autodoc_internals Console Auto-Documentation Internals
|
||||
///
|
||||
/// The consoleDoc system works by inserting "hidden" entries in Namespace and
|
||||
/// AbstractClassRep; these hidden entries are assigned special type IDs so that
|
||||
/// they aren't touched by the standard name resolution code. At documentation
|
||||
/// creation time, the dumpConsole functions iterate through the Namespace hierarchy
|
||||
/// and the AbstractClassRep data and extract this "hidden" information, outputting
|
||||
/// it in a Doxygen-compatible format.
|
||||
///
|
||||
/// @note You can customize the output of the console documentation system by modifying
|
||||
/// these functions:
|
||||
/// - printClassHeader()
|
||||
/// - printClassMethod()
|
||||
/// - printGroupStart()
|
||||
/// - printClassMember()
|
||||
/// - printGroupEnd()
|
||||
/// - printClassFooter()
|
||||
///
|
||||
/// @note There was once support for 'overloaded' script functions; ie, script functions
|
||||
/// with multiple usage strings. Certain functions in the audio library used this.
|
||||
/// However, it was deemed too complex, and removed from the scripting engine. There
|
||||
/// are still some latent traces of it, however.
|
1270
engine/console/consoleFunctions.cc
Executable file
1270
engine/console/consoleFunctions.cc
Executable file
File diff suppressed because it is too large
Load Diff
1064
engine/console/consoleInternal.cc
Executable file
1064
engine/console/consoleInternal.cc
Executable file
File diff suppressed because it is too large
Load Diff
310
engine/console/consoleInternal.h
Executable file
310
engine/console/consoleInternal.h
Executable file
@ -0,0 +1,310 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _CONSOLEINTERNAL_H_
|
||||
#define _CONSOLEINTERNAL_H_
|
||||
|
||||
#ifndef _STRINGTABLE_H_
|
||||
#include "core/stringTable.h"
|
||||
#endif
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/tVector.h"
|
||||
#endif
|
||||
#ifndef _CONSOLETYPES_H_
|
||||
#include "console/consoleTypes.h"
|
||||
#endif
|
||||
|
||||
class ExprEvalState;
|
||||
struct FunctionDecl;
|
||||
class CodeBlock;
|
||||
class AbstractClassRep;
|
||||
|
||||
class Namespace
|
||||
{
|
||||
enum {
|
||||
MaxActivePackages = 512,
|
||||
};
|
||||
|
||||
static U32 mNumActivePackages;
|
||||
static U32 mOldNumActivePackages;
|
||||
static StringTableEntry mActivePackages[MaxActivePackages];
|
||||
public:
|
||||
StringTableEntry mName;
|
||||
StringTableEntry mPackage;
|
||||
|
||||
Namespace *mParent;
|
||||
Namespace *mNext;
|
||||
AbstractClassRep *mClassRep;
|
||||
U32 mRefCountToParent;
|
||||
|
||||
struct Entry
|
||||
{
|
||||
enum {
|
||||
GroupMarker = -3,
|
||||
OverloadMarker = -2,
|
||||
InvalidFunctionType = -1,
|
||||
ScriptFunctionType,
|
||||
StringCallbackType,
|
||||
IntCallbackType,
|
||||
FloatCallbackType,
|
||||
VoidCallbackType,
|
||||
BoolCallbackType
|
||||
};
|
||||
|
||||
Namespace *mNamespace;
|
||||
Entry *mNext;
|
||||
StringTableEntry mFunctionName;
|
||||
S32 mType;
|
||||
S32 mMinArgs;
|
||||
S32 mMaxArgs;
|
||||
const char *mUsage;
|
||||
StringTableEntry mPackage;
|
||||
|
||||
CodeBlock *mCode;
|
||||
U32 mFunctionOffset;
|
||||
union {
|
||||
StringCallback mStringCallbackFunc;
|
||||
IntCallback mIntCallbackFunc;
|
||||
VoidCallback mVoidCallbackFunc;
|
||||
FloatCallback mFloatCallbackFunc;
|
||||
BoolCallback mBoolCallbackFunc;
|
||||
const char* mGroupName;
|
||||
} cb;
|
||||
Entry();
|
||||
void clear();
|
||||
|
||||
const char *execute(S32 argc, const char **argv, ExprEvalState *state);
|
||||
|
||||
};
|
||||
Entry *mEntryList;
|
||||
|
||||
Entry **mHashTable;
|
||||
U32 mHashSize;
|
||||
U32 mHashSequence; ///< @note The hash sequence is used by the autodoc console facility
|
||||
/// as a means of testing reference state.
|
||||
|
||||
Namespace();
|
||||
void addFunction(StringTableEntry name, CodeBlock *cb, U32 functionOffset);
|
||||
void addCommand(StringTableEntry name,StringCallback, const char *usage, S32 minArgs, S32 maxArgs);
|
||||
void addCommand(StringTableEntry name,IntCallback, const char *usage, S32 minArgs, S32 maxArgs);
|
||||
void addCommand(StringTableEntry name,FloatCallback, const char *usage, S32 minArgs, S32 maxArgs);
|
||||
void addCommand(StringTableEntry name,VoidCallback, const char *usage, S32 minArgs, S32 maxArgs);
|
||||
void addCommand(StringTableEntry name,BoolCallback, const char *usage, S32 minArgs, S32 maxArgs);
|
||||
|
||||
void addOverload(const char *name, const char* altUsage);
|
||||
|
||||
void markGroup(const char* name, const char* usage);
|
||||
char * lastUsage;
|
||||
|
||||
void getEntryList(Vector<Entry *> *);
|
||||
|
||||
Entry *lookup(StringTableEntry name);
|
||||
Entry *lookupRecursive(StringTableEntry name);
|
||||
Entry *createLocalEntry(StringTableEntry name);
|
||||
void buildHashTable();
|
||||
void clearEntries();
|
||||
bool classLinkTo(Namespace *parent);
|
||||
|
||||
const char *tabComplete(const char *prevText, S32 baseLen, bool fForward);
|
||||
|
||||
static U32 mCacheSequence;
|
||||
static DataChunker mCacheAllocator;
|
||||
static DataChunker mAllocator;
|
||||
static void trashCache();
|
||||
static Namespace *mNamespaceList;
|
||||
static Namespace *mGlobalNamespace;
|
||||
|
||||
static void init();
|
||||
static void shutdown();
|
||||
static Namespace *global();
|
||||
|
||||
static Namespace *find(StringTableEntry name, StringTableEntry package=NULL);
|
||||
|
||||
static void activatePackage(StringTableEntry name);
|
||||
static void deactivatePackage(StringTableEntry name);
|
||||
static void dumpClasses();
|
||||
static void dumpFunctions();
|
||||
static void printNamespaceEntries(Namespace * g);
|
||||
static void unlinkPackages();
|
||||
static void relinkPackages();
|
||||
static bool isPackage(StringTableEntry name);
|
||||
};
|
||||
|
||||
extern char *typeValueEmpty;
|
||||
|
||||
class Dictionary
|
||||
{
|
||||
public:
|
||||
struct Entry
|
||||
{
|
||||
enum
|
||||
{
|
||||
TypeInternalInt = -3,
|
||||
TypeInternalFloat = -2,
|
||||
TypeInternalString = -1,
|
||||
};
|
||||
|
||||
StringTableEntry name;
|
||||
Entry *nextEntry;
|
||||
S32 type;
|
||||
char *sval;
|
||||
U32 ival; // doubles as strlen when type = -1
|
||||
F32 fval;
|
||||
U32 bufferLen;
|
||||
void *dataPtr;
|
||||
|
||||
Entry(StringTableEntry name);
|
||||
~Entry();
|
||||
|
||||
U32 getIntValue()
|
||||
{
|
||||
if(type <= TypeInternalString)
|
||||
return ival;
|
||||
else
|
||||
return dAtoi(Con::getData(type, dataPtr, 0));
|
||||
}
|
||||
F32 getFloatValue()
|
||||
{
|
||||
if(type <= TypeInternalString)
|
||||
return fval;
|
||||
else
|
||||
return dAtof(Con::getData(type, dataPtr, 0));
|
||||
}
|
||||
const char *getStringValue()
|
||||
{
|
||||
if(type == TypeInternalString)
|
||||
return sval;
|
||||
if(type == TypeInternalFloat)
|
||||
return Con::getData(TypeF32, &fval, 0);
|
||||
else if(type == TypeInternalInt)
|
||||
return Con::getData(TypeS32, &ival, 0);
|
||||
else
|
||||
return Con::getData(type, dataPtr, 0);
|
||||
}
|
||||
void setIntValue(U32 val)
|
||||
{
|
||||
if(type <= TypeInternalString)
|
||||
{
|
||||
fval = (F32)val;
|
||||
ival = val;
|
||||
if(sval != typeValueEmpty)
|
||||
{
|
||||
dFree(sval);
|
||||
sval = typeValueEmpty;
|
||||
}
|
||||
type = TypeInternalInt;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
const char *dptr = Con::getData(TypeS32, &val, 0);
|
||||
Con::setData(type, dataPtr, 0, 1, &dptr);
|
||||
}
|
||||
}
|
||||
void setFloatValue(F32 val)
|
||||
{
|
||||
if(type <= TypeInternalString)
|
||||
{
|
||||
fval = val;
|
||||
ival = static_cast<U32>(val);
|
||||
if(sval != typeValueEmpty)
|
||||
{
|
||||
dFree(sval);
|
||||
sval = typeValueEmpty;
|
||||
}
|
||||
type = TypeInternalFloat;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
const char *dptr = Con::getData(TypeF32, &val, 0);
|
||||
Con::setData(type, dataPtr, 0, 1, &dptr);
|
||||
}
|
||||
}
|
||||
void setStringValue(const char *value);
|
||||
};
|
||||
|
||||
private:
|
||||
struct HashTableData
|
||||
{
|
||||
Dictionary* owner;
|
||||
S32 size;
|
||||
S32 count;
|
||||
Entry **data;
|
||||
};
|
||||
|
||||
HashTableData *hashTable;
|
||||
ExprEvalState *exprState;
|
||||
public:
|
||||
StringTableEntry scopeName;
|
||||
Namespace *scopeNamespace;
|
||||
CodeBlock *code;
|
||||
U32 ip;
|
||||
|
||||
Dictionary();
|
||||
Dictionary(ExprEvalState *state, Dictionary* ref=NULL);
|
||||
~Dictionary();
|
||||
Entry *lookup(StringTableEntry name);
|
||||
Entry *add(StringTableEntry name);
|
||||
void setState(ExprEvalState *state, Dictionary* ref=NULL);
|
||||
void remove(Entry *);
|
||||
void reset();
|
||||
|
||||
void exportVariables(const char *varString, const char *fileName, bool append);
|
||||
void deleteVariables(const char *varString);
|
||||
|
||||
void setVariable(StringTableEntry name, const char *value);
|
||||
const char *getVariable(StringTableEntry name, bool *valid = NULL);
|
||||
|
||||
void addVariable(const char *name, S32 type, void *dataPtr);
|
||||
bool removeVariable(StringTableEntry name);
|
||||
|
||||
/// Return the best tab completion for prevText, with the length
|
||||
/// of the pre-tab string in baseLen.
|
||||
const char *tabComplete(const char *prevText, S32 baseLen, bool);
|
||||
};
|
||||
|
||||
class ExprEvalState
|
||||
{
|
||||
public:
|
||||
/// @name Expression Evaluation
|
||||
/// @{
|
||||
|
||||
///
|
||||
SimObject *thisObject;
|
||||
Dictionary::Entry *currentVariable;
|
||||
bool traceOn;
|
||||
|
||||
ExprEvalState();
|
||||
~ExprEvalState();
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Stack Management
|
||||
/// @{
|
||||
|
||||
///
|
||||
Dictionary globalVars;
|
||||
Vector<Dictionary *> stack;
|
||||
void setCurVarName(StringTableEntry name);
|
||||
void setCurVarNameCreate(StringTableEntry name);
|
||||
S32 getIntVariable();
|
||||
F64 getFloatVariable();
|
||||
const char *getStringVariable();
|
||||
void setIntVariable(S32 val);
|
||||
void setFloatVariable(F64 val);
|
||||
void setStringVariable(const char *str);
|
||||
|
||||
void pushFrame(StringTableEntry frameName, Namespace *ns);
|
||||
void popFrame();
|
||||
|
||||
/// Puts a reference to an existing stack frame
|
||||
/// on the top of the stack.
|
||||
void pushFrameRef(S32 stackIndex);
|
||||
|
||||
/// @}
|
||||
};
|
||||
|
||||
#endif
|
215
engine/console/consoleLogger.cc
Executable file
215
engine/console/consoleLogger.cc
Executable file
@ -0,0 +1,215 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "console/consoleLogger.h"
|
||||
#include "console/consoleTypes.h"
|
||||
|
||||
Vector<ConsoleLogger *> ConsoleLogger::mActiveLoggers;
|
||||
bool ConsoleLogger::smInitialized = false;
|
||||
|
||||
IMPLEMENT_CONOBJECT( ConsoleLogger );
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
ConsoleLogger::ConsoleLogger()
|
||||
{
|
||||
mFilename = NULL;
|
||||
mLogging = false;
|
||||
mAppend = false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
ConsoleLogger::ConsoleLogger( const char *fileName, bool append )
|
||||
{
|
||||
mLogging = false;
|
||||
|
||||
mLevel = ConsoleLogEntry::Normal;
|
||||
mFilename = StringTable->insert( fileName );
|
||||
mAppend = append;
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static EnumTable::Enums logLevelEnums[] =
|
||||
{
|
||||
{ ConsoleLogEntry::Normal, "normal" },
|
||||
{ ConsoleLogEntry::Warning, "warning" },
|
||||
{ ConsoleLogEntry::Error, "error" },
|
||||
};
|
||||
|
||||
static EnumTable gLogLevelTable( 3, &logLevelEnums[0] );
|
||||
|
||||
void ConsoleLogger::initPersistFields()
|
||||
{
|
||||
Parent::initPersistFields();
|
||||
|
||||
addGroup( "Logging" );
|
||||
addField( "level", TypeEnum, Offset( mLevel, ConsoleLogger ), 1, &gLogLevelTable );
|
||||
endGroup( "Logging" );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool ConsoleLogger::processArguments( S32 argc, const char **argv )
|
||||
{
|
||||
if( argc == 0 )
|
||||
return false;
|
||||
|
||||
bool append = false;
|
||||
|
||||
if( argc == 2 )
|
||||
append = dAtob( argv[1] );
|
||||
|
||||
mAppend = append;
|
||||
mFilename = StringTable->insert( argv[0] );
|
||||
|
||||
if( init() )
|
||||
{
|
||||
attach();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
ConsoleLogger::~ConsoleLogger()
|
||||
{
|
||||
detach();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool ConsoleLogger::init()
|
||||
{
|
||||
if( smInitialized )
|
||||
return true;
|
||||
|
||||
Con::addConsumer( ConsoleLogger::logCallback );
|
||||
smInitialized = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool ConsoleLogger::attach()
|
||||
{
|
||||
if( mFilename == NULL )
|
||||
{
|
||||
Con::errorf( "ConsoleLogger failed to attach: no filename supplied." );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check to see if this is initialized before using it
|
||||
if( !smInitialized )
|
||||
{
|
||||
if( !init() )
|
||||
{
|
||||
Con::errorf( "ConsoleLogger failed to initalize." );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if( mLogging )
|
||||
return false;
|
||||
|
||||
// Open the filestream
|
||||
mStream.open( mFilename, ( mAppend ? FileStream::WriteAppend : FileStream::Write ) );
|
||||
|
||||
// Add this to list of active loggers
|
||||
mActiveLoggers.push_back( this );
|
||||
mLogging = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool ConsoleLogger::detach()
|
||||
{
|
||||
|
||||
// Make sure this is valid before messing with it
|
||||
if( !smInitialized )
|
||||
{
|
||||
if( !init() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if( !mLogging )
|
||||
return false;
|
||||
|
||||
// Close filestream
|
||||
mStream.close();
|
||||
|
||||
// Remove this object from the list of active loggers
|
||||
for( int i = 0; i < mActiveLoggers.size(); i++ )
|
||||
{
|
||||
if( mActiveLoggers[i] == this )
|
||||
{
|
||||
mActiveLoggers.erase( i );
|
||||
mLogging = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false; // If this happens, it's bad...
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void ConsoleLogger::logCallback( ConsoleLogEntry::Level level, const char *consoleLine )
|
||||
{
|
||||
|
||||
ConsoleLogger *curr;
|
||||
|
||||
// Loop through active consumers and send them the message
|
||||
for( int i = 0; i < mActiveLoggers.size(); i++ )
|
||||
{
|
||||
curr = mActiveLoggers[i];
|
||||
|
||||
// If the log level is within the log threshhold, log it
|
||||
if( curr->mLevel <= level )
|
||||
curr->log( consoleLine );
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void ConsoleLogger::log( const char *consoleLine )
|
||||
{
|
||||
// Check to see if this is intalized before using it
|
||||
if( !smInitialized )
|
||||
{
|
||||
if( !init() )
|
||||
{
|
||||
Con::errorf( "I don't know how this happened, but log called on this without it being initialized" );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mStream.writeLine( (U8 *)consoleLine );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
ConsoleMethod( ConsoleLogger, attach, bool, 2, 2, "() Attaches this object to the console and begins logging" )
|
||||
{
|
||||
ConsoleLogger *logger = static_cast<ConsoleLogger *>( object );
|
||||
return logger->attach();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
ConsoleMethod( ConsoleLogger, detach, bool, 2, 2, "() Detaches this object from the console and stops logging" )
|
||||
{
|
||||
ConsoleLogger *logger = static_cast<ConsoleLogger *>( object );
|
||||
return logger->detach();
|
||||
}
|
93
engine/console/consoleLogger.h
Executable file
93
engine/console/consoleLogger.h
Executable file
@ -0,0 +1,93 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "console/simBase.h"
|
||||
#include "console/console.h"
|
||||
#include "core/fileStream.h"
|
||||
|
||||
#ifndef _CONSOLE_LOGGER_H_
|
||||
#define _CONSOLE_LOGGER_H_
|
||||
|
||||
/// A class designed to be used as a console consumer and log
|
||||
/// the data it receives to a file.
|
||||
class ConsoleLogger : public SimObject
|
||||
{
|
||||
typedef SimObject Parent;
|
||||
|
||||
private:
|
||||
bool mLogging; ///< True if it is currently consuming and logging
|
||||
FileStream mStream; ///< File stream this object writes to
|
||||
static bool smInitialized; ///< This is for use with the default constructor
|
||||
bool mAppend; ///< If false, it will clear the file before logging to it.
|
||||
StringTableEntry mFilename; ///< The file name to log to.
|
||||
|
||||
/// List of active ConsoleLoggers to send log messages to
|
||||
static Vector<ConsoleLogger *> mActiveLoggers;
|
||||
|
||||
/// The log function called by the consumer callback
|
||||
/// @param consoleLine Line of text to log
|
||||
void log( const char *consoleLine );
|
||||
|
||||
/// Utility function, sets up the object (for script interface) returns true if successful
|
||||
bool init();
|
||||
|
||||
public:
|
||||
|
||||
// @name Public console variables
|
||||
/// @{
|
||||
ConsoleLogEntry::Level mLevel; ///< The level of log messages to log
|
||||
/// @}
|
||||
|
||||
DECLARE_CONOBJECT( ConsoleLogger );
|
||||
|
||||
static void initPersistFields();
|
||||
|
||||
/// Console constructor
|
||||
///
|
||||
/// @code
|
||||
/// // Example script constructor usage.
|
||||
/// %obj = new ConsoleLogger( objName, logFileName, [append = false] );
|
||||
/// @endcode
|
||||
bool processArguments( S32 argc, const char **argv );
|
||||
|
||||
/// Default constructor, make sure to initalize
|
||||
ConsoleLogger();
|
||||
|
||||
/// Constructor
|
||||
/// @param fileName File name to log to
|
||||
/// @param append If false, it will clear the file, then start logging, else it will append
|
||||
ConsoleLogger( const char *fileName, bool append = false );
|
||||
|
||||
/// Destructor
|
||||
~ConsoleLogger();
|
||||
|
||||
/// Attach to the console and begin logging
|
||||
///
|
||||
/// Returns true if the action is successful
|
||||
bool attach();
|
||||
|
||||
/// Detach from the console and stop logging
|
||||
///
|
||||
/// Returns true if the action is successful
|
||||
bool detach();
|
||||
|
||||
/// Sets the level of console messages to log.
|
||||
///
|
||||
/// @param level Log level. Only items of the specified level or
|
||||
/// lower are logged.
|
||||
/// @see ConsoleLogEntry::Level
|
||||
void setLogLevel( ConsoleLogEntry::Level level );
|
||||
|
||||
/// Returns the level of console messages to log
|
||||
ConsoleLogEntry::Level getLogLevel() const;
|
||||
|
||||
/// The callback for the console consumer
|
||||
///
|
||||
/// @note This is a global callback, not executed per-instance.
|
||||
/// @see Con::addConsumer
|
||||
static void logCallback( ConsoleLogEntry::Level level, const char *consoleLine );
|
||||
};
|
||||
|
||||
#endif
|
348
engine/console/consoleObject.cc
Executable file
348
engine/console/consoleObject.cc
Executable file
@ -0,0 +1,348 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "console/consoleObject.h"
|
||||
#include "core/stringTable.h"
|
||||
#include "core/crc.h"
|
||||
#include "console/console.h"
|
||||
#include "console/consoleInternal.h"
|
||||
#include "console/typeValidators.h"
|
||||
|
||||
AbstractClassRep * AbstractClassRep::classLinkList = NULL;
|
||||
static AbstractClassRep::FieldList sg_tempFieldList;
|
||||
U32 AbstractClassRep::NetClassCount [NetClassGroupsCount][NetClassTypesCount] = {{0, },};
|
||||
U32 AbstractClassRep::NetClassBitSize[NetClassGroupsCount][NetClassTypesCount] = {{0, },};
|
||||
|
||||
AbstractClassRep ** AbstractClassRep::classTable[NetClassGroupsCount][NetClassTypesCount];
|
||||
|
||||
U32 AbstractClassRep::classCRC[NetClassGroupsCount] = {INITIAL_CRC_VALUE, };
|
||||
bool AbstractClassRep::initialized = false;
|
||||
|
||||
//--------------------------------------
|
||||
const AbstractClassRep::Field *AbstractClassRep::findField(StringTableEntry name) const
|
||||
{
|
||||
for(U32 i = 0; i < mFieldList.size(); i++)
|
||||
if(mFieldList[i].pFieldname == name)
|
||||
return &mFieldList[i];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
void AbstractClassRep::registerClassRep(AbstractClassRep* in_pRep)
|
||||
{
|
||||
AssertFatal(in_pRep != NULL, "AbstractClassRep::registerClassRep was passed a NULL pointer!");
|
||||
|
||||
#ifdef TORQUE_DEBUG // assert if this class is already registered.
|
||||
for(AbstractClassRep *walk = classLinkList; walk; walk = walk->nextClass)
|
||||
{
|
||||
AssertFatal(dStrcmp(in_pRep->mClassName, walk->mClassName),
|
||||
"Duplicate class name registered in AbstractClassRep::registerClassRep()");
|
||||
}
|
||||
#endif
|
||||
|
||||
in_pRep->nextClass = classLinkList;
|
||||
classLinkList = in_pRep;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
|
||||
ConsoleObject* AbstractClassRep::create(const char* in_pClassName)
|
||||
{
|
||||
AssertFatal(initialized,
|
||||
"AbstractClassRep::create() - Tried to create an object before AbstractClassRep::initialize().");
|
||||
|
||||
for (AbstractClassRep *walk = classLinkList; walk; walk = walk->nextClass)
|
||||
if (!dStrcmp(walk->getClassName(), in_pClassName))
|
||||
return walk->create();
|
||||
|
||||
AssertWarn(0, avar("Couldn't find class rep for dynamic class: %s", in_pClassName));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
ConsoleObject* AbstractClassRep::create(const U32 groupId, const U32 typeId, const U32 in_classId)
|
||||
{
|
||||
AssertFatal(initialized,
|
||||
"AbstractClassRep::create() - Tried to create an object before AbstractClassRep::initialize().");
|
||||
AssertFatal(in_classId < NetClassCount[groupId][typeId],
|
||||
"AbstractClassRep::create() - Class id out of range.");
|
||||
AssertFatal(classTable[groupId][typeId][in_classId] != NULL,
|
||||
"AbstractClassRep::create() - No class with requested ID type.");
|
||||
|
||||
// Look up the specified class and create it.
|
||||
if(classTable[groupId][typeId][in_classId])
|
||||
return classTable[groupId][typeId][in_classId]->create();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
|
||||
static S32 QSORT_CALLBACK ACRCompare(const void *aptr, const void *bptr)
|
||||
{
|
||||
const AbstractClassRep *a = *((const AbstractClassRep **) aptr);
|
||||
const AbstractClassRep *b = *((const AbstractClassRep **) bptr);
|
||||
|
||||
if(a->mClassType != b->mClassType)
|
||||
return a->mClassType - b->mClassType;
|
||||
return dStrcmp(a->getClassName(), b->getClassName());
|
||||
}
|
||||
|
||||
void AbstractClassRep::initialize()
|
||||
{
|
||||
AssertFatal(!initialized, "Duplicate call to AbstractClassRep::initialize()!");
|
||||
Vector<AbstractClassRep *> dynamicTable(__FILE__, __LINE__);
|
||||
|
||||
AbstractClassRep *walk;
|
||||
|
||||
// Initialize namespace references...
|
||||
for (walk = classLinkList; walk; walk = walk->nextClass)
|
||||
{
|
||||
walk->mNamespace = Con::lookupNamespace(StringTable->insert(walk->getClassName()));
|
||||
walk->mNamespace->mClassRep = walk;
|
||||
}
|
||||
|
||||
// Initialize field lists... (and perform other console registration).
|
||||
for (walk = classLinkList; walk; walk = walk->nextClass)
|
||||
{
|
||||
// sg_tempFieldList is used as a staging area for field lists
|
||||
// (see addField, addGroup, etc.)
|
||||
sg_tempFieldList.setSize(0);
|
||||
|
||||
walk->init();
|
||||
|
||||
// So if we have things in it, copy it over...
|
||||
if (sg_tempFieldList.size() != 0)
|
||||
walk->mFieldList = sg_tempFieldList;
|
||||
|
||||
// And of course delete it every round.
|
||||
sg_tempFieldList.clear();
|
||||
}
|
||||
|
||||
// Calculate counts and bit sizes for the various NetClasses.
|
||||
for (U32 group = 0; group < NetClassGroupsCount; group++)
|
||||
{
|
||||
U32 groupMask = 1 << group;
|
||||
|
||||
// Specifically, for each NetClass of each NetGroup...
|
||||
for(U32 type = 0; type < NetClassTypesCount; type++)
|
||||
{
|
||||
// Go through all the classes and find matches...
|
||||
for (walk = classLinkList; walk; walk = walk->nextClass)
|
||||
{
|
||||
if(walk->mClassType == type && walk->mClassGroupMask & groupMask)
|
||||
dynamicTable.push_back(walk);
|
||||
}
|
||||
|
||||
// Set the count for this NetGroup and NetClass
|
||||
NetClassCount[group][type] = dynamicTable.size();
|
||||
if(!NetClassCount[group][type])
|
||||
continue; // If no classes matched, skip to next.
|
||||
|
||||
// Sort by type and then by name.
|
||||
dQsort((void *) &dynamicTable[0], dynamicTable.size(), sizeof(AbstractClassRep *), ACRCompare);
|
||||
|
||||
// Allocate storage in the classTable
|
||||
classTable[group][type] = new AbstractClassRep*[NetClassCount[group][type]];
|
||||
|
||||
// Fill this in and assign class ids for this group.
|
||||
for(U32 i = 0; i < NetClassCount[group][type];i++)
|
||||
{
|
||||
classTable[group][type][i] = dynamicTable[i];
|
||||
dynamicTable[i]->mClassId[group] = i;
|
||||
}
|
||||
|
||||
// And calculate the size of bitfields for this group and type.
|
||||
NetClassBitSize[group][type] =
|
||||
getBinLog2(getNextPow2(NetClassCount[group][type] + 1));
|
||||
|
||||
dynamicTable.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Ok, we're golden!
|
||||
initialized = true;
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//-------------------------------------- ConsoleObject
|
||||
|
||||
char replacebuf[1024];
|
||||
char* suppressSpaces(const char* in_pname)
|
||||
{
|
||||
U32 i = 0;
|
||||
char chr;
|
||||
do
|
||||
{
|
||||
chr = in_pname[i];
|
||||
replacebuf[i++] = (chr != 32) ? chr : '_';
|
||||
} while(chr);
|
||||
|
||||
return replacebuf;
|
||||
}
|
||||
|
||||
void ConsoleObject::addGroup(const char* in_pGroupname, const char* in_pGroupDocs)
|
||||
{
|
||||
// Remove spaces.
|
||||
char* pFieldNameBuf = suppressSpaces(in_pGroupname);
|
||||
|
||||
// Append group type to fieldname.
|
||||
dStrcat(pFieldNameBuf, "_begingroup");
|
||||
|
||||
// Create Field.
|
||||
AbstractClassRep::Field f;
|
||||
f.pFieldname = StringTable->insert(pFieldNameBuf);
|
||||
f.pGroupname = StringTable->insert(in_pGroupname);
|
||||
|
||||
if(in_pGroupDocs)
|
||||
f.pFieldDocs = StringTable->insert(in_pGroupDocs);
|
||||
else
|
||||
f.pFieldDocs = NULL;
|
||||
|
||||
f.type = AbstractClassRep::StartGroupFieldType;
|
||||
f.elementCount = 0;
|
||||
f.groupExpand = false;
|
||||
f.validator = NULL;
|
||||
|
||||
// Add to field list.
|
||||
sg_tempFieldList.push_back(f);
|
||||
}
|
||||
|
||||
void ConsoleObject::endGroup(const char* in_pGroupname)
|
||||
{
|
||||
// Remove spaces.
|
||||
char* pFieldNameBuf = suppressSpaces(in_pGroupname);
|
||||
|
||||
// Append group type to fieldname.
|
||||
dStrcat(pFieldNameBuf, "_endgroup");
|
||||
|
||||
// Create Field.
|
||||
AbstractClassRep::Field f;
|
||||
f.pFieldname = StringTable->insert(pFieldNameBuf);
|
||||
f.pGroupname = StringTable->insert(in_pGroupname);
|
||||
f.pFieldDocs = NULL;
|
||||
f.type = AbstractClassRep::EndGroupFieldType;
|
||||
f.groupExpand = false;
|
||||
f.validator = NULL;
|
||||
f.elementCount = 0;
|
||||
|
||||
// Add to field list.
|
||||
sg_tempFieldList.push_back(f);
|
||||
}
|
||||
|
||||
void ConsoleObject::addField(const char* in_pFieldname,
|
||||
const U32 in_fieldType,
|
||||
const dsize_t in_fieldOffset,
|
||||
const char* in_pFieldDocs)
|
||||
{
|
||||
addField(
|
||||
in_pFieldname,
|
||||
in_fieldType,
|
||||
in_fieldOffset,
|
||||
1,
|
||||
NULL,
|
||||
in_pFieldDocs);
|
||||
}
|
||||
|
||||
void ConsoleObject::addField(const char* in_pFieldname,
|
||||
const U32 in_fieldType,
|
||||
const dsize_t in_fieldOffset,
|
||||
const U32 in_elementCount,
|
||||
EnumTable *in_table,
|
||||
const char* in_pFieldDocs)
|
||||
{
|
||||
AbstractClassRep::Field f;
|
||||
f.pFieldname = StringTable->insert(in_pFieldname);
|
||||
f.pGroupname = NULL;
|
||||
|
||||
if(in_pFieldDocs)
|
||||
f.pFieldDocs = StringTable->insert(in_pFieldDocs);
|
||||
else
|
||||
f.pFieldDocs = NULL;
|
||||
|
||||
f.type = in_fieldType;
|
||||
f.offset = in_fieldOffset;
|
||||
f.elementCount = in_elementCount;
|
||||
f.table = in_table;
|
||||
f.validator = NULL;
|
||||
|
||||
sg_tempFieldList.push_back(f);
|
||||
}
|
||||
|
||||
void ConsoleObject::addFieldV(const char* in_pFieldname,
|
||||
const U32 in_fieldType,
|
||||
const dsize_t in_fieldOffset,
|
||||
TypeValidator *v,
|
||||
const char* in_pFieldDocs)
|
||||
{
|
||||
AbstractClassRep::Field f;
|
||||
f.pFieldname = StringTable->insert(in_pFieldname);
|
||||
f.pGroupname = NULL;
|
||||
if(in_pFieldDocs)
|
||||
f.pFieldDocs = StringTable->insert(in_pFieldDocs);
|
||||
else
|
||||
f.pFieldDocs = NULL;
|
||||
f.type = in_fieldType;
|
||||
f.offset = in_fieldOffset;
|
||||
f.elementCount = 1;
|
||||
f.table = NULL;
|
||||
f.validator = v;
|
||||
v->fieldIndex = sg_tempFieldList.size();
|
||||
|
||||
sg_tempFieldList.push_back(f);
|
||||
}
|
||||
|
||||
void ConsoleObject::addDepricatedField(const char *fieldName)
|
||||
{
|
||||
AbstractClassRep::Field f;
|
||||
f.pFieldname = StringTable->insert(fieldName);
|
||||
f.pGroupname = NULL;
|
||||
f.pFieldDocs = NULL;
|
||||
f.type = AbstractClassRep::DepricatedFieldType;
|
||||
f.offset = 0;
|
||||
f.elementCount = 0;
|
||||
f.table = NULL;
|
||||
f.validator = NULL;
|
||||
|
||||
sg_tempFieldList.push_back(f);
|
||||
}
|
||||
|
||||
|
||||
bool ConsoleObject::removeField(const char* in_pFieldname)
|
||||
{
|
||||
for (U32 i = 0; i < sg_tempFieldList.size(); i++) {
|
||||
if (dStricmp(in_pFieldname, sg_tempFieldList[i].pFieldname) == 0) {
|
||||
sg_tempFieldList.erase(i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
void ConsoleObject::initPersistFields()
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
void ConsoleObject::consoleInit()
|
||||
{
|
||||
}
|
||||
|
||||
ConsoleObject::~ConsoleObject()
|
||||
{
|
||||
}
|
||||
|
||||
//--------------------------------------
|
||||
AbstractClassRep* ConsoleObject::getClassRep() const
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
680
engine/console/consoleObject.h
Executable file
680
engine/console/consoleObject.h
Executable file
@ -0,0 +1,680 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _CONSOLEOBJECT_H_
|
||||
#define _CONSOLEOBJECT_H_
|
||||
|
||||
//Includes
|
||||
#ifndef _PLATFORM_H_
|
||||
#include "platform/platform.h"
|
||||
#endif
|
||||
#ifndef _TVECTOR_H_
|
||||
#include "core/tVector.h"
|
||||
#endif
|
||||
#ifndef _STRINGTABLE_H_
|
||||
#include "core/stringTable.h"
|
||||
#endif
|
||||
#ifndef _BITSET_H_
|
||||
#include "core/bitSet.h"
|
||||
#endif
|
||||
#ifndef _CONSOLE_H_
|
||||
#include "console/console.h"
|
||||
#endif
|
||||
|
||||
class Namespace;
|
||||
|
||||
enum NetClassTypes {
|
||||
NetClassTypeObject = 0,
|
||||
NetClassTypeDataBlock,
|
||||
NetClassTypeEvent,
|
||||
NetClassTypesCount,
|
||||
};
|
||||
|
||||
enum NetClassGroups {
|
||||
NetClassGroupGame = 0,
|
||||
NetClassGroupCommunity,
|
||||
NetClassGroup3,
|
||||
NetClassGroup4,
|
||||
NetClassGroupsCount,
|
||||
};
|
||||
|
||||
enum NetClassMasks {
|
||||
NetClassGroupGameMask = BIT(NetClassGroupGame),
|
||||
NetClassGroupCommunityMask = BIT(NetClassGroupCommunity),
|
||||
};
|
||||
|
||||
enum NetDirection
|
||||
{
|
||||
NetEventDirAny,
|
||||
NetEventDirServerToClient,
|
||||
NetEventDirClientToServer,
|
||||
};
|
||||
|
||||
class SimObject;
|
||||
class TypeValidator;
|
||||
|
||||
/// Core functionality for class manipulation.
|
||||
///
|
||||
/// @section AbstractClassRep_intro Introduction (or, Why AbstractClassRep?)
|
||||
///
|
||||
/// Many of Torque's subsystems, especially network, console, and sim,
|
||||
/// require the ability to programatically instantiate classes. For instance,
|
||||
/// when objects are ghosted, the networking layer needs to be able to create
|
||||
/// an instance of the object on the client. When the console scripting
|
||||
/// language runtime encounters the "new" keyword, it has to be able to fill
|
||||
/// that request.
|
||||
///
|
||||
/// Since standard C++ doesn't provide a function to create a new instance of
|
||||
/// an arbitrary class at runtime, one must be created. This is what
|
||||
/// AbstractClassRep and ConcreteClassRep are all about. They allow the registration
|
||||
/// and instantiation of arbitrary classes at runtime.
|
||||
///
|
||||
/// In addition, ACR keeps track of the fields (registered via addField() and co.) of
|
||||
/// a class, allowing programmatic access of class fields.
|
||||
///
|
||||
/// @see ConsoleObject
|
||||
///
|
||||
/// @note In general, you will only access the functionality implemented in this class via
|
||||
/// ConsoleObject::create(). Most of the time, you will only ever need to use this part
|
||||
/// part of the engine indirectly - ie, you will use the networking system or the console,
|
||||
/// or ConsoleObject, and they will indirectly use this code. <b>The following discussion
|
||||
/// is really only relevant for advanced engine users.</b>
|
||||
///
|
||||
/// @section AbstractClassRep_netstuff NetClasses and Class IDs
|
||||
///
|
||||
/// Torque supports a notion of group, type, and direction for objects passed over
|
||||
/// the network. Class IDs are assigned sequentially per-group, per-type, so that, for instance,
|
||||
/// the IDs assigned to Datablocks are seperate from the IDs assigned to NetObjects or NetEvents.
|
||||
/// This can translate into significant bandwidth savings (especially since the size of the fields
|
||||
/// for transmitting these bits are determined at run-time based on the number of IDs given out.
|
||||
///
|
||||
/// @section AbstractClassRep_details AbstractClassRep Internals
|
||||
///
|
||||
/// Much like ConsoleConstructor, ACR does some preparatory work at runtime before execution
|
||||
/// is passed to main(). In actual fact, this preparatory work is done by the ConcreteClassRep
|
||||
/// template. Let's examine this more closely.
|
||||
///
|
||||
/// If we examine ConsoleObject, we see that two macros must be used in the definition of a
|
||||
/// properly integrated objects. From the ConsoleObject example:
|
||||
///
|
||||
/// @code
|
||||
/// // This is from inside the class definition...
|
||||
/// DECLARE_CONOBJECT(TorqueObject);
|
||||
///
|
||||
/// // And this is from outside the class definition...
|
||||
/// IMPLEMENT_CONOBJECT(TorqueObject);
|
||||
/// @endcode
|
||||
///
|
||||
/// What do these things actually do?
|
||||
///
|
||||
/// Not all that much, in fact. They expand to code something like this:
|
||||
///
|
||||
/// @code
|
||||
/// // This is from inside the class definition...
|
||||
/// static ConcreteClassRep<TorqueObject> dynClassRep;
|
||||
/// static AbstractClassRep* getParentStaticClassRep();
|
||||
/// static AbstractClassRep* getStaticClassRep();
|
||||
/// virtual AbstractClassRep* getClassRep() const;
|
||||
/// @endcode
|
||||
///
|
||||
/// @code
|
||||
/// // And this is from outside the class definition...
|
||||
/// AbstractClassRep* TorqueObject::getClassRep() const { return &TorqueObject::dynClassRep; }
|
||||
/// AbstractClassRep* TorqueObject::getStaticClassRep() { return &dynClassRep; }
|
||||
/// AbstractClassRep* TorqueObject::getParentStaticClassRep() { return Parent::getStaticClassRep(); }
|
||||
/// ConcreteClassRep<TorqueObject> TorqueObject::dynClassRep("TorqueObject", 0, -1, 0);
|
||||
/// @endcode
|
||||
///
|
||||
/// As you can see, getClassRep(), getStaticClassRep(), and getParentStaticClassRep() are just
|
||||
/// accessors to allow access to various ConcreteClassRep instances. This is where the Parent
|
||||
/// typedef comes into play as well - it lets getParentStaticClassRep() get the right
|
||||
/// class rep.
|
||||
///
|
||||
/// In addition, dynClassRep is declared as a member of TorqueObject, and defined later
|
||||
/// on. Much like ConsoleConstructor, ConcreteClassReps add themselves to a global linked
|
||||
/// list in their constructor.
|
||||
///
|
||||
/// Then, when AbstractClassRep::initialize() is called, from Con::init(), we iterate through
|
||||
/// the list and perform the following tasks:
|
||||
/// - Sets up a Namespace for each class.
|
||||
/// - Call the init() method on each ConcreteClassRep. This method:
|
||||
/// - Links namespaces between parent and child classes, using Con::classLinkNamespaces.
|
||||
/// - Calls initPersistFields() and consoleInit().
|
||||
/// - As a result of calling initPersistFields, the field list for the class is populated.
|
||||
/// - Assigns network IDs for classes based on their NetGroup membership. Determines
|
||||
/// bit allocations for network ID fields.
|
||||
///
|
||||
/// @nosubgrouping
|
||||
class AbstractClassRep
|
||||
{
|
||||
friend class ConsoleObject;
|
||||
|
||||
public:
|
||||
|
||||
/// @name 'Tructors
|
||||
/// @{
|
||||
|
||||
AbstractClassRep()
|
||||
{
|
||||
VECTOR_SET_ASSOCIATION(mFieldList);
|
||||
parentClass = NULL;
|
||||
}
|
||||
virtual ~AbstractClassRep() { }
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Representation Interface
|
||||
/// @{
|
||||
|
||||
S32 mClassGroupMask; ///< Mask indicating in which NetGroups this object belongs.
|
||||
S32 mClassType; ///< Stores the NetClass of this class.
|
||||
S32 mNetEventDir; ///< Stores the NetDirection of this class.
|
||||
S32 mClassId[NetClassGroupsCount]; ///< Stores the IDs assigned to this class for each group.
|
||||
|
||||
S32 getClassId (U32 netClassGroup) const;
|
||||
static U32 getClassCRC (U32 netClassGroup);
|
||||
const char* getClassName() const;
|
||||
static AbstractClassRep* getClassList();
|
||||
Namespace* getNameSpace();
|
||||
AbstractClassRep* getNextClass();
|
||||
AbstractClassRep* getParentClass();
|
||||
|
||||
/// Helper class to see if we are a given class, or a subclass thereof.
|
||||
bool isClass(AbstractClassRep *acr)
|
||||
{
|
||||
AbstractClassRep *walk = this;
|
||||
|
||||
// Walk up parents, checking for equivalence.
|
||||
while(walk)
|
||||
{
|
||||
if(walk == acr)
|
||||
return true;
|
||||
|
||||
walk = walk->parentClass;
|
||||
};
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual ConsoleObject* create () const = 0;
|
||||
|
||||
protected:
|
||||
virtual void init() const = 0;
|
||||
|
||||
const char * mClassName;
|
||||
AbstractClassRep * nextClass;
|
||||
AbstractClassRep * parentClass;
|
||||
Namespace * mNamespace;
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
/// @name Fields
|
||||
/// @{
|
||||
public:
|
||||
enum ACRFieldTypes
|
||||
{
|
||||
StartGroupFieldType = 0xFFFFFFFD,
|
||||
EndGroupFieldType = 0xFFFFFFFE,
|
||||
DepricatedFieldType = 0xFFFFFFFF
|
||||
};
|
||||
|
||||
struct Field {
|
||||
const char* pFieldname; ///< Name of the field.
|
||||
const char* pGroupname; ///< Optionally filled field containing the group name.
|
||||
///
|
||||
/// This is filled when type is StartField or EndField
|
||||
|
||||
const char* pFieldDocs; ///< Documentation about this field; see consoleDoc.cc.
|
||||
bool groupExpand; ///< Flag to track expanded/not state of this group in the editor.
|
||||
U32 type; ///< A type ID. @see ACRFieldTypes
|
||||
U32 offset; ///< Memory offset from beginning of class for this field.
|
||||
S32 elementCount; ///< Number of elements, if this is an array.
|
||||
EnumTable * table; ///< If this is an enum, this points to the table defining it.
|
||||
BitSet32 flag; ///< Stores various flags
|
||||
TypeValidator *validator; ///< Validator, if any.
|
||||
};
|
||||
typedef Vector<Field> FieldList;
|
||||
|
||||
FieldList mFieldList;
|
||||
|
||||
bool mDynamicGroupExpand;
|
||||
|
||||
const Field *findField(StringTableEntry fieldName) const;
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Abstract Class Database
|
||||
/// @{
|
||||
|
||||
protected:
|
||||
static AbstractClassRep ** classTable[NetClassGroupsCount][NetClassTypesCount];
|
||||
static AbstractClassRep * classLinkList;
|
||||
static U32 classCRC[NetClassGroupsCount];
|
||||
static bool initialized;
|
||||
|
||||
static ConsoleObject* create(const char* in_pClassName);
|
||||
static ConsoleObject* create(const U32 groupId, const U32 typeId, const U32 in_classId);
|
||||
|
||||
public:
|
||||
static U32 NetClassCount [NetClassGroupsCount][NetClassTypesCount];
|
||||
static U32 NetClassBitSize[NetClassGroupsCount][NetClassTypesCount];
|
||||
|
||||
static void registerClassRep(AbstractClassRep*);
|
||||
static void initialize(); // Called from Con::init once on startup
|
||||
|
||||
|
||||
/// @}
|
||||
};
|
||||
|
||||
inline AbstractClassRep *AbstractClassRep::getClassList()
|
||||
{
|
||||
return classLinkList;
|
||||
}
|
||||
|
||||
inline U32 AbstractClassRep::getClassCRC(U32 group)
|
||||
{
|
||||
return classCRC[group];
|
||||
}
|
||||
|
||||
inline AbstractClassRep *AbstractClassRep::getNextClass()
|
||||
{
|
||||
return nextClass;
|
||||
}
|
||||
|
||||
inline AbstractClassRep *AbstractClassRep::getParentClass()
|
||||
{
|
||||
return parentClass;
|
||||
}
|
||||
|
||||
inline S32 AbstractClassRep::getClassId(U32 group) const
|
||||
{
|
||||
return mClassId[group];
|
||||
}
|
||||
|
||||
inline const char* AbstractClassRep::getClassName() const
|
||||
{
|
||||
return mClassName;
|
||||
}
|
||||
|
||||
inline Namespace *AbstractClassRep::getNameSpace()
|
||||
{
|
||||
return mNamespace;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//-------------------------------------- ConcreteClassRep
|
||||
//
|
||||
|
||||
|
||||
/// Helper class for AbstractClassRep.
|
||||
///
|
||||
/// @see AbtractClassRep
|
||||
/// @see ConsoleObject
|
||||
template <class T>
|
||||
class ConcreteClassRep : public AbstractClassRep
|
||||
{
|
||||
public:
|
||||
ConcreteClassRep(const char *name, S32 netClassGroupMask, S32 netClassType, S32 netEventDir, AbstractClassRep *parent)
|
||||
{
|
||||
// name is a static compiler string so no need to worry about copying or deleting
|
||||
mClassName = name;
|
||||
|
||||
// Clean up mClassId
|
||||
for(U32 i = 0; i < NetClassGroupsCount; i++)
|
||||
mClassId[i] = -1;
|
||||
|
||||
// Set properties for this ACR
|
||||
mClassType = netClassType;
|
||||
mClassGroupMask = netClassGroupMask;
|
||||
mNetEventDir = netEventDir;
|
||||
parentClass = parent;
|
||||
|
||||
// Finally, register ourselves.
|
||||
registerClassRep(this);
|
||||
};
|
||||
|
||||
/// Perform class specific initialization tasks.
|
||||
///
|
||||
/// Link namespaces, call initPersistFields() and consoleInit().
|
||||
void init() const
|
||||
{
|
||||
// Get handle to our parent class, if any, and ourselves (we are our parent's child).
|
||||
AbstractClassRep *parent = T::getParentStaticClassRep();
|
||||
AbstractClassRep *child = T::getStaticClassRep ();
|
||||
|
||||
// If we got reps, then link those namespaces! (To get proper inheritance.)
|
||||
if(parent && child)
|
||||
Con::classLinkNamespaces(parent->getNameSpace(), child->getNameSpace());
|
||||
|
||||
// Finally, do any class specific initialization...
|
||||
T::initPersistFields();
|
||||
T::consoleInit();
|
||||
}
|
||||
|
||||
/// Wrap constructor.
|
||||
ConsoleObject* create() const { return new T; }
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/// Interface class to the console.
|
||||
///
|
||||
/// @section ConsoleObject_basics The Basics
|
||||
///
|
||||
/// Any object which you want to work with the console system should derive from this,
|
||||
/// and access functionality through the static interface.
|
||||
///
|
||||
/// This class is always used with the DECLARE_CONOBJECT and IMPLEMENT_* macros.
|
||||
///
|
||||
/// @code
|
||||
/// // A very basic example object. It will do nothing!
|
||||
/// class TorqueObject : public ConsoleObject {
|
||||
/// // Must provide a Parent typedef so the console system knows what we inherit from.
|
||||
/// typedef ConsoleObject Parent;
|
||||
///
|
||||
/// // This does a lot of menial declaration for you.
|
||||
/// DECLARE_CONOBJECT(TorqueObject);
|
||||
///
|
||||
/// // This is for us to register our fields in.
|
||||
/// static void initPersistFields();
|
||||
///
|
||||
/// // A sample field.
|
||||
/// S8 mSample;
|
||||
/// }
|
||||
/// @endcode
|
||||
///
|
||||
/// @code
|
||||
/// // And the accordant implementation...
|
||||
/// IMPLEMENT_CONOBJECT(TorqueObject);
|
||||
///
|
||||
/// void TorqueObject::initPersistFields()
|
||||
/// {
|
||||
/// // If you want to inherit any fields from the parent (you do), do this:
|
||||
/// Parent::initPersistFields();
|
||||
///
|
||||
/// // Pass the field, the type, the offset, and a usage string.
|
||||
/// addField("sample", TypeS8, Offset(mSample, TorqueObject), "A test field.");
|
||||
/// }
|
||||
/// @endcode
|
||||
///
|
||||
/// That's all you need to do to get a class registered with the console system. At this point,
|
||||
/// you can instantiate it via script, tie methods to it using ConsoleMethod, register fields,
|
||||
/// and so forth. You can also register any global variables related to the class by creating
|
||||
/// a consoleInit() method.
|
||||
///
|
||||
/// You will need to use different IMPLEMENT_ macros in different cases; for instance, if you
|
||||
/// are making a NetObject (for ghosting), a DataBlock, or a NetEvent.
|
||||
///
|
||||
/// @see AbstractClassRep for gory implementation details.
|
||||
/// @nosubgrouping
|
||||
class ConsoleObject
|
||||
{
|
||||
protected:
|
||||
/// @deprecated This is disallowed.
|
||||
ConsoleObject() { /* disallowed */ }
|
||||
/// @deprecated This is disallowed.
|
||||
ConsoleObject(const ConsoleObject&);
|
||||
|
||||
protected:
|
||||
/// Get a reference to a field by name.
|
||||
const AbstractClassRep::Field *findField(StringTableEntry fieldName) const;
|
||||
|
||||
public:
|
||||
|
||||
/// Gets the ClassRep.
|
||||
virtual AbstractClassRep* getClassRep() const;
|
||||
|
||||
/// Set the value of a field.
|
||||
bool setField(const char *fieldName, const char *value);
|
||||
virtual ~ConsoleObject();
|
||||
|
||||
public:
|
||||
/// @name Object Creation
|
||||
/// @{
|
||||
static ConsoleObject* create(const char* in_pClassName);
|
||||
static ConsoleObject* create(const U32 groupId, const U32 typeId, const U32 in_classId);
|
||||
/// @}
|
||||
|
||||
public:
|
||||
/// Get the classname from a class tag.
|
||||
static const char* lookupClassName(const U32 in_classTag);
|
||||
|
||||
protected:
|
||||
/// @name Fields
|
||||
/// @{
|
||||
|
||||
/// Mark the beginning of a group of fields.
|
||||
///
|
||||
/// This is used in the consoleDoc system.
|
||||
/// @see console_autodoc
|
||||
static void addGroup(const char* in_pGroupname, const char* in_pGroupDocs = NULL);
|
||||
|
||||
/// Mark the end of a group of fields.
|
||||
///
|
||||
/// This is used in the consoleDoc system.
|
||||
/// @see console_autodoc
|
||||
static void endGroup(const char* in_pGroupname);
|
||||
|
||||
/// Register a complex field.
|
||||
///
|
||||
/// @param in_pFieldname Name of the field.
|
||||
/// @param in_fieldType Type of the field. @see ConsoleDynamicTypes
|
||||
/// @param in_fieldOffset Offset to the field from the start of the class; calculated using the Offset() macro.
|
||||
/// @param in_elementCount Number of elements in this field. Arrays of elements are assumed to be contiguous in memory.
|
||||
/// @param in_table An EnumTable, if this is an enumerated field.
|
||||
/// @param in_pFieldDocs Usage string for this field. @see console_autodoc
|
||||
static void addField(const char* in_pFieldname,
|
||||
const U32 in_fieldType,
|
||||
const dsize_t in_fieldOffset,
|
||||
const U32 in_elementCount = 1,
|
||||
EnumTable * in_table = NULL,
|
||||
const char* in_pFieldDocs = NULL);
|
||||
|
||||
/// Register a simple field.
|
||||
///
|
||||
/// @param in_pFieldname Name of the field.
|
||||
/// @param in_fieldType Type of the field. @see ConsoleDynamicTypes
|
||||
/// @param in_fieldOffset Offset to the field from the start of the class; calculated using the Offset() macro.
|
||||
/// @param in_pFieldDocs Usage string for this field. @see console_autodoc
|
||||
static void addField(const char* in_pFieldname,
|
||||
const U32 in_fieldType,
|
||||
const dsize_t in_fieldOffset,
|
||||
const char* in_pFieldDocs);
|
||||
|
||||
/// Register a validated field.
|
||||
///
|
||||
/// A validated field is just like a normal field except that you can't
|
||||
/// have it be an array, and that you give it a pointer to a TypeValidator
|
||||
/// subclass, which is then used to validate any value placed in it. Invalid
|
||||
/// values are ignored and an error is printed to the console.
|
||||
///
|
||||
/// @see addField
|
||||
/// @see typeValidators.h
|
||||
static void addFieldV(const char* in_pFieldname,
|
||||
const U32 in_fieldType,
|
||||
const dsize_t in_fieldOffset,
|
||||
TypeValidator *v,
|
||||
const char * in_pFieldDocs = NULL);
|
||||
|
||||
/// Add a deprecated field.
|
||||
///
|
||||
/// A deprecated field will always be undefined, even if you assign a value to it. This
|
||||
/// is useful when you need to make sure that a field is not being used anymore.
|
||||
static void addDepricatedField(const char *fieldName);
|
||||
|
||||
/// Remove a field.
|
||||
///
|
||||
/// Sometimes, you just have to remove a field!
|
||||
/// @returns True on success.
|
||||
static bool removeField(const char* in_pFieldname);
|
||||
|
||||
/// @}
|
||||
public:
|
||||
/// Register dynamic fields in a subclass of ConsoleObject.
|
||||
///
|
||||
/// @see addField(), addFieldV(), addDepricatedField(), addGroup(), endGroup()
|
||||
static void initPersistFields();
|
||||
|
||||
/// Register global constant variables and do other one-time initialization tasks in
|
||||
/// a subclass of ConsoleObject.
|
||||
///
|
||||
/// @deprecated You should use ConsoleMethod and ConsoleFunction, not this, to
|
||||
/// register methods or commands.
|
||||
/// @see console
|
||||
static void consoleInit();
|
||||
|
||||
/// @name Field List
|
||||
/// @{
|
||||
|
||||
/// Get a list of all the fields. This information cannot be modified.
|
||||
const AbstractClassRep::FieldList& getFieldList() const;
|
||||
|
||||
/// Get a list of all the fields, set up so we can modify them.
|
||||
///
|
||||
/// @note This is a bad trick to pull if you aren't very careful,
|
||||
/// since you can blast field data!
|
||||
AbstractClassRep::FieldList& getModifiableFieldList();
|
||||
|
||||
/// Get a handle to a boolean telling us if we expanded the dynamic group.
|
||||
///
|
||||
/// @see GuiInspector::Inspect()
|
||||
bool& getDynamicGroupExpand();
|
||||
/// @}
|
||||
|
||||
/// @name ConsoleObject Implementation
|
||||
///
|
||||
/// These functions are implemented in every subclass of
|
||||
/// ConsoleObject by an IMPLEMENT_CONOBJECT or IMPLEMENT_CO_* macro.
|
||||
/// @{
|
||||
|
||||
/// Get the abstract class information for this class.
|
||||
static AbstractClassRep *getStaticClassRep() { return NULL; }
|
||||
|
||||
/// Get the abstract class information for this class's superclass.
|
||||
static AbstractClassRep *getParentStaticClassRep() { return NULL; }
|
||||
|
||||
/// Get our network-layer class id.
|
||||
///
|
||||
/// @param netClassGroup The net class for which we want our ID.
|
||||
/// @see
|
||||
S32 getClassId(U32 netClassGroup) const;
|
||||
|
||||
/// Get our compiler and platform independent class name.
|
||||
///
|
||||
/// @note This name can be used to instantiate another instance using create()
|
||||
const char *getClassName() const;
|
||||
|
||||
/// @}
|
||||
};
|
||||
|
||||
#define addNamedField(fieldName,type,className) addField(#fieldName, type, Offset(fieldName,className))
|
||||
#define addNamedFieldV(fieldName,type,className, validator) addFieldV(#fieldName, type, Offset(fieldName,className), validator)
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//-------------------------------------- Inlines
|
||||
//
|
||||
inline S32 ConsoleObject::getClassId(U32 netClassGroup) const
|
||||
{
|
||||
AssertFatal(getClassRep() != NULL,"Cannot get tag from non-declared dynamic class!");
|
||||
return getClassRep()->getClassId(netClassGroup);
|
||||
}
|
||||
|
||||
inline const char * ConsoleObject::getClassName() const
|
||||
{
|
||||
AssertFatal(getClassRep() != NULL,
|
||||
"Cannot get tag from non-declared dynamic class");
|
||||
return getClassRep()->getClassName();
|
||||
}
|
||||
|
||||
inline const AbstractClassRep::Field * ConsoleObject::findField(StringTableEntry name) const
|
||||
{
|
||||
AssertFatal(getClassRep() != NULL,
|
||||
avar("Cannot get field '%s' from non-declared dynamic class.", name));
|
||||
return getClassRep()->findField(name);
|
||||
}
|
||||
|
||||
inline bool ConsoleObject::setField(const char *fieldName, const char *value)
|
||||
{
|
||||
//sanity check
|
||||
if ((! fieldName) || (! fieldName[0]) || (! value))
|
||||
return false;
|
||||
|
||||
if (! getClassRep())
|
||||
return false;
|
||||
|
||||
const AbstractClassRep::Field *myField = getClassRep()->findField(StringTable->insert(fieldName));
|
||||
|
||||
if (! myField)
|
||||
return false;
|
||||
|
||||
Con::setData(
|
||||
myField->type,
|
||||
(void *) (((const char *)(this)) + myField->offset),
|
||||
0,
|
||||
1,
|
||||
&value,
|
||||
myField->table,
|
||||
myField->flag);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline ConsoleObject* ConsoleObject::create(const char* in_pClassName)
|
||||
{
|
||||
return AbstractClassRep::create(in_pClassName);
|
||||
}
|
||||
|
||||
inline ConsoleObject* ConsoleObject::create(const U32 groupId, const U32 typeId, const U32 in_classId)
|
||||
{
|
||||
return AbstractClassRep::create(groupId, typeId, in_classId);
|
||||
}
|
||||
|
||||
inline const AbstractClassRep::FieldList& ConsoleObject::getFieldList() const
|
||||
{
|
||||
return getClassRep()->mFieldList;
|
||||
}
|
||||
|
||||
inline AbstractClassRep::FieldList& ConsoleObject::getModifiableFieldList()
|
||||
{
|
||||
return getClassRep()->mFieldList;
|
||||
}
|
||||
|
||||
inline bool& ConsoleObject::getDynamicGroupExpand()
|
||||
{
|
||||
return getClassRep()->mDynamicGroupExpand;
|
||||
}
|
||||
|
||||
/// @name ConsoleObject Macros
|
||||
/// @{
|
||||
|
||||
#define DECLARE_CONOBJECT(className) \
|
||||
static ConcreteClassRep<className> dynClassRep; \
|
||||
static AbstractClassRep* getParentStaticClassRep(); \
|
||||
static AbstractClassRep* getStaticClassRep(); \
|
||||
virtual AbstractClassRep* getClassRep() const
|
||||
|
||||
#define IMPLEMENT_CONOBJECT(className) \
|
||||
AbstractClassRep* className::getClassRep() const { return &className::dynClassRep; } \
|
||||
AbstractClassRep* className::getStaticClassRep() { return &dynClassRep; } \
|
||||
AbstractClassRep* className::getParentStaticClassRep() { return Parent::getStaticClassRep(); } \
|
||||
ConcreteClassRep<className> className::dynClassRep(#className, 0, -1, 0, className::getParentStaticClassRep())
|
||||
|
||||
#define IMPLEMENT_CO_NETOBJECT_V1(className) \
|
||||
AbstractClassRep* className::getClassRep() const { return &className::dynClassRep; } \
|
||||
AbstractClassRep* className::getStaticClassRep() { return &dynClassRep; } \
|
||||
AbstractClassRep* className::getParentStaticClassRep() { return Parent::getStaticClassRep(); } \
|
||||
ConcreteClassRep<className> className::dynClassRep(#className, NetClassGroupGameMask, NetClassTypeObject, 0, className::getParentStaticClassRep())
|
||||
|
||||
#define IMPLEMENT_CO_DATABLOCK_V1(className) \
|
||||
AbstractClassRep* className::getClassRep() const { return &className::dynClassRep; } \
|
||||
AbstractClassRep* className::getStaticClassRep() { return &dynClassRep; } \
|
||||
AbstractClassRep* className::getParentStaticClassRep() { return Parent::getStaticClassRep(); } \
|
||||
ConcreteClassRep<className> className::dynClassRep(#className, NetClassGroupGameMask, NetClassTypeDataBlock, 0, className::getParentStaticClassRep())
|
||||
|
||||
/// @}
|
||||
|
||||
#endif //_CONSOLEOBJECT_H_
|
76
engine/console/consoleParser.cc
Executable file
76
engine/console/consoleParser.cc
Executable file
@ -0,0 +1,76 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "console/console.h"
|
||||
#include "console/consoleParser.h"
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
|
||||
static ConsoleParser *gParserList = NULL;
|
||||
static ConsoleParser *gDefaultParser = NULL;
|
||||
|
||||
void freeConsoleParserList(void)
|
||||
{
|
||||
ConsoleParser *pParser;
|
||||
|
||||
while(pParser = gParserList)
|
||||
{
|
||||
gParserList = pParser->next;
|
||||
delete pParser;
|
||||
}
|
||||
|
||||
gDefaultParser = NULL;
|
||||
}
|
||||
|
||||
bool addConsoleParser(char *ext, fnGetCurrentFile gcf, fnGetCurrentLine gcl, fnParse p, fnRestart r, fnSetScanBuffer ssb, bool def /* = false */)
|
||||
{
|
||||
AssertFatal(ext && gcf && gcl && p && r, "AddConsoleParser called with one or more NULL arguments");
|
||||
|
||||
ConsoleParser *pParser;
|
||||
if(pParser = new ConsoleParser)
|
||||
{
|
||||
pParser->ext = ext;
|
||||
pParser->getCurrentFile = gcf;
|
||||
pParser->getCurrentLine = gcl;
|
||||
pParser->parse = p;
|
||||
pParser->restart = r;
|
||||
pParser->setScanBuffer = ssb;
|
||||
|
||||
if(def)
|
||||
gDefaultParser = pParser;
|
||||
|
||||
pParser->next = gParserList;
|
||||
gParserList = pParser;
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ConsoleParser * getParserForFile(const char *filename)
|
||||
{
|
||||
char *ptr;
|
||||
|
||||
if(filename == NULL)
|
||||
return gDefaultParser;
|
||||
|
||||
if(ptr = dStrrchr((char *)filename, '.'))
|
||||
{
|
||||
ptr++;
|
||||
|
||||
ConsoleParser *p;
|
||||
for(p = gParserList;p;p = p->next)
|
||||
{
|
||||
if(dStricmp(ptr, p->ext) == 0)
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
return gDefaultParser;
|
||||
}
|
||||
|
||||
} // end namespace Con
|
105
engine/console/consoleParser.h
Executable file
105
engine/console/consoleParser.h
Executable file
@ -0,0 +1,105 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//
|
||||
// TorqueBASIC
|
||||
// (c) Copyright 2004 Burnt Wasp
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _CONSOLE_PARSER_H_
|
||||
#define _CONSOLE_PARSER_H_
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
namespace Compiler
|
||||
{
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
/// \brief Function for GetCurrentFile from the lexer
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
typedef const char *(*fnGetCurrentFile)();
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
/// \brief Function for GetCurrentLine from the lexer
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
typedef S32 (*fnGetCurrentLine)();
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
/// \brief Function for Parse from the lexer
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
typedef S32 (*fnParse)();
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
/// \brief Function for Restart from the lexer
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
typedef void (*fnRestart)(FILE *input_file);
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
/// \brief Function for SetScanBuffer from the lexer
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
typedef void (*fnSetScanBuffer)(const char *sb, const char *fn);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
/// \brief List of parsers for the compiler
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
struct ConsoleParser
|
||||
{
|
||||
struct ConsoleParser *next; //!< Next object in list or NULL
|
||||
|
||||
char *ext; //!< Filename extension handled by this parser
|
||||
|
||||
fnGetCurrentFile getCurrentFile; //!< GetCurrentFile lexer function
|
||||
fnGetCurrentLine getCurrentLine; //!< GetCurrentLine lexer function
|
||||
fnParse parse; //!< Parse lexer function
|
||||
fnRestart restart; //!< Restart lexer function
|
||||
fnSetScanBuffer setScanBuffer; //!< SetScanBuffer lexer function
|
||||
};
|
||||
|
||||
// Macros
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
/// \brief Declare a parser's function prototypes
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
#define CON_DECLARE_PARSER(prefix) \
|
||||
const char * prefix##GetCurrentFile(); \
|
||||
S32 prefix##GetCurrentLine(); \
|
||||
void prefix##SetScanBuffer(const char *sb, const char *fn); \
|
||||
S32 prefix##parse(); \
|
||||
void prefix##restart(FILE *input_file)
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
/// \brief Helper macro to add console parsers
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
#define CON_ADD_PARSER(prefix, ext, def) \
|
||||
Compiler::addConsoleParser(ext, prefix##GetCurrentFile, prefix##GetCurrentLine, prefix##parse, \
|
||||
prefix##restart, prefix##SetScanBuffer, def)
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
/// \brief Free the console parser list
|
||||
///
|
||||
/// \sa AddConsoleParser()
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void freeConsoleParserList(void);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
/// \brief Add a console parser to the list
|
||||
///
|
||||
/// \param ext Filename extension
|
||||
/// \param gcf GetCurrentFile function
|
||||
/// \param gcl GetCurrentLine function
|
||||
/// \param p Parse function
|
||||
/// \param r Restart function
|
||||
/// \param ssb SetScanBuffer function
|
||||
/// \param def true if this is the default parser (<b>Note:</b> set this only on the .cs parser!)
|
||||
/// \return true for success, false for failure (out of memory)
|
||||
/// \sa FreeConsoleParserList(), ConsoleParser
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool addConsoleParser(char *ext, fnGetCurrentFile gcf, fnGetCurrentLine gcl, fnParse p, fnRestart r, fnSetScanBuffer ssb, bool def = false);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
/// \brief Get the parser for a particular file based on its extension
|
||||
///
|
||||
/// \param filename Filename of file to obtain parser for
|
||||
/// \sa ConsoleParser
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
ConsoleParser * getParserForFile(const char *filename);
|
||||
|
||||
} // end namespace Con
|
||||
|
||||
#endif // _CONSOLE_PARSER_H_
|
490
engine/console/consoleTypes.cc
Executable file
490
engine/console/consoleTypes.cc
Executable file
@ -0,0 +1,490 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "console/console.h"
|
||||
#include "console/consoleTypes.h"
|
||||
#include "core/stringTable.h"
|
||||
#include "core/color.h"
|
||||
#include "console/simBase.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// TypeString
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
ConsoleType( string, TypeString, sizeof(const char*) )
|
||||
|
||||
ConsoleGetType( TypeString )
|
||||
{
|
||||
return *((const char **)(dptr));
|
||||
}
|
||||
|
||||
ConsoleSetType( TypeString )
|
||||
{
|
||||
if(argc == 1)
|
||||
*((const char **) dptr) = StringTable->insert(argv[0]);
|
||||
else
|
||||
Con::printf("(TypeString) Cannot set multiple args to a single string.");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// TypeCaseString
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
ConsoleType( caseString, TypeCaseString, sizeof(const char*) )
|
||||
|
||||
ConsoleSetType( TypeCaseString )
|
||||
{
|
||||
if(argc == 1)
|
||||
*((const char **) dptr) = StringTable->insert(argv[0], true);
|
||||
else
|
||||
Con::printf("(TypeCaseString) Cannot set multiple args to a single string.");
|
||||
}
|
||||
|
||||
ConsoleGetType( TypeCaseString )
|
||||
{
|
||||
return *((const char **)(dptr));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// TypeFileName
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
ConsoleType( filename, TypeFilename, sizeof( const char* ) )
|
||||
|
||||
ConsoleSetType( TypeFilename )
|
||||
{
|
||||
if(argc == 1)
|
||||
{
|
||||
char buffer[1024];
|
||||
if (Con::expandScriptFilename(buffer, 1024, argv[0]))
|
||||
*((const char **) dptr) = StringTable->insert(buffer);
|
||||
else
|
||||
Con::warnf("(TypeFilename) illegal filename detected: %s", argv[0]);
|
||||
}
|
||||
else
|
||||
Con::printf("(TypeFilename) Cannot set multiple args to a single filename.");
|
||||
}
|
||||
|
||||
ConsoleGetType( TypeFilename )
|
||||
{
|
||||
return *((const char **)(dptr));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// TypeS8
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
ConsoleType( char, TypeS8, sizeof(U8) )
|
||||
|
||||
ConsoleGetType( TypeS8 )
|
||||
{
|
||||
char* returnBuffer = Con::getReturnBuffer(256);
|
||||
dSprintf(returnBuffer, 256, "%d", *((U8 *) dptr) );
|
||||
return returnBuffer;
|
||||
}
|
||||
|
||||
ConsoleSetType( TypeS8 )
|
||||
{
|
||||
if(argc == 1)
|
||||
*((U8 *) dptr) = dAtoi(argv[0]);
|
||||
else
|
||||
Con::printf("(TypeU8) Cannot set multiple args to a single S32.");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// TypeS32
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
ConsoleType( int, TypeS32, sizeof(S32) )
|
||||
|
||||
ConsoleGetType( TypeS32 )
|
||||
{
|
||||
char* returnBuffer = Con::getReturnBuffer(256);
|
||||
dSprintf(returnBuffer, 256, "%d", *((S32 *) dptr) );
|
||||
return returnBuffer;
|
||||
}
|
||||
|
||||
ConsoleSetType( TypeS32 )
|
||||
{
|
||||
if(argc == 1)
|
||||
*((S32 *) dptr) = dAtoi(argv[0]);
|
||||
else
|
||||
Con::printf("(TypeS32) Cannot set multiple args to a single S32.");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// TypeS32Vector
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
ConsoleType( intList, TypeS32Vector, sizeof(Vector<S32>) )
|
||||
|
||||
ConsoleGetType( TypeS32Vector )
|
||||
{
|
||||
Vector<S32> *vec = (Vector<S32> *)dptr;
|
||||
char* returnBuffer = Con::getReturnBuffer(1024);
|
||||
S32 maxReturn = 1024;
|
||||
returnBuffer[0] = '\0';
|
||||
S32 returnLeng = 0;
|
||||
for (Vector<S32>::iterator itr = vec->begin(); itr != vec->end(); itr++)
|
||||
{
|
||||
// concatenate the next value onto the return string
|
||||
dSprintf(returnBuffer + returnLeng, maxReturn - returnLeng, "%d ", *itr);
|
||||
// update the length of the return string (so far)
|
||||
returnLeng = dStrlen(returnBuffer);
|
||||
}
|
||||
// trim off that last extra space
|
||||
if (returnLeng > 0 && returnBuffer[returnLeng - 1] == ' ')
|
||||
returnBuffer[returnLeng - 1] = '\0';
|
||||
return returnBuffer;
|
||||
}
|
||||
|
||||
ConsoleSetType( TypeS32Vector )
|
||||
{
|
||||
Vector<S32> *vec = (Vector<S32> *)dptr;
|
||||
// we assume the vector should be cleared first (not just appending)
|
||||
vec->clear();
|
||||
if(argc == 1)
|
||||
{
|
||||
const char *values = argv[0];
|
||||
const char *endValues = values + dStrlen(values);
|
||||
S32 value;
|
||||
// advance through the string, pulling off S32's and advancing the pointer
|
||||
while (values < endValues && dSscanf(values, "%d", &value) != 0)
|
||||
{
|
||||
vec->push_back(value);
|
||||
const char *nextValues = dStrchr(values, ' ');
|
||||
if (nextValues != 0 && nextValues < endValues)
|
||||
values = nextValues + 1;
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (argc > 1)
|
||||
{
|
||||
for (S32 i = 0; i < argc; i++)
|
||||
vec->push_back(dAtoi(argv[i]));
|
||||
}
|
||||
else
|
||||
Con::printf("Vector<S32> must be set as { a, b, c, ... } or \"a b c ...\"");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// TypeF32
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
ConsoleType( float, TypeF32, sizeof(F32) )
|
||||
|
||||
ConsoleGetType( TypeF32 )
|
||||
{
|
||||
char* returnBuffer = Con::getReturnBuffer(256);
|
||||
dSprintf(returnBuffer, 256, "%g", *((F32 *) dptr) );
|
||||
return returnBuffer;
|
||||
}
|
||||
ConsoleSetType( TypeF32 )
|
||||
{
|
||||
if(argc == 1)
|
||||
*((F32 *) dptr) = dAtof(argv[0]);
|
||||
else
|
||||
Con::printf("(TypeF32) Cannot set multiple args to a single F32.");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// TypeF32Vector
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
ConsoleType( floatList, TypeF32Vector, sizeof(Vector<F32>) )
|
||||
|
||||
ConsoleGetType( TypeF32Vector )
|
||||
{
|
||||
Vector<F32> *vec = (Vector<F32> *)dptr;
|
||||
char* returnBuffer = Con::getReturnBuffer(1024);
|
||||
S32 maxReturn = 1024;
|
||||
returnBuffer[0] = '\0';
|
||||
S32 returnLeng = 0;
|
||||
for (Vector<F32>::iterator itr = vec->begin(); itr != vec->end(); itr++)
|
||||
{
|
||||
// concatenate the next value onto the return string
|
||||
dSprintf(returnBuffer + returnLeng, maxReturn - returnLeng, "%g ", *itr);
|
||||
// update the length of the return string (so far)
|
||||
returnLeng = dStrlen(returnBuffer);
|
||||
}
|
||||
// trim off that last extra space
|
||||
if (returnLeng > 0 && returnBuffer[returnLeng - 1] == ' ')
|
||||
returnBuffer[returnLeng - 1] = '\0';
|
||||
return returnBuffer;
|
||||
}
|
||||
|
||||
ConsoleSetType( TypeF32Vector )
|
||||
{
|
||||
Vector<F32> *vec = (Vector<F32> *)dptr;
|
||||
// we assume the vector should be cleared first (not just appending)
|
||||
vec->clear();
|
||||
if(argc == 1)
|
||||
{
|
||||
const char *values = argv[0];
|
||||
const char *endValues = values + dStrlen(values);
|
||||
F32 value;
|
||||
// advance through the string, pulling off F32's and advancing the pointer
|
||||
while (values < endValues && dSscanf(values, "%g", &value) != 0)
|
||||
{
|
||||
vec->push_back(value);
|
||||
const char *nextValues = dStrchr(values, ' ');
|
||||
if (nextValues != 0 && nextValues < endValues)
|
||||
values = nextValues + 1;
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (argc > 1)
|
||||
{
|
||||
for (S32 i = 0; i < argc; i++)
|
||||
vec->push_back(dAtof(argv[i]));
|
||||
}
|
||||
else
|
||||
Con::printf("Vector<F32> must be set as { a, b, c, ... } or \"a b c ...\"");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// TypeBool
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
ConsoleType( bool, TypeBool, sizeof(bool) )
|
||||
|
||||
ConsoleGetType( TypeBool )
|
||||
{
|
||||
return *((bool *) dptr) ? "1" : "0";
|
||||
}
|
||||
|
||||
ConsoleSetType( TypeBool )
|
||||
{
|
||||
if(argc == 1)
|
||||
*((bool *) dptr) = dAtob(argv[0]);
|
||||
else
|
||||
Con::printf("(TypeBool) Cannot set multiple args to a single bool.");
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// TypeBoolVector
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
ConsoleType( boolList, TypeBoolVector, sizeof(Vector<bool>) )
|
||||
|
||||
ConsoleGetType( TypeBoolVector )
|
||||
{
|
||||
Vector<bool> *vec = (Vector<bool>*)dptr;
|
||||
char* returnBuffer = Con::getReturnBuffer(1024);
|
||||
S32 maxReturn = 1024;
|
||||
returnBuffer[0] = '\0';
|
||||
S32 returnLeng = 0;
|
||||
for (Vector<bool>::iterator itr = vec->begin(); itr < vec->end(); itr++)
|
||||
{
|
||||
// concatenate the next value onto the return string
|
||||
dSprintf(returnBuffer + returnLeng, maxReturn - returnLeng, "%d ", (*itr == true ? 1 : 0));
|
||||
returnLeng = dStrlen(returnBuffer);
|
||||
}
|
||||
// trim off that last extra space
|
||||
if (returnLeng > 0 && returnBuffer[returnLeng - 1] == ' ')
|
||||
returnBuffer[returnLeng - 1] = '\0';
|
||||
return(returnBuffer);
|
||||
}
|
||||
|
||||
ConsoleSetType( TypeBoolVector )
|
||||
{
|
||||
Vector<bool> *vec = (Vector<bool>*)dptr;
|
||||
// we assume the vector should be cleared first (not just appending)
|
||||
vec->clear();
|
||||
if (argc == 1)
|
||||
{
|
||||
const char *values = argv[0];
|
||||
const char *endValues = values + dStrlen(values);
|
||||
S32 value;
|
||||
// advance through the string, pulling off bool's and advancing the pointer
|
||||
while (values < endValues && dSscanf(values, "%d", &value) != 0)
|
||||
{
|
||||
vec->push_back(value == 0 ? false : true);
|
||||
const char *nextValues = dStrchr(values, ' ');
|
||||
if (nextValues != 0 && nextValues < endValues)
|
||||
values = nextValues + 1;
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (argc > 1)
|
||||
{
|
||||
for (S32 i = 0; i < argc; i++)
|
||||
vec->push_back(dAtob(argv[i]));
|
||||
}
|
||||
else
|
||||
Con::printf("Vector<bool> must be set as { a, b, c, ... } or \"a b c ...\"");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// TypeEnum
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
ConsoleType( enumval, TypeEnum, sizeof(S32) )
|
||||
|
||||
ConsoleGetType( TypeEnum )
|
||||
{
|
||||
AssertFatal(tbl, "Null enum table passed to getDataTypeEnum()");
|
||||
S32 dptrVal = *(S32*)dptr;
|
||||
for (S32 i = 0; i < tbl->size; i++)
|
||||
{
|
||||
if (dptrVal == tbl->table[i].index)
|
||||
{
|
||||
return tbl->table[i].label;
|
||||
}
|
||||
}
|
||||
|
||||
//not found
|
||||
return "";
|
||||
}
|
||||
|
||||
ConsoleSetType( TypeEnum )
|
||||
{
|
||||
AssertFatal(tbl, "Null enum table passed to setDataTypeEnum()");
|
||||
if (argc != 1) return;
|
||||
|
||||
S32 val = 0;
|
||||
for (S32 i = 0; i < tbl->size; i++)
|
||||
{
|
||||
if (! dStricmp(argv[0], tbl->table[i].label))
|
||||
{
|
||||
val = tbl->table[i].index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
*((S32 *) dptr) = val;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// TypeFlag
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
ConsoleType( flag, TypeFlag, sizeof(S32) )
|
||||
|
||||
ConsoleGetType( TypeFlag )
|
||||
{
|
||||
BitSet32 tempFlags = *(BitSet32 *)dptr;
|
||||
if (tempFlags.test(flag)) return "true";
|
||||
else return "false";
|
||||
}
|
||||
|
||||
ConsoleSetType( TypeFlag )
|
||||
{
|
||||
bool value = true;
|
||||
if (argc != 1)
|
||||
{
|
||||
Con::printf("flag must be true or false");
|
||||
}
|
||||
else
|
||||
{
|
||||
value = dAtob(argv[0]);
|
||||
}
|
||||
((BitSet32 *)dptr)->set(flag, value);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// TypeColorF
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
ConsoleType( ColorF, TypeColorF, sizeof(ColorF) )
|
||||
|
||||
ConsoleGetType( TypeColorF )
|
||||
{
|
||||
ColorF * color = (ColorF*)dptr;
|
||||
char* returnBuffer = Con::getReturnBuffer(256);
|
||||
dSprintf(returnBuffer, 256, "%g %g %g %g", color->red, color->green, color->blue, color->alpha);
|
||||
return(returnBuffer);
|
||||
}
|
||||
|
||||
ConsoleSetType( TypeColorF )
|
||||
{
|
||||
ColorF *tmpColor = (ColorF *) dptr;
|
||||
if(argc == 1)
|
||||
{
|
||||
tmpColor->set(0, 0, 0, 1);
|
||||
F32 r,g,b,a;
|
||||
S32 args = dSscanf(argv[0], "%g %g %g %g", &r, &g, &b, &a);
|
||||
tmpColor->red = r;
|
||||
tmpColor->green = g;
|
||||
tmpColor->blue = b;
|
||||
if (args == 4)
|
||||
tmpColor->alpha = a;
|
||||
}
|
||||
else if(argc == 3)
|
||||
{
|
||||
tmpColor->red = dAtof(argv[0]);
|
||||
tmpColor->green = dAtof(argv[1]);
|
||||
tmpColor->blue = dAtof(argv[2]);
|
||||
tmpColor->alpha = 1.f;
|
||||
}
|
||||
else if(argc == 4)
|
||||
{
|
||||
tmpColor->red = dAtof(argv[0]);
|
||||
tmpColor->green = dAtof(argv[1]);
|
||||
tmpColor->blue = dAtof(argv[2]);
|
||||
tmpColor->alpha = dAtof(argv[3]);
|
||||
}
|
||||
else
|
||||
Con::printf("Color must be set as { r, g, b [,a] }");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// TypeColorI
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
ConsoleType( ColorI, TypeColorI, sizeof(ColorI) )
|
||||
|
||||
ConsoleGetType( TypeColorI )
|
||||
{
|
||||
ColorI *color = (ColorI *) dptr;
|
||||
char* returnBuffer = Con::getReturnBuffer(256);
|
||||
dSprintf(returnBuffer, 256, "%d %d %d %d", color->red, color->green, color->blue, color->alpha);
|
||||
return returnBuffer;
|
||||
}
|
||||
|
||||
ConsoleSetType( TypeColorI )
|
||||
{
|
||||
ColorI *tmpColor = (ColorI *) dptr;
|
||||
if(argc == 1)
|
||||
{
|
||||
tmpColor->set(0, 0, 0, 255);
|
||||
S32 r,g,b,a;
|
||||
S32 args = dSscanf(argv[0], "%d %d %d %d", &r, &g, &b, &a);
|
||||
tmpColor->red = r;
|
||||
tmpColor->green = g;
|
||||
tmpColor->blue = b;
|
||||
if (args == 4)
|
||||
tmpColor->alpha = a;
|
||||
}
|
||||
else if(argc == 3)
|
||||
{
|
||||
tmpColor->red = dAtoi(argv[0]);
|
||||
tmpColor->green = dAtoi(argv[1]);
|
||||
tmpColor->blue = dAtoi(argv[2]);
|
||||
tmpColor->alpha = 255;
|
||||
}
|
||||
else if(argc == 4)
|
||||
{
|
||||
tmpColor->red = dAtoi(argv[0]);
|
||||
tmpColor->green = dAtoi(argv[1]);
|
||||
tmpColor->blue = dAtoi(argv[2]);
|
||||
tmpColor->alpha = dAtoi(argv[3]);
|
||||
}
|
||||
else
|
||||
Con::printf("Color must be set as { r, g, b [,a] }");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// TypeSimObjectPtr
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
ConsoleType( SimObjectPtr, TypeSimObjectPtr, sizeof(SimObject*) )
|
||||
|
||||
ConsoleSetType( TypeSimObjectPtr )
|
||||
{
|
||||
if(argc == 1)
|
||||
{
|
||||
SimObject **obj = (SimObject **)dptr;
|
||||
*obj = Sim::findObject(argv[0]);
|
||||
}
|
||||
else
|
||||
Con::printf("(TypeSimObjectPtr) Cannot set multiple args to a single S32.");
|
||||
}
|
||||
|
||||
ConsoleGetType( TypeSimObjectPtr )
|
||||
{
|
||||
SimObject **obj = (SimObject**)dptr;
|
||||
char* returnBuffer = Con::getReturnBuffer(256);
|
||||
dSprintf(returnBuffer, 256, "%s", *obj ? (*obj)->getName() : "");
|
||||
return returnBuffer;
|
||||
}
|
38
engine/console/consoleTypes.h
Executable file
38
engine/console/consoleTypes.h
Executable file
@ -0,0 +1,38 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _CONSOLETYPES_H_
|
||||
#define _CONSOLETYPES_H_
|
||||
|
||||
#ifndef _DYNAMIC_CONSOLETYPES_H_
|
||||
#include "console/dynamicTypes.h"
|
||||
#endif
|
||||
|
||||
#ifndef Offset
|
||||
#if defined(TORQUE_COMPILER_GCC) && (__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1))
|
||||
#define Offset(m,T) ((int)(&((T *)1)->m) - 1)
|
||||
#else
|
||||
#define Offset(x, cls) ((dsize_t)((const char *)&(((cls *)0)->x)-(const char *)0))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Define Core Console Types
|
||||
DefineConsoleType( TypeF32 )
|
||||
DefineConsoleType( TypeS8 )
|
||||
DefineConsoleType( TypeS32 )
|
||||
DefineConsoleType( TypeS32Vector )
|
||||
DefineConsoleType( TypeBool )
|
||||
DefineConsoleType( TypeBoolVector )
|
||||
DefineConsoleType( TypeF32Vector )
|
||||
DefineConsoleType( TypeString )
|
||||
DefineConsoleType( TypeCaseString )
|
||||
DefineConsoleType( TypeFilename )
|
||||
DefineConsoleType( TypeEnum )
|
||||
DefineConsoleType( TypeFlag )
|
||||
DefineConsoleType( TypeColorI )
|
||||
DefineConsoleType( TypeColorF )
|
||||
DefineConsoleType( TypeSimObjectPtr )
|
||||
|
||||
#endif
|
72
engine/console/dynamicTypes.cc
Executable file
72
engine/console/dynamicTypes.cc
Executable file
@ -0,0 +1,72 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "console/dynamicTypes.h"
|
||||
|
||||
// Init the globals.
|
||||
ConsoleBaseType *ConsoleBaseType::smListHead = NULL;
|
||||
S32 ConsoleBaseType::smConsoleTypeCount = 0;
|
||||
|
||||
// And, we also privately store the types lookup table.
|
||||
VectorPtr<ConsoleBaseType*> gConsoleTypeTable;
|
||||
|
||||
ConsoleBaseType *ConsoleBaseType::getListHead()
|
||||
{
|
||||
return smListHead;
|
||||
}
|
||||
|
||||
void ConsoleBaseType::initialize()
|
||||
{
|
||||
// Prep and empty the vector.
|
||||
gConsoleTypeTable.setSize(smConsoleTypeCount+1);
|
||||
dMemset(gConsoleTypeTable.address(), 0, sizeof(ConsoleBaseType*) * gConsoleTypeTable.size());
|
||||
|
||||
// Walk the list and register each one with the console system.
|
||||
ConsoleBaseType *walk = getListHead();
|
||||
while(walk)
|
||||
{
|
||||
// Store a pointer to the type in the appropriate slot.
|
||||
const S32 id = walk->getTypeID();
|
||||
AssertFatal(gConsoleTypeTable[id]==NULL, "ConsoleBaseType::initialize - encountered a table slot that contained something!");
|
||||
gConsoleTypeTable[id] = walk;
|
||||
|
||||
// Advance down the list...
|
||||
walk = walk->getListNext();
|
||||
}
|
||||
|
||||
// Alright, we're all done here; we can now achieve fast lookups by ID.
|
||||
}
|
||||
|
||||
ConsoleBaseType *ConsoleBaseType::getType(const S32 typeID)
|
||||
{
|
||||
return gConsoleTypeTable[typeID];
|
||||
}
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
ConsoleBaseType::ConsoleBaseType(const S32 size, S32 *idPtr, const char *aTypeName)
|
||||
{
|
||||
// General initialization.
|
||||
mInspectorFieldType = NULL;
|
||||
|
||||
// Store general info.
|
||||
mTypeSize = size;
|
||||
mTypeName = aTypeName;
|
||||
|
||||
// Get our type ID and store it.
|
||||
mTypeID = smConsoleTypeCount++;
|
||||
*idPtr = mTypeID;
|
||||
|
||||
// Link ourselves into the list.
|
||||
mListNext = smListHead;
|
||||
smListHead = this;
|
||||
|
||||
// Alright, all done for now. Console initialization time code
|
||||
// takes us from having a list of general info and classes to
|
||||
// a fully initialized type table.
|
||||
}
|
||||
|
||||
ConsoleBaseType::~ConsoleBaseType()
|
||||
{
|
||||
// Nothing to do for now; we could unlink ourselves from the list, but why?
|
||||
}
|
106
engine/console/dynamicTypes.h
Executable file
106
engine/console/dynamicTypes.h
Executable file
@ -0,0 +1,106 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
#ifndef _DYNAMIC_CONSOLETYPES_H_
|
||||
#define _DYNAMIC_CONSOLETYPES_H_
|
||||
|
||||
#ifndef _SIMBASE_H_
|
||||
#include "console/simBase.h"
|
||||
#endif
|
||||
|
||||
class ConsoleBaseType
|
||||
{
|
||||
protected:
|
||||
/// This is used to generate unique IDs for each type.
|
||||
static S32 smConsoleTypeCount;
|
||||
|
||||
/// We maintain a linked list of all console types; this is its head.
|
||||
static ConsoleBaseType *smListHead;
|
||||
|
||||
/// Next item in the list of all console types.
|
||||
ConsoleBaseType *mListNext;
|
||||
|
||||
/// Destructor is private to avoid people mucking up the list.
|
||||
~ConsoleBaseType();
|
||||
|
||||
S32 mTypeID;
|
||||
dsize_t mTypeSize;
|
||||
const char *mTypeName;
|
||||
const char *mInspectorFieldType;
|
||||
|
||||
public:
|
||||
|
||||
/// @name cbt_list List Interface
|
||||
///
|
||||
/// Interface for accessing/traversing the list of types.
|
||||
|
||||
/// Get the head of the list.
|
||||
static ConsoleBaseType *getListHead();
|
||||
|
||||
/// Get the item that follows this item in the list.
|
||||
ConsoleBaseType *getListNext() const
|
||||
{
|
||||
return mListNext;
|
||||
}
|
||||
|
||||
/// Called once to initialize the console type system.
|
||||
static void initialize();
|
||||
|
||||
/// Call me to get a pointer to a type's info.
|
||||
static ConsoleBaseType *getType(const S32 typeID);
|
||||
|
||||
/// @}
|
||||
|
||||
/// The constructor is responsible for linking an element into the
|
||||
/// master list, registering the type ID, etc.
|
||||
ConsoleBaseType(const S32 size, S32 *idPtr, const char *aTypeName);
|
||||
|
||||
const S32 getTypeID() const { return mTypeID; }
|
||||
const S32 getTypeSize() const { return mTypeSize; }
|
||||
const char *getTypeName() const { return mTypeName; }
|
||||
|
||||
void setInspectorFieldType(const char *type) { mInspectorFieldType = type; }
|
||||
const char *getInspectorFieldType() { return mInspectorFieldType; }
|
||||
|
||||
virtual void setData(void *dptr, S32 argc, const char **argv, EnumTable *tbl, BitSet32 flag)=0;
|
||||
virtual const char *getData(void *dptr, EnumTable *tbl, BitSet32 flag )=0;
|
||||
virtual const char *getTypeClassName()=0;
|
||||
virtual const bool isDatablock() { return false; };
|
||||
};
|
||||
|
||||
#define DefineConsoleType( type ) extern S32 type;
|
||||
|
||||
#define ConsoleType( typeName, type, size ) \
|
||||
class ConsoleType##type : public ConsoleBaseType \
|
||||
{ \
|
||||
public: \
|
||||
ConsoleType##type (const S32 aSize, S32 *idPtr, const char *aTypeName) : ConsoleBaseType(aSize, idPtr, aTypeName) { } \
|
||||
virtual void setData(void *dptr, S32 argc, const char **argv, EnumTable *tbl, BitSet32 flag); \
|
||||
virtual const char *getData(void *dptr, EnumTable *tbl, BitSet32 flag ); \
|
||||
virtual const char *getTypeClassName() { return #typeName ; } \
|
||||
}; \
|
||||
S32 type = -1; \
|
||||
ConsoleType##type gConsoleType##type##Instance(size,&type,#type); \
|
||||
|
||||
#define ConsoleSetType( type ) \
|
||||
void ConsoleType##type::setData(void *dptr, S32 argc, const char **argv, EnumTable *tbl, BitSet32 flag)
|
||||
|
||||
#define ConsoleGetType( type ) \
|
||||
const char *ConsoleType##type::getData(void *dptr, EnumTable *tbl, BitSet32 flag )
|
||||
|
||||
#define DatablockConsoleType( typeName, type, size, className ) \
|
||||
class ConsoleType##type : public ConsoleBaseType \
|
||||
{ \
|
||||
public: \
|
||||
ConsoleType##type (const S32 aSize, S32 *idPtr, const char *aTypeName) : ConsoleBaseType(aSize, idPtr, aTypeName) { } \
|
||||
virtual void setData(void *dptr, S32 argc, const char **argv, EnumTable *tbl, BitSet32 flag); \
|
||||
virtual const char *getData(void *dptr, EnumTable *tbl, BitSet32 flag ); \
|
||||
virtual const char *getTypeClassName() { return #className; }; \
|
||||
virtual const bool isDatablock() { return true; }; \
|
||||
}; \
|
||||
S32 type = -1; \
|
||||
ConsoleType##type gConsoleType##type##Instance(size,&type,#type); \
|
||||
|
||||
|
||||
#endif
|
170
engine/console/scriptObject.cc
Executable file
170
engine/console/scriptObject.cc
Executable file
@ -0,0 +1,170 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Torque Game Engine
|
||||
// Copyright (C) GarageGames.com, Inc.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform/platform.h"
|
||||
#include "console/simBase.h"
|
||||
#include "console/consoleTypes.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Script object placeholder
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class ScriptObject : public SimObject
|
||||
{
|
||||
typedef SimObject Parent;
|
||||
StringTableEntry mClassName;
|
||||
StringTableEntry mSuperClassName;
|
||||
public:
|
||||
ScriptObject();
|
||||
bool onAdd();
|
||||
void onRemove();
|
||||
|
||||
DECLARE_CONOBJECT(ScriptObject);
|
||||
|
||||
static void initPersistFields();
|
||||
};
|
||||
|
||||
IMPLEMENT_CONOBJECT(ScriptObject);
|
||||
|
||||
void ScriptObject::initPersistFields()
|
||||
{
|
||||
addGroup("Classes", "Script objects have the ability to inherit and have class information.");
|
||||
addField("class", TypeString, Offset(mClassName, ScriptObject), "Class of object.");
|
||||
addField("superClass", TypeString, Offset(mSuperClassName, ScriptObject), "Superclass of object.");
|
||||
endGroup("Classes");
|
||||
}
|
||||
|
||||
ScriptObject::ScriptObject()
|
||||
{
|
||||
mClassName = "";
|
||||
mSuperClassName = "";
|
||||
}
|
||||
|
||||
bool ScriptObject::onAdd()
|
||||
{
|
||||
if (!Parent::onAdd())
|
||||
return false;
|
||||
|
||||
// it's possible that all the namespace links can fail, if
|
||||
// multiple objects are named the same thing with different script
|
||||
// hierarchies.
|
||||
// linkNamespaces will now return false and echo an error message
|
||||
// rather than asserting.
|
||||
|
||||
// superClassName -> ScriptObject
|
||||
StringTableEntry parent = StringTable->insert("ScriptObject");
|
||||
if(mSuperClassName[0])
|
||||
{
|
||||
if(Con::linkNamespaces(parent, mSuperClassName))
|
||||
parent = mSuperClassName;
|
||||
}
|
||||
|
||||
// className -> superClassName
|
||||
if (mClassName[0])
|
||||
{
|
||||
if(Con::linkNamespaces(parent, mClassName))
|
||||
parent = mClassName;
|
||||
}
|
||||
|
||||
// objectName -> className
|
||||
StringTableEntry objectName = getName();
|
||||
if (objectName && objectName[0])
|
||||
{
|
||||
if(Con::linkNamespaces(parent, objectName))
|
||||
parent = objectName;
|
||||
}
|
||||
|
||||
// Store our namespace
|
||||
mNameSpace = Con::lookupNamespace(parent);
|
||||
|
||||
// Call onAdd in script!
|
||||
Con::executef(this, 2, "onAdd", Con::getIntArg(getId()));
|
||||
return true;
|
||||
}
|
||||
|
||||
void ScriptObject::onRemove()
|
||||
{
|
||||
// Call onRemove in script!
|
||||
Con::executef(this, 2, "onRemove", Con::getIntArg(getId()));
|
||||
Parent::onRemove();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Script group placeholder
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class ScriptGroup : public SimGroup
|
||||
{
|
||||
typedef SimGroup Parent;
|
||||
StringTableEntry mClassName;
|
||||
StringTableEntry mSuperClassName;
|
||||
public:
|
||||
ScriptGroup();
|
||||
bool onAdd();
|
||||
void onRemove();
|
||||
|
||||
DECLARE_CONOBJECT(ScriptGroup);
|
||||
|
||||
static void initPersistFields();
|
||||
};
|
||||
|
||||
IMPLEMENT_CONOBJECT(ScriptGroup);
|
||||
|
||||
void ScriptGroup::initPersistFields()
|
||||
{
|
||||
addGroup("Classes");
|
||||
addField("class", TypeString, Offset(mClassName, ScriptGroup));
|
||||
addField("superClass", TypeString, Offset(mSuperClassName, ScriptGroup));
|
||||
endGroup("Classes");
|
||||
}
|
||||
|
||||
ScriptGroup::ScriptGroup()
|
||||
{
|
||||
mClassName = "";
|
||||
mSuperClassName = "";
|
||||
}
|
||||
|
||||
bool ScriptGroup::onAdd()
|
||||
{
|
||||
if (!Parent::onAdd())
|
||||
return false;
|
||||
|
||||
// superClassName -> ScriptGroup
|
||||
StringTableEntry parent = StringTable->insert("ScriptGroup");
|
||||
if(mSuperClassName[0])
|
||||
{
|
||||
if(Con::linkNamespaces(parent, mSuperClassName))
|
||||
parent = mSuperClassName;
|
||||
}
|
||||
|
||||
// className -> superClassName
|
||||
if(mClassName[0])
|
||||
{
|
||||
if(Con::linkNamespaces(parent, mClassName))
|
||||
parent = mClassName;
|
||||
}
|
||||
|
||||
// objectName -> className
|
||||
StringTableEntry objectName = getName();
|
||||
if (objectName && objectName[0])
|
||||
{
|
||||
if(Con::linkNamespaces(parent, objectName))
|
||||
parent = objectName;
|
||||
}
|
||||
|
||||
// Store our namespace
|
||||
mNameSpace = Con::lookupNamespace(parent);
|
||||
|
||||
// Call onAdd in script!
|
||||
Con::executef(this, 2, "onAdd", Con::getIntArg(getId()));
|
||||
return true;
|
||||
}
|
||||
|
||||
void ScriptGroup::onRemove()
|
||||
{
|
||||
// Call onRemove in script!
|
||||
Con::executef(this, 2, "onRemove", Con::getIntArg(getId()));
|
||||
Parent::onRemove();
|
||||
}
|
1739
engine/console/simBase.cc
Executable file
1739
engine/console/simBase.cc
Executable file
File diff suppressed because it is too large
Load Diff
1368
engine/console/simBase.h
Executable file
1368
engine/console/simBase.h
Executable file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user