mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-03-03 12:49:40 +00:00
- added -debug option
- removed all logging and output per default - updated to latest monster, bullet and ogre versions git-svn-id: https://openmw.svn.sourceforge.net/svnroot/openmw/trunk@102 ea6a568a-9f4f-0410-981a-c910a81bb256
This commit is contained in:
parent
fd79223cf7
commit
714b724bf2
28 changed files with 439 additions and 203 deletions
|
@ -37,11 +37,11 @@ Dependencies:
|
|||
|
||||
Dependencies needed to build OpenMW:
|
||||
|
||||
OGRE 1.4.5 (3d engine)
|
||||
OGRE 1.6.2 (3d engine)
|
||||
OIS-1.0.0 (input system)
|
||||
MyGUI (GUI system for OGRE)
|
||||
Bullet 2.72 (collision and physics library)
|
||||
Monster 0.11 (scripting engine, included)
|
||||
MyGUI 2.2.2 (GUI system for OGRE)
|
||||
Bullet 2.74 (collision and physics library)
|
||||
Monster 0.12 (scripting engine, included)
|
||||
OpenAL (3d sound system)
|
||||
libavcodec,
|
||||
libavformat (For MP3 playback)
|
||||
|
@ -116,7 +116,7 @@ the libbulletdynamics.a etc if necessary.)
|
|||
|
||||
Example:
|
||||
cd openmw/bullet
|
||||
ln -s ~/software/bullet-2.72/src/BulletCollision/libbulletcollision.a .
|
||||
ln -s ~/software/bullet-2.74/src/BulletCollision/libbulletcollision.a .
|
||||
|
||||
Next, create a link to the "src" directory in openmw/include/bullet/ .
|
||||
|
||||
|
@ -124,7 +124,7 @@ Example:
|
|||
cd openmw
|
||||
mkdir -p include
|
||||
cd include
|
||||
ln -s ~/software/bullet-2.72/src bullet
|
||||
ln -s ~/software/bullet-2.74/src bullet
|
||||
|
||||
|
||||
Setting up MyGUI
|
||||
|
|
3
Makefile
3
Makefile
|
@ -1,9 +1,8 @@
|
|||
# Designed for GNU Make
|
||||
|
||||
# Compiler settings
|
||||
CXXFLAGS?= -g
|
||||
CXXFLAGS?= -g -Iutil/
|
||||
DMD=gdmd -version=Posix
|
||||
#DMD=dmd -version=Posix
|
||||
|
||||
# Some extra flags for niftool and bsatool
|
||||
NIFFLAGS=-debug=warnstd -debug=check -debug=statecheck -debug=strict -debug=verbose
|
||||
|
|
|
@ -87,7 +87,7 @@ int main(char[][] args)
|
|||
int help(char[] msg)
|
||||
{
|
||||
writefln("%s", msg);
|
||||
writefln("Format: bsatool archive.bsa [-x filename] [-l] [-n]");
|
||||
writefln("Format: bsatool archive.bsa [-x filename] [-l] [-n] [-xa]");
|
||||
writefln(" -x filename extract file");
|
||||
writefln(" -xa extract everything");
|
||||
writefln(" -l list all files");
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
|
||||
#include <iostream>
|
||||
|
||||
#include "dbg.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
class CustomOverlappingPairCallback;
|
||||
|
@ -406,7 +408,7 @@ extern "C" void bullet_insertStatic(btConcaveShape *shape,
|
|||
// Are we scaled?
|
||||
if(scale != 1.0)
|
||||
{
|
||||
cout << "Scaling shape " << shape << " by " << scale << endl;
|
||||
//cout << "Scaling shape " << shape << " by " << scale << endl;
|
||||
|
||||
// Not quite sure how to handle local scaling yet. Our initial
|
||||
// attempt was to create a wrapper that showed a scale mesh to
|
||||
|
@ -454,6 +456,7 @@ extern "C" void bullet_insertStatic(btConcaveShape *shape,
|
|||
// Move the physics simulation 'delta' seconds forward in time
|
||||
extern "C" void bullet_timeStep(float delta)
|
||||
{
|
||||
TRACE("bullet_timeStep");
|
||||
// TODO: We might experiment with the number of time steps. Remember
|
||||
// that the function also returns the number of steps performed.
|
||||
g_dynamicsWorld->stepSimulation(delta,2);
|
||||
|
|
|
@ -281,11 +281,8 @@ struct ConfigManager
|
|||
{
|
||||
char[] s = ini.getString("Game Files", format("GameFile[%d]",i), null);
|
||||
if(s != null && srch.has(s))
|
||||
{
|
||||
writefln("Adding game file %s", s);
|
||||
gameFiles ~= esmDir ~ s;
|
||||
}
|
||||
}
|
||||
delete srch;
|
||||
|
||||
if(gameFiles.length == 0)
|
||||
|
|
|
@ -36,6 +36,7 @@ import scene.player;
|
|||
import bullet.bindings;
|
||||
|
||||
import monster.monster;
|
||||
import monster.vm.dbg;
|
||||
|
||||
import ogre.bindings;
|
||||
|
||||
|
@ -242,6 +243,9 @@ extern(C) int d_frameStarted(float time, int guiMode)
|
|||
{
|
||||
if(doExit) return 0;
|
||||
|
||||
dbg.trace("d_frameStarted");
|
||||
scope(exit) dbg.untrace();
|
||||
|
||||
// Run the Monster scheduler
|
||||
vm.frame(time);
|
||||
|
||||
|
|
|
@ -37,8 +37,8 @@ module monster.options;
|
|||
the language, let us know!
|
||||
*/
|
||||
|
||||
const:
|
||||
static:
|
||||
const:
|
||||
|
||||
|
||||
/*********************************************************
|
||||
|
@ -97,7 +97,8 @@ int maxFStack = 100;
|
|||
// can run at once. Enabling this will prevent infinite loops.
|
||||
bool enableExecLimit = true;
|
||||
|
||||
// Maximum number of instructions to allow in once call to execute() (if
|
||||
// Maximum number of instructions to allow in each call to execute()
|
||||
// (if enableExecLimit is true)
|
||||
long execLimit = 10000000;
|
||||
|
||||
|
||||
|
@ -109,17 +110,18 @@ long execLimit = 10000000;
|
|||
|
||||
*********************************************************/
|
||||
|
||||
// Enable tracing of external functions on the function stack. If
|
||||
// true, you may use vm.trace() and vm.untrace() to add your own
|
||||
// functions to the internal Monster function stack for debug purposes
|
||||
// (the functions will show up in debug output.) If false, these
|
||||
// functions will be empty and most likely optimized away completely
|
||||
// by the D compiler (in release builds).
|
||||
bool enableTrace = true;
|
||||
|
||||
|
||||
// If true, set the log output to standard out. If false, logging is
|
||||
// disabled by default and must be activated (through monster.vm.dbg)
|
||||
// at runtime.
|
||||
bool defaultLogToStdout = false;
|
||||
|
||||
// If true, all function stack operations (pushes and pops) are logged
|
||||
// with dbg.log(), and all logged messages are indented according to
|
||||
// the current fstack level.
|
||||
bool logFStack = true;
|
||||
|
||||
// If true, log when threads are put into the background/forground.
|
||||
bool logThreads = true;
|
||||
|
||||
/*********************************************************
|
||||
|
||||
|
|
|
@ -37,8 +37,8 @@ module monster.options;
|
|||
the language, let us know!
|
||||
*/
|
||||
|
||||
const:
|
||||
static:
|
||||
const:
|
||||
|
||||
|
||||
/*********************************************************
|
||||
|
@ -97,7 +97,8 @@ int maxFStack = 100;
|
|||
// can run at once. Enabling this will prevent infinite loops.
|
||||
bool enableExecLimit = true;
|
||||
|
||||
// Maximum number of instructions to allow in once call to execute() (if
|
||||
// Maximum number of instructions to allow in each call to execute()
|
||||
// (if enableExecLimit is true)
|
||||
long execLimit = 10000000;
|
||||
|
||||
|
||||
|
@ -109,17 +110,14 @@ long execLimit = 10000000;
|
|||
|
||||
*********************************************************/
|
||||
|
||||
// Enable tracing of external functions on the function stack. If
|
||||
// true, you may use vm.trace() and vm.untrace() to add your own
|
||||
// functions to the internal Monster function stack for debug purposes
|
||||
// (the functions will show up in debug output.) If false, these
|
||||
// functions will be empty and most likely optimized away completely
|
||||
// by the D compiler (in release builds).
|
||||
bool enableTrace = true;
|
||||
|
||||
|
||||
|
||||
// If true, set the log output to standard out. If false, logging is
|
||||
// disabled by default and must be activated (through monster.vm.dbg)
|
||||
// at runtime.
|
||||
bool defaultLogToStdout = false;
|
||||
|
||||
// If true, all function stack operations (pushes and pops) are logged
|
||||
// with dbg.log().
|
||||
bool logFStack = true;
|
||||
|
||||
/*********************************************************
|
||||
|
||||
|
|
|
@ -10,4 +10,6 @@ done
|
|||
|
||||
svn st
|
||||
|
||||
diff options.openmw options.d
|
||||
diff options.openmw options.d || $EDITOR options.d
|
||||
mv options.openmw options.openmw_last
|
||||
cp options.d options.openmw
|
||||
|
|
155
monster/vm/dbg.d
Normal file
155
monster/vm/dbg.d
Normal file
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
Monster - an advanced game scripting language
|
||||
Copyright (C) 2007-2009 Nicolay Korslund
|
||||
Email: <korslund@gmail.com>
|
||||
WWW: http://monster.snaptoad.com/
|
||||
|
||||
This file (dbg.d) is part of the Monster script language package.
|
||||
|
||||
Monster is distributed as free software: you can redistribute it
|
||||
and/or modify it under the terms of the GNU General Public License
|
||||
version 3, as published by the Free Software Foundation.
|
||||
|
||||
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
|
||||
version 3 along with this program. If not, see
|
||||
http://www.gnu.org/licenses/ .
|
||||
|
||||
*/
|
||||
|
||||
module monster.vm.dbg;
|
||||
|
||||
import monster.vm.thread;
|
||||
import std.stream;
|
||||
import std.string;
|
||||
import std.cstream;
|
||||
import monster.vm.fstack;
|
||||
import monster.options;
|
||||
|
||||
/*
|
||||
This file is used for runtime debugging, stack tracing, etc.
|
||||
*/
|
||||
|
||||
/*
|
||||
Create a trace for your function that automatically pops itself of
|
||||
the fstack when it goes out of scope. Usage:
|
||||
|
||||
auto scope _t = new MTrace("funcName");
|
||||
|
||||
This is one of the few places where C++ is actually easier to use...
|
||||
|
||||
An alternative way to do the same thing:
|
||||
|
||||
dbg.trace("funcName");
|
||||
scope(exit) dbg.untrace();
|
||||
*/
|
||||
|
||||
scope class MTrace
|
||||
{
|
||||
this(char[] str) { dbg.trace(str); }
|
||||
~this() { dbg.untrace(); }
|
||||
}
|
||||
|
||||
Dbg dbg;
|
||||
|
||||
struct Dbg
|
||||
{
|
||||
Stream dbgOut = null;
|
||||
|
||||
// Add indentation for each function stack level (only works if
|
||||
// logFStack is true in options.d)
|
||||
char[] logLevelString = "| ";
|
||||
|
||||
// Called at startup
|
||||
void init()
|
||||
{
|
||||
static if(defaultLogToStdout)
|
||||
dbgOut = dout;
|
||||
}
|
||||
|
||||
void log(char[] msg)
|
||||
{
|
||||
if(dbgOut !is null)
|
||||
{
|
||||
int logLevel = getFStackLevel();
|
||||
int extLevel = getExtLevel();
|
||||
int trdIndex = getThreadIndex();
|
||||
|
||||
char[] str = format("(trd=%s,ext=%s,lev=%s)",
|
||||
trdIndex,
|
||||
extLevel,
|
||||
logLevel);
|
||||
str = format("%-24s", str);
|
||||
dbgOut.writeString(str);
|
||||
|
||||
// If we're logging function stack activity, put in some fancy
|
||||
// indentation as well.
|
||||
static if(logFStack)
|
||||
{
|
||||
for(int i;i<logLevel+extLevel;i++)
|
||||
dbgOut.writeString(logLevelString);
|
||||
}
|
||||
|
||||
dbgOut.writeLine(msg);
|
||||
}
|
||||
}
|
||||
|
||||
// Stack tracing functions. These might be used internally in the
|
||||
// engine, so they should not be disabled.
|
||||
|
||||
// Add an external function to the stack
|
||||
void trace(char[] name)
|
||||
{
|
||||
getFStack().pushExt(name);
|
||||
}
|
||||
|
||||
// Pop the last function pushed by trace()
|
||||
void untrace()
|
||||
{
|
||||
auto fs = getFStack();
|
||||
assert(fs.cur !is null && fs.cur.isExternal,
|
||||
"vm.untrace() used on a non-external function stack entry");
|
||||
fs.pop();
|
||||
}
|
||||
|
||||
// Return the current function stack printout
|
||||
char[] getTrace()
|
||||
{ return getFStack().toString(); }
|
||||
|
||||
private:
|
||||
|
||||
int getExtLevel()
|
||||
{
|
||||
return externals.list.length;
|
||||
}
|
||||
|
||||
int getFStackLevel()
|
||||
{
|
||||
if(cthread !is null)
|
||||
return cthread.fstack.list.length;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int getThreadIndex()
|
||||
{
|
||||
if(cthread !is null)
|
||||
return cthread.getIndex();
|
||||
else return 0;
|
||||
}
|
||||
|
||||
// Get the active function stack, or the externals stack if no
|
||||
// thread is active.
|
||||
FunctionStack *getFStack()
|
||||
{
|
||||
if(cthread !is null)
|
||||
{
|
||||
assert(!cthread.fstack.isEmpty);
|
||||
return &cthread.fstack;
|
||||
}
|
||||
return &externals;
|
||||
}
|
||||
}
|
|
@ -29,6 +29,7 @@ import monster.vm.mclass;
|
|||
import monster.vm.stack;
|
||||
import monster.vm.error;
|
||||
import monster.vm.thread;
|
||||
import monster.vm.dbg;
|
||||
import monster.compiler.states;
|
||||
import monster.compiler.functions;
|
||||
import monster.compiler.linespec;
|
||||
|
@ -123,7 +124,7 @@ struct StackPoint
|
|||
char[] toString()
|
||||
{
|
||||
if(isExternal)
|
||||
return "external function " ~ extName;
|
||||
return "external " ~ extName;
|
||||
|
||||
assert(func !is null);
|
||||
|
||||
|
@ -241,19 +242,12 @@ struct FunctionStack
|
|||
// Set the stack point up as a function. Allows obj to be null.
|
||||
void push(Function *func, MonsterObject *obj)
|
||||
{
|
||||
//assert(cthread !is null && this is &cthread.fstack);
|
||||
|
||||
push(obj);
|
||||
|
||||
assert(func !is null);
|
||||
cur.ftype = SPType.Function;
|
||||
cur.func = func;
|
||||
|
||||
/*
|
||||
writefln("Pushing ", func.name);
|
||||
writefln(toString, "\n");
|
||||
*/
|
||||
|
||||
assert(func.owner !is null);
|
||||
assert(obj is null || func.owner.parentOf(obj.cls));
|
||||
|
||||
|
@ -264,6 +258,11 @@ struct FunctionStack
|
|||
natives++;
|
||||
|
||||
assert(!func.isIdle, "don't use fstack.push() on idle functions");
|
||||
|
||||
static if(logFStack)
|
||||
{
|
||||
dbg.log("+++ " ~ cur.toString());
|
||||
}
|
||||
}
|
||||
|
||||
// Set the stack point up as a state
|
||||
|
@ -273,8 +272,6 @@ struct FunctionStack
|
|||
assert(isEmpty,
|
||||
"state code can only run at the bottom of the function stack");
|
||||
|
||||
//writefln("Pushing state ", st.name);
|
||||
|
||||
push(obj);
|
||||
cur.ftype = SPType.State;
|
||||
cur.state = st;
|
||||
|
@ -285,6 +282,11 @@ struct FunctionStack
|
|||
|
||||
// Set up the byte code
|
||||
cur.code.setData(st.bcode);
|
||||
|
||||
static if(logFStack)
|
||||
{
|
||||
dbg.log("+++ " ~ cur.toString());
|
||||
}
|
||||
}
|
||||
|
||||
// Push an external (non-scripted) function on the function
|
||||
|
@ -295,6 +297,11 @@ struct FunctionStack
|
|||
natives++;
|
||||
cur.ftype = SPType.External;
|
||||
cur.extName = name;
|
||||
|
||||
static if(logFStack)
|
||||
{
|
||||
dbg.log("+++ " ~ cur.toString());
|
||||
}
|
||||
}
|
||||
|
||||
void pushIdle(Function *func, MonsterObject *obj)
|
||||
|
@ -304,11 +311,14 @@ struct FunctionStack
|
|||
cur.func = func;
|
||||
cur.ftype = SPType.Idle;
|
||||
|
||||
//writefln("Pushing idle ", func.name);
|
||||
|
||||
assert(func.owner !is null);
|
||||
assert(obj is null || func.owner.parentOf(obj.cls));
|
||||
assert(func.isIdle, func.name.str ~ "() is not an idle function");
|
||||
|
||||
static if(logFStack)
|
||||
{
|
||||
dbg.log("+++ " ~ cur.toString());
|
||||
}
|
||||
}
|
||||
|
||||
// Pops one entry of the stack. Checks that the stack level has been
|
||||
|
@ -324,11 +334,10 @@ struct FunctionStack
|
|||
natives--;
|
||||
assert(natives >= 0);
|
||||
|
||||
/*
|
||||
if(cur.isFunc) writefln("popping function ", cur.func.name);
|
||||
else if(cur.isState) writefln("popping state ", cur.state.name);
|
||||
writefln(toString, "\n");
|
||||
*/
|
||||
static if(logFStack)
|
||||
{
|
||||
dbg.log(" -- " ~ cur.toString());
|
||||
}
|
||||
|
||||
// Remove the topmost node from the list, and set cur.
|
||||
assert(cur == list.getHead());
|
||||
|
@ -338,6 +347,11 @@ struct FunctionStack
|
|||
restoreFrame();
|
||||
|
||||
assert(list.length != 0 || cur is null);
|
||||
|
||||
static if(logFStack)
|
||||
{
|
||||
dbg.log("");
|
||||
}
|
||||
}
|
||||
|
||||
// Get a stack trace (pretty basic at the moment)
|
||||
|
|
|
@ -32,6 +32,7 @@ import monster.vm.thread;
|
|||
import monster.vm.stack;
|
||||
import monster.vm.arrays;
|
||||
import monster.vm.vm;
|
||||
import monster.vm.dbg;
|
||||
|
||||
import monster.modules.all;
|
||||
import monster.options;
|
||||
|
@ -121,6 +122,9 @@ void doMonsterInit()
|
|||
|
||||
// Next, initialize the Monster library
|
||||
|
||||
// Initialize the debugger structure
|
||||
dbg.init();
|
||||
|
||||
// Initialize compiler constructs
|
||||
initTokenizer();
|
||||
initProperties();
|
||||
|
|
|
@ -455,7 +455,8 @@ final class MonsterClass
|
|||
|
||||
// Needed to make sure execute() exits when the constructor is
|
||||
// done.
|
||||
vm.pushExt("callConst");
|
||||
if(cthread !is null)
|
||||
cthread.fstack.pushExt("callConst");
|
||||
|
||||
// Call constructors
|
||||
foreach(c; tree)
|
||||
|
@ -473,7 +474,8 @@ final class MonsterClass
|
|||
c.scptConst.fn.call(obj);
|
||||
}
|
||||
|
||||
vm.popExt();
|
||||
if(cthread !is null)
|
||||
cthread.fstack.pop();
|
||||
}
|
||||
|
||||
// Get the whole allocated buffer belonging to this object
|
||||
|
|
|
@ -42,6 +42,7 @@ import monster.vm.mclass;
|
|||
import monster.vm.mobject;
|
||||
import monster.vm.codestream;
|
||||
import monster.vm.stack;
|
||||
import monster.vm.dbg;
|
||||
import monster.vm.idlefunction;
|
||||
import monster.vm.arrays;
|
||||
import monster.vm.iterators;
|
||||
|
@ -139,6 +140,11 @@ struct Thread
|
|||
return cn;
|
||||
}
|
||||
|
||||
int getIndex()
|
||||
{
|
||||
return NodeList.getIndex(this)+1;
|
||||
}
|
||||
|
||||
// Schedule the function to run the next frame. Can only be used on
|
||||
// paused threads.
|
||||
void restart()
|
||||
|
@ -366,6 +372,10 @@ struct Thread
|
|||
// Clear out our stack values
|
||||
stack.reset();
|
||||
|
||||
static if(logThreads)
|
||||
dbg.log(format("------ deactivate thread=%s (stack=%s) ------",
|
||||
getIndex, sstack.length));
|
||||
|
||||
assert(!isRunning);
|
||||
}
|
||||
|
||||
|
@ -383,6 +393,10 @@ struct Thread
|
|||
stack.getPos() == 0,
|
||||
"only empty transient threads kan restore into a non-empty stack");
|
||||
|
||||
static if(logThreads)
|
||||
dbg.log(format("------ activate thread=%s (stack=%s) ------",
|
||||
getIndex, sstack.length));
|
||||
|
||||
if(sstack.length)
|
||||
{
|
||||
assert(stack.getPos() == 0,
|
||||
|
@ -390,6 +404,8 @@ struct Thread
|
|||
assert(!isTransient,
|
||||
"cannot restore a transent thread with stack");
|
||||
|
||||
|
||||
|
||||
// Push the values back, and free the buffer
|
||||
stack.pushInts(sstack);
|
||||
Buffers.free(sstack);
|
||||
|
|
|
@ -28,7 +28,6 @@ import monster.vm.thread;
|
|||
import monster.vm.mclass;
|
||||
import monster.vm.mobject;
|
||||
import monster.vm.init;
|
||||
import monster.vm.fstack;
|
||||
|
||||
import monster.compiler.tokenizer;
|
||||
import monster.compiler.linespec;
|
||||
|
@ -135,63 +134,6 @@ struct VM
|
|||
return load(ms, name);
|
||||
}
|
||||
|
||||
static if(enableTrace)
|
||||
{
|
||||
// Push the given string onto the function stack as an external
|
||||
// function.
|
||||
void tracef(...)
|
||||
{
|
||||
char[] s;
|
||||
|
||||
void putc(dchar c)
|
||||
{
|
||||
std.utf.encode(s, c);
|
||||
}
|
||||
|
||||
std.format.doFormat(&putc, _arguments, _argptr);
|
||||
trace(s);
|
||||
}
|
||||
|
||||
void trace(char[] name) { pushExt(name); }
|
||||
void untrace() { popExt(); }
|
||||
}
|
||||
else
|
||||
{
|
||||
void tracef(...) {}
|
||||
void trace(char[]) {}
|
||||
void untrace() {}
|
||||
}
|
||||
|
||||
void pushExt(char[] name)
|
||||
{
|
||||
getFStack().pushExt(name);
|
||||
}
|
||||
|
||||
// Pop the last function pushed by pushExt()
|
||||
void popExt()
|
||||
{
|
||||
auto fs = getFStack();
|
||||
assert(fs.cur !is null && fs.cur.isExternal,
|
||||
"vm.untrace() used on a non-external function stack entry");
|
||||
fs.pop();
|
||||
}
|
||||
|
||||
// Get the active function stack, or the externals stack if no
|
||||
// thread is active.
|
||||
FunctionStack *getFStack()
|
||||
{
|
||||
if(cthread !is null)
|
||||
{
|
||||
assert(!cthread.fstack.isEmpty);
|
||||
return &cthread.fstack;
|
||||
}
|
||||
return &externals;
|
||||
}
|
||||
|
||||
// Return the current function stack printout
|
||||
char[] getTrace()
|
||||
{ return getFStack().toString(); }
|
||||
|
||||
void addPath(char[] path)
|
||||
{
|
||||
init();
|
||||
|
|
|
@ -2086,19 +2086,3 @@ char[] sMagicCreature02ID;
|
|||
char[] sMagicCreature03ID;
|
||||
char[] sMagicCreature04ID;
|
||||
char[] sMagicCreature05ID;
|
||||
|
||||
test()
|
||||
{
|
||||
// Print out some random variables. If you want these not to get
|
||||
// lost in a sea of output from OGRE, run openmw with the -n switch
|
||||
// to disable rendering.
|
||||
print();
|
||||
print("Some random GMST variables:");
|
||||
print("sThief:", sThief);
|
||||
print("iAlchemyMod:", iAlchemyMod);
|
||||
print("sBribe_100_Gold:", sBribe_100_Gold);
|
||||
print("fMaxWalkSpeed:", fMaxWalkSpeed);
|
||||
print("fWereWolfMagicka:", fWereWolfMagicka);
|
||||
print("sEffectSummonFabricant:", sEffectSummonFabricant);
|
||||
print();
|
||||
}
|
||||
|
|
|
@ -47,9 +47,6 @@ void initMonsterScripts()
|
|||
|
||||
// Set up the GUI Monster module
|
||||
setupGUIScripts();
|
||||
|
||||
// Run the test script
|
||||
vm.run("test.mn");
|
||||
}
|
||||
|
||||
// Run the GUI scripts. These should be run only after the
|
||||
|
@ -62,3 +59,11 @@ void runGUIScripts()
|
|||
// Run the fps ticker
|
||||
vm.run("fpsticker.mn");
|
||||
}
|
||||
|
||||
// This should probably not be here:
|
||||
import monster.vm.dbg;
|
||||
import std.string;
|
||||
|
||||
extern(C):
|
||||
void dbg_trace(char*str) { dbg.trace(toString(str)); }
|
||||
void dbg_untrace() { dbg.untrace(); }
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
// A short example script
|
||||
|
||||
sleep(6);
|
||||
|
||||
while(true)
|
||||
{
|
||||
print("Howdy from the world of Monster scripts!");
|
||||
print("This script is located in mscripts/test.mn. Check it out!");
|
||||
sleep(60);
|
||||
}
|
|
@ -26,6 +26,7 @@ import nif.record;
|
|||
|
||||
abstract class Property : Named
|
||||
{
|
||||
// The meaning of these depends on the actual property type.
|
||||
ushort flags;
|
||||
|
||||
override:
|
||||
|
@ -105,7 +106,7 @@ class NiTexturingProperty : Property
|
|||
}
|
||||
|
||||
/*
|
||||
* This just in: The textures in this list is as follows:
|
||||
* The textures in this list is as follows:
|
||||
*
|
||||
* 0 - Base texture
|
||||
* 1 - Dark texture
|
||||
|
@ -236,6 +237,46 @@ class NiShadeProperty : Property
|
|||
|
||||
class NiAlphaProperty : Property
|
||||
{
|
||||
/*
|
||||
In NiAlphaProperty, the flags have the following meaning.
|
||||
|
||||
Bit 0 : alpha blending enable
|
||||
Bits 1-4 : source blend mode
|
||||
Bits 5-8 : destination blend mode
|
||||
Bit 9 : alpha test enable
|
||||
Bit 10-12 : alpha test mode
|
||||
Bit 13 : no sorter flag ( disables triangle sorting )
|
||||
|
||||
blend modes (glBlendFunc):
|
||||
0000 GL_ONE
|
||||
0001 GL_ZERO
|
||||
0010 GL_SRC_COLOR
|
||||
0011 GL_ONE_MINUS_SRC_COLOR
|
||||
0100 GL_DST_COLOR
|
||||
0101 GL_ONE_MINUS_DST_COLOR
|
||||
0110 GL_SRC_ALPHA
|
||||
0111 GL_ONE_MINUS_SRC_ALPHA
|
||||
1000 GL_DST_ALPHA
|
||||
1001 GL_ONE_MINUS_DST_ALPHA
|
||||
1010 GL_SRC_ALPHA_SATURATE
|
||||
|
||||
test modes (glAlphaFunc):
|
||||
000 GL_ALWAYS
|
||||
001 GL_LESS
|
||||
010 GL_EQUAL
|
||||
011 GL_LEQUAL
|
||||
100 GL_GREATER
|
||||
101 GL_NOTEQUAL
|
||||
110 GL_GEQUAL
|
||||
111 GL_NEVER
|
||||
|
||||
Taken from:
|
||||
http://niftools.sourceforge.net/doc/nif/NiAlphaProperty.html
|
||||
*/
|
||||
|
||||
// Tested against when certain flags are set (see above.)
|
||||
ubyte threshold;
|
||||
|
||||
override:
|
||||
void read()
|
||||
{
|
||||
|
|
|
@ -50,8 +50,8 @@ extern(C):
|
|||
// Do engine configuration. Returns 0 if we should continue, 1 if
|
||||
// not.
|
||||
int ogre_configure(int showConfig, // Do we show the config dialogue?
|
||||
char *plugincfg // Name of 'plugin.cfg' file
|
||||
);
|
||||
char *plugincfg,// Name of 'plugin.cfg' file
|
||||
int debutOut); // Enable or disable debug output
|
||||
|
||||
// Sets up the window
|
||||
void ogre_initWindow();
|
||||
|
@ -122,8 +122,10 @@ void ogre_createMaterial(char *name, // Name to give resource
|
|||
float *specular,
|
||||
float *emissive, // Self illumination
|
||||
float glossiness,// Same as shininess?
|
||||
float alpha, // Use this in all alpha values?
|
||||
char *texture); // Texture name
|
||||
float alpha, // Reflection alpha?
|
||||
char *texture, // Texture name
|
||||
int alphaFlags, // Alpha settings (see
|
||||
ubyte alphaTest);// NiAlphaProperty in nif/)
|
||||
|
||||
// Creates a mesh and gives it a bounding box. Also creates an entity
|
||||
// and attached it to the given SceneNode 'owner'.
|
||||
|
@ -165,7 +167,7 @@ void ogre_moveCameraRel(float x, float y, float z);
|
|||
|
||||
// GUI functions. Under development.
|
||||
typedef void* WidgetPtr;
|
||||
void gui_setupGUI();
|
||||
void gui_setupGUI(int debugOut);
|
||||
void gui_toggleGui();
|
||||
void gui_setCellName(char *str);
|
||||
|
||||
|
|
|
@ -43,10 +43,21 @@ extern "C" void ogre_cleanup()
|
|||
|
||||
extern "C" int32_t ogre_configure(
|
||||
int32_t showConfig, // Do we show the config dialogue?
|
||||
char *plugincfg // Name of 'plugin.cfg' file
|
||||
)
|
||||
char *plugincfg, // Name of 'plugin.cfg' file
|
||||
int32_t debugOut) // Enable or disable logging
|
||||
{
|
||||
mRoot = new Root(plugincfg);
|
||||
// Set up logging first
|
||||
new LogManager;
|
||||
Log *log = LogManager::getSingleton().createLog("default");
|
||||
|
||||
if(debugOut)
|
||||
// Full log detail
|
||||
log->setLogDetail(LL_BOREME);
|
||||
else
|
||||
// Disable logging
|
||||
log->setDebugOutputEnabled(false);
|
||||
|
||||
mRoot = new Root(plugincfg, "ogre.cfg", "");
|
||||
|
||||
// Add the BSA archive manager before reading the config file.
|
||||
ArchiveManager::getSingleton().addArchiveFactory( &mBSAFactory );
|
||||
|
@ -95,7 +106,7 @@ extern "C" int32_t ogre_configure(
|
|||
// Initialize window. This will create and show the actual window.
|
||||
extern "C" void ogre_initWindow()
|
||||
{
|
||||
std::cout << "ogre_initWindow()\n";
|
||||
TRACE("ogre_initWindow");
|
||||
|
||||
// Initialize OGRE.
|
||||
mWindow = mRoot->initialise(true, "OpenMW", "");
|
||||
|
@ -155,8 +166,6 @@ extern "C" void ogre_initWindow()
|
|||
// Register the input listener
|
||||
mKeyboard -> setEventCallback( &mInput );
|
||||
mMouse -> setEventCallback( &mInput );
|
||||
|
||||
std::cout << "ogre_initWindow finished\n";
|
||||
}
|
||||
|
||||
// Make a scene
|
||||
|
@ -619,7 +628,10 @@ extern "C" void ogre_createMaterial(char *name, // Name to give
|
|||
float alpha, // Use this in all
|
||||
// alpha values?
|
||||
|
||||
char* texture) // Texture
|
||||
char* texture, // Texture
|
||||
|
||||
int32_t alphaFlags,
|
||||
uint8_t alphaTest) // Alpha settings
|
||||
{
|
||||
MaterialPtr material = MaterialManager::getSingleton().create(
|
||||
name,
|
||||
|
@ -630,7 +642,30 @@ extern "C" void ogre_createMaterial(char *name, // Name to give
|
|||
// directory), it will automatically be loaded when needed. If
|
||||
// not, we should already have inserted a manual loader for the texture.
|
||||
if(texture)
|
||||
material->getTechnique(0)->getPass(0)->createTextureUnitState(texture);
|
||||
{
|
||||
Pass *pass = material->getTechnique(0)->getPass(0);
|
||||
TextureUnitState *txt = pass->createTextureUnitState(texture);
|
||||
|
||||
// Add transparencly.
|
||||
|
||||
if(alphaFlags != -1)
|
||||
{
|
||||
// The 237 alpha flags are by far the most common. Check
|
||||
// NiAlphaProperty in nif/properties.d if you need to
|
||||
// decode other values. 237 basically means normal
|
||||
// transparencly.
|
||||
if(alphaFlags == 237)
|
||||
{
|
||||
// Enable transparency
|
||||
pass->setSceneBlending(SBT_TRANSPARENT_ALPHA);
|
||||
|
||||
//pass->setDepthCheckEnabled(false);
|
||||
pass->setDepthWriteEnabled(false);
|
||||
}
|
||||
else
|
||||
std::cout << "UNHANDLED ALPHA FOR " << texture << ": " << alphaFlags << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Set bells and whistles
|
||||
material->setAmbient(ambient[0], ambient[1], ambient[2]);
|
||||
|
@ -638,12 +673,6 @@ extern "C" void ogre_createMaterial(char *name, // Name to give
|
|||
material->setSpecular(specular[0], specular[1], specular[2], alpha);
|
||||
material->setSelfIllumination(emissive[0], emissive[1], emissive[2]);
|
||||
material->setShininess(glossiness);
|
||||
|
||||
// Enable transparancy? This is just guesswork. Perhaps we
|
||||
// should use NiAlphaProperty or something instead. This looks
|
||||
// like crap so it's not enabled right now.
|
||||
|
||||
//material->setSceneBlending(SBT_TRANSPARENT_ALPHA);
|
||||
}
|
||||
|
||||
extern "C" SceneNode *ogre_getDetachedNode()
|
||||
|
|
|
@ -30,7 +30,11 @@ extern "C" void gui_setNeedMouseFocus(MyGUI::WidgetPtr p, int32_t b)
|
|||
{ p->setNeedMouseFocus(b); }
|
||||
|
||||
extern "C" void gui_setTextColor(MyGUI::WidgetPtr p, float r,float g,float b)
|
||||
{ p->setTextColour(Ogre::ColourValue(b,g,r)); }
|
||||
{
|
||||
MyGUI::StaticText *st = dynamic_cast<MyGUI::StaticText*>(p);
|
||||
if(st != NULL)
|
||||
st->setTextColour(Ogre::ColourValue(b,g,r));
|
||||
}
|
||||
|
||||
extern "C" void gui_setCoord(MyGUI::WidgetPtr p,
|
||||
int32_t x,int32_t y,int32_t w,int32_t h)
|
||||
|
@ -177,7 +181,7 @@ public:
|
|||
{
|
||||
MyGUI::WidgetPtr pt;
|
||||
getWidget(pt, name);
|
||||
pt->setTextColour(Ogre::ColourValue(b,g,r));
|
||||
gui_setTextColor(pt,r,g,b);
|
||||
}
|
||||
|
||||
void setImage(const char* name, const char* imgName)
|
||||
|
@ -278,7 +282,6 @@ public:
|
|||
setCoord(500,0,320,300);
|
||||
mMainWidget->setCaption(cellName);
|
||||
setText("WorldButton", "World");
|
||||
setColor("WorldButton", 0.75, 0.6, 0.35);
|
||||
setImage("Compass", "compass.dds");
|
||||
}
|
||||
};
|
||||
|
@ -399,11 +402,15 @@ void turnGuiOff(MyGUI::WidgetPtr sender)
|
|||
gui_toggleGui();
|
||||
}
|
||||
|
||||
extern "C" void gui_setupGUI()
|
||||
extern "C" void gui_setupGUI(int32_t debugOut)
|
||||
{
|
||||
ResourceGroupManager::getSingleton().
|
||||
addResourceLocation("media_mygui", "FileSystem", "General");
|
||||
|
||||
// Enable/disable logging to stdout
|
||||
MyGUI::LogManager::initialise();
|
||||
MyGUI::LogManager::setSTDOutputEnabled(debugOut);
|
||||
|
||||
mGUI = new MyGUI::Gui();
|
||||
mGUI->initialise(mWindow);
|
||||
|
||||
|
|
|
@ -35,6 +35,8 @@
|
|||
|
||||
#include <MyGUI.h>
|
||||
|
||||
#include "dbg.h"
|
||||
|
||||
using namespace Ogre;
|
||||
|
||||
RenderWindow* mWindow;
|
||||
|
|
|
@ -66,8 +66,6 @@ struct MeshLoader
|
|||
{
|
||||
baseName = name;
|
||||
|
||||
writefln("Loading mesh '%s'", name);
|
||||
|
||||
// Check if the first record is a node
|
||||
Node n = cast(Node) nifMesh.records[0];
|
||||
|
||||
|
@ -194,7 +192,7 @@ struct MeshLoader
|
|||
//writefln("Found path controller");
|
||||
assert(cont.target is data);
|
||||
}
|
||||
else writefln("Other controller (%s)", cont);
|
||||
//else writefln("Other controller (%s)", cont);
|
||||
cont = cont.next;
|
||||
}
|
||||
|
||||
|
@ -210,6 +208,10 @@ struct MeshLoader
|
|||
char[] newName = UniqueName(baseName);
|
||||
NiMaterialProperty mp;
|
||||
|
||||
// Special alpha settings, if the NiAlphaProperty is present
|
||||
int alphaFlags = -1;
|
||||
ubyte alphaTest;
|
||||
|
||||
bool hidden = (flags & 0x01) != 0; // Not displayed
|
||||
bool collide = (flags & 0x02) != 0; // Use this mesh for collision
|
||||
bool bbcollide = (flags & 0x04) != 0; // Use bounding box for
|
||||
|
@ -260,6 +262,16 @@ struct MeshLoader
|
|||
}
|
||||
}
|
||||
|
||||
// NiAlphaProperty
|
||||
{
|
||||
NiAlphaProperty a = cast(NiAlphaProperty) p;
|
||||
if(a !is null)
|
||||
{
|
||||
alphaFlags = a.flags;
|
||||
alphaTest = a.threshold;
|
||||
}
|
||||
}
|
||||
|
||||
// NiMaterialProperty block
|
||||
{
|
||||
NiMaterialProperty tmp = cast(NiMaterialProperty) p;
|
||||
|
@ -270,11 +282,8 @@ struct MeshLoader
|
|||
material = newName;
|
||||
continue;
|
||||
}
|
||||
//writefln("Unknown property found: ", p);
|
||||
}
|
||||
}
|
||||
//if(!texture.length) writefln("No texture found");
|
||||
|
||||
// Get a pointer to the texture name
|
||||
char* texturePtr;
|
||||
if(texture.length) texturePtr = toStringz(texture);
|
||||
|
@ -282,9 +291,12 @@ struct MeshLoader
|
|||
|
||||
// Create the material
|
||||
if(material.length)
|
||||
ogre_createMaterial(material.ptr, mp.ambient.array.ptr, mp.diffuse.array.ptr,
|
||||
// A material is present. Use it.
|
||||
ogre_createMaterial(material.ptr, mp.ambient.array.ptr,
|
||||
mp.diffuse.array.ptr,
|
||||
mp.specular.array.ptr, mp.emissive.array.ptr,
|
||||
mp.glossiness, mp.alpha, texturePtr);
|
||||
mp.glossiness, mp.alpha, texturePtr,
|
||||
alphaFlags, alphaTest);
|
||||
else if(texturePtr)
|
||||
{
|
||||
// Texture, but no material. Make a default one.
|
||||
|
@ -294,8 +306,8 @@ struct MeshLoader
|
|||
zero[] = 0.0;
|
||||
one[] = 1.0;
|
||||
|
||||
ogre_createMaterial(newName.ptr, one.ptr, one.ptr, zero.ptr, zero.ptr, 0.0, 1.0,
|
||||
texturePtr);
|
||||
ogre_createMaterial(newName.ptr, one.ptr, one.ptr, zero.ptr, zero.ptr,
|
||||
0.0, 1.0, texturePtr, alphaFlags, alphaTest);
|
||||
}
|
||||
|
||||
nomaterial:
|
||||
|
|
10
ogre/ogre.d
10
ogre/ogre.d
|
@ -94,7 +94,7 @@ static ~this()
|
|||
}
|
||||
|
||||
// Loads ogre configurations, creats the root, etc.
|
||||
void setupOgre()
|
||||
void setupOgre(bool debugOut)
|
||||
{
|
||||
char[] plugincfg;
|
||||
|
||||
|
@ -108,7 +108,7 @@ void setupOgre()
|
|||
|
||||
// Later we will send more config info from core.config along with
|
||||
// this function
|
||||
if(ogre_configure(config.finalOgreConfig, toStringz(plugincfg)))
|
||||
if(ogre_configure(config.finalOgreConfig, toStringz(plugincfg), debugOut))
|
||||
OgreException("Configuration abort");
|
||||
|
||||
ogre_initWindow();
|
||||
|
@ -119,7 +119,7 @@ void setupOgre()
|
|||
ogre_makeScene();
|
||||
|
||||
// Load the GUI system
|
||||
gui_setupGUI();
|
||||
gui_setupGUI(debugOut);
|
||||
|
||||
// Run the GUI scripts
|
||||
runGUIScripts();
|
||||
|
@ -148,8 +148,8 @@ void startRendering()
|
|||
ogre_startRendering();
|
||||
}
|
||||
|
||||
// Cleans up after OGRE. Resets things like screen resolution and
|
||||
// mouse control.
|
||||
// Cleans up after OGRE. Resets things like screen resolution, mouse
|
||||
// control and keyboard repeat rate.
|
||||
void cleanupOgre()
|
||||
{
|
||||
if(ogreSetup)
|
||||
|
|
33
openmw.d
33
openmw.d
|
@ -43,6 +43,7 @@ import core.config;
|
|||
|
||||
import monster.util.string;
|
||||
import monster.vm.mclass;
|
||||
import monster.vm.dbg;
|
||||
import mscripts.setup;
|
||||
|
||||
import sound.audio;
|
||||
|
@ -69,6 +70,7 @@ void main(char[][] args)
|
|||
bool resetKeys = false;
|
||||
bool showOgreFlag = false;
|
||||
bool noSound = false;
|
||||
bool debugOut = false;
|
||||
|
||||
// Some examples to try:
|
||||
//
|
||||
|
@ -99,6 +101,14 @@ void main(char[][] args)
|
|||
else if(a == "-rk") resetKeys = true;
|
||||
else if(a == "-oc") showOgreFlag = true;
|
||||
else if(a == "-ns") noSound = true;
|
||||
else if(a == "-debug")
|
||||
{
|
||||
// Enable Monster debug output
|
||||
dbg.dbgOut = dout;
|
||||
|
||||
// Tell OGRE to do the same later on
|
||||
debugOut = true;
|
||||
}
|
||||
else cells ~= a;
|
||||
|
||||
if(cells.length + eCells.length/2 > 1 )
|
||||
|
@ -116,6 +126,7 @@ void main(char[][] args)
|
|||
writefln(" -rk Reset key bindings to default");
|
||||
writefln(" -oc Show the Ogre config dialogue");
|
||||
writefln(" -ns Completely disable sound");
|
||||
writefln(" -debug Print debug information");
|
||||
writefln(" -h Show this help");
|
||||
writefln("");
|
||||
writefln("Specifying more than one cell implies -n");
|
||||
|
@ -205,7 +216,7 @@ Try specifying another cell name on the command line, or edit openmw.ini.");
|
|||
// Get a cell data holder and load an interior cell
|
||||
cd = cellList.get();
|
||||
|
||||
writefln("Will load %s,%s", x, y);
|
||||
if(debugOut) writefln("Will load %s,%s", x, y);
|
||||
try cd.loadExtCell(x,y);
|
||||
catch(Exception e)
|
||||
{
|
||||
|
@ -219,11 +230,12 @@ Try specifying another cell name on the command line, or edit openmw.ini.");
|
|||
NodePtr putObject(MeshIndex m, Placement *pos, float scale,
|
||||
bool collide=false)
|
||||
{
|
||||
if(m == null)
|
||||
if(m is null)
|
||||
writefln("WARNING: CANNOT PUT NULL OBJECT");
|
||||
else if(m.isEmpty)
|
||||
writefln("WARNING: CANNOT INSERT EMPTY MESH '%s'", m.getName);
|
||||
else return placeObject(m, pos, scale, collide);
|
||||
else if(!m.isEmpty)
|
||||
return placeObject(m, pos, scale, collide);
|
||||
|
||||
//writefln("WARNING: CANNOT INSERT EMPTY MESH '%s'", m.getName);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -234,7 +246,7 @@ Try specifying another cell name on the command line, or edit openmw.ini.");
|
|||
if(render)
|
||||
{
|
||||
// Warm up OGRE
|
||||
setupOgre();
|
||||
setupOgre(debugOut);
|
||||
scope(exit) cleanupOgre();
|
||||
|
||||
// Set up Bullet
|
||||
|
@ -278,7 +290,6 @@ Try specifying another cell name on the command line, or edit openmw.ini.");
|
|||
Sound *s = ls.m.sound;
|
||||
if(s)
|
||||
{
|
||||
writefln("Dynamic light %s has sound %s", ls.m.id, s.id);
|
||||
ls.loopSound = soundScene.insert(s, true);
|
||||
if(ls.loopSound)
|
||||
{
|
||||
|
@ -300,7 +311,6 @@ Try specifying another cell name on the command line, or edit openmw.ini.");
|
|||
Sound *s = ls.m.sound;
|
||||
if(s)
|
||||
{
|
||||
writefln("Static light %s has sound %s", ls.m.id, s.id);
|
||||
ls.loopSound = soundScene.insert(s, true);
|
||||
if(ls.loopSound)
|
||||
{
|
||||
|
@ -369,7 +379,7 @@ Try specifying another cell name on the command line, or edit openmw.ini.");
|
|||
// Run it until the user tells us to quit
|
||||
startRendering();
|
||||
}
|
||||
else debug(verbose) writefln("Skipping rendering");
|
||||
else if(debugOut) writefln("Skipping rendering");
|
||||
|
||||
if(!noSound)
|
||||
{
|
||||
|
@ -377,7 +387,7 @@ Try specifying another cell name on the command line, or edit openmw.ini.");
|
|||
shutdownSound();
|
||||
}
|
||||
|
||||
debug(verbose)
|
||||
if(debugOut)
|
||||
{
|
||||
writefln();
|
||||
writefln("%d statics", cd.statics.length);
|
||||
|
@ -421,7 +431,10 @@ Try specifying another cell name on the command line, or edit openmw.ini.");
|
|||
cellList.release(cd);
|
||||
|
||||
// Write some statistics
|
||||
if(debugOut)
|
||||
{
|
||||
poolSize();
|
||||
writefln(esmRegion);
|
||||
writefln("Total objects: ", MonsterClass.getTotalObjects);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,4 @@ void loadGameSettings()
|
|||
// UTF32 at load time, so string8 will not be needed here.
|
||||
else if(b.type == VarType.String) gmstObj.setString8(name, b.str);
|
||||
}
|
||||
|
||||
// Call the test function
|
||||
gmstObj.call("test");
|
||||
}
|
||||
|
|
16
util/dbg.h
Normal file
16
util/dbg.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
#include <iostream>
|
||||
|
||||
extern "C" void dbg_trace(const char *);
|
||||
extern "C" void dbg_untrace();
|
||||
|
||||
class MTrace
|
||||
{
|
||||
public:
|
||||
MTrace(const char* str)
|
||||
{ dbg_trace(str); }
|
||||
|
||||
~MTrace()
|
||||
{ dbg_untrace(); }
|
||||
};
|
||||
|
||||
#define TRACE(x) MTrace _trc(x);
|
Loading…
Reference in a new issue